Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation for JS/TS miniflare API #784

Open
ryaninvents opened this issue Oct 10, 2024 · 3 comments
Open

Documentation for JS/TS miniflare API #784

ryaninvents opened this issue Oct 10, 2024 · 3 comments
Labels
documentation Improvements or additions to documentation

Comments

@ryaninvents
Copy link

ryaninvents commented Oct 10, 2024

Context: I'm trying to set up a new project using Miniflare, but the latest version of miniflare (3.20240925.1) does not seem to match the docs.

What I've tried: I'm trying to start an HTTP server with the code listed on the "Get Started" page (reproduced below):

import { Miniflare } from "miniflare";

const mf = new Miniflare({
  modules: true,
  script: `
  export default {
    async fetch(request, env, ctx) {
      return new Response("Hello Miniflare!");
    }
  }
  `,
  port: 5000,
});
const server = await mf.startServer();
console.log("Listening on :5000");

Unfortunately, it looks like the mf.startServer API is no longer supported. After some searching, I discovered the @miniflare/http-server package. I looked at the package and tried the script in the README, with some modifications and additions to work around missing pieces:

import { CorePlugin, MiniflareCore } from "@miniflare/core";
import {
  HTTPPlugin,
  convertNodeRequest,
  createServer,
  startServer,
} from "@miniflare/http-server";
import { VMScriptRunner } from "@miniflare/runner-vm";
import { Log, LogLevel } from "@miniflare/shared";
import http from "http";
import { QueueBroker } from "@miniflare/queues";

// Converting Node.js http.IncomingMessage to Miniflare's Request
http.createServer(async (nodeReq, nodeRes) => {
  const { request: req } = await convertNodeRequest(nodeReq, {
    forwardedProto: "http",
    realIp: "127.0.0.1",
  });
  nodeRes.end(await req.text());
});

// Creating and starting HTTP servers
export class BadStorageFactory {
  storage() {
    throw new Error("This example shouldn't need storage!");
  }
}

const plugins = { CorePlugin, HTTPPlugin };
const ctx = {
  log: new Log(LogLevel.INFO),
  storageFactory: new BadStorageFactory() as any,
  scriptRunner: new VMScriptRunner(),
  queueBroker: new QueueBroker(),
};

const mf = new MiniflareCore(plugins, ctx, {
  modules: true,
  script: `export default {
    async fetch(request, env) {
      return new Response("body");
    }
  }`,
  port: 5000,
});

// Start the server yourself...
const server = await createServer(mf);
// ...or get Miniflare to start it for you, logging the port
const server2 = await startServer(mf);

After installing the required packages, this resulted in an error:

${project_root}/node_modules/.pnpm/@[email protected]/node_modules/@miniflare/runner-vm/dist/src/index.js:298
      throw new VMScriptRunnerError("ERR_MODULE_DISABLED", "Modules support requires the --experimental-vm-modules flag");
            ^

VMScriptRunnerError [ERR_MODULE_DISABLED]: Modules support requires the --experimental-vm-modules flag
    at VMScriptRunner.run (${project_root}/node_modules/.pnpm/@[email protected]/node_modules/@miniflare/runner-vm/src/index.ts:53:13)
    at MiniflareCore.#reload (${project_root}/node_modules/.pnpm/@[email protected]/node_modules/@miniflare/core/src/index.ts:793:42) {
  code: 'ERR_MODULE_DISABLED',
  cause: undefined
}

Node.js v22.9.0

I tried adding --experimental-vm-modules on the command line while starting my scripts, and I also searched the API of the node:vm module, but couldn't figure out what it's asking for here.

If someone could point me in the right direction, I'd be happy to contribute documentation! I'm a fan of Cloudflare and Miniflare, just a little blocked on my current project.

Other things I've tried:

@ryaninvents
Copy link
Author

For anyone else who's stuck on the same thing: here's a temporary script I created to work around this for the time being.

import { createServer } from "node:http";
import { Miniflare, Request } from "miniflare";

const mf = new Miniflare({
  modules: true,
  script: `
  export default {
    async fetch(request, env, ctx) {
      return new Response("Hello Miniflare!");
    }
  }
  `,
});

function rawHeadersToHeaders(rawHeaders: string[]): Headers {
  const headers = new Headers();
  for (let i = 0; i < rawHeaders.length; i += 2) {
    headers.append(rawHeaders[i], rawHeaders[i + 1]);
  }
  return headers;
}

createServer(async (req, res) => {
  // Create a Request object based on the incoming Node request
  const request = new Request(`http://${req.headers.host}${req.url}`, {
    method: req.method,
    headers: rawHeadersToHeaders(req.rawHeaders),
    body: req.method !== "GET" && req.method !== "HEAD" ? req : null,
    duplex: "half",
  });

  const response = await mf.dispatchFetch(request);

  // Write the Response back to the Node server's res
  res.writeHead(
    response.status,
    Object.fromEntries(response.headers.entries()),
  );
  const body = await response.text();
  res.end(body);
}).listen(3000, () => {
  console.log("Server running on http://localhost:3000");
});

@emily-shen
Copy link

Hey! I think all the documentation you've linked is for miniflare 2, not miniflare 3. Here is the miniflare 3 readme: https://github.com/cloudflare/workers-sdk/blob/main/packages/miniflare/README.md. Let me know if that doesn't answer your questions :)

But agreed, its definitely confusing to have all these v2 docs floating around without more explicit pointers to v3 docs - we'll probably be moving those docs around soon so I'll make sure we add some more mentions of v3 while we're at it.

Also just curious, is there any reason you're using miniflare directly rather than via wrangler?

@emily-shen emily-shen added the documentation Improvements or additions to documentation label Oct 11, 2024
@ryaninvents
Copy link
Author

Thanks! Yes this makes a lot more sense -- answers my question :)

I'm using Miniflare directly because we are fetching credentials from a credential store. I could have had our start script update wrangler.toml with the credentials, but it seemed simpler to inject them via the bindings option, and also it was a little easier to integrate our custom build step this way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
Status: Backlog
Development

No branches or pull requests

2 participants