FetchableDevEnvironment API #18191
Replies: 7 comments 52 replies
-
This looks great! Especially the Is the The purist in me wants to avoid making
I appreciate that such a restriction would make migrations from Vite 5 more awkward though, and possibly prevent other valid use cases. |
Beta Was this translation helpful? Give feedback.
-
How do I dispatch a |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
I don't yet completely understand all of this, but I'm liking it. I'm currently building a plugin, which is a directory-based API router that should support multiple runtimes; and, since this is a fresh project, I'm using the v6 beta. The proposed helper functions, to convert requests and responses, would be great. As of now, I've stolen SvelteKit's versions of these, via their As for using a const environment = new FetchableEnvironment(name, config, {
async handleRequest(request) {
const entry = await environment.import(environment.config.entryPoint)
const response = await entry.default.fetch(request)
validateResponse(response)
return response
},
})
await server.environments.ssr.dispatchFetch(
new Request(convertToUrl('/entry-point.js'))
) |
Beta Was this translation helpful? Give feedback.
-
To add my 2 cents to the discussion, I think that this proposal would benefit from more standard middlewares. What I would like to be able to do: server.middlewares.use(async (request: Request) => {
// We can directly reuse `request` in `dispatchFetch`
return new Response(...);
}); I created The new syntax would internally be wrapped using |
Beta Was this translation helpful? Give feedback.
-
The first implementation of the API should be available as an experimental feature in 6.3. Any feedback is welcome. The fetchable runner is not part of the implementation yet, but the environment can be used by anyone. |
Beta Was this translation helpful? Give feedback.
-
I know I'm late to the game but I agree with @magne4000's comment. Mixing I'd expect most implementations of Instead of Maybe one day we can even define a standardized two-way HTTP API to handle Vite <=> environment communications for environments that don't support websockets. I implemented a toy (but mostly functional) Fastly Compute environment with the above idea. It is tiny which shows how awesome the environment API is already! On the Vite side, apart from setting the environment config properly ( function configureServer(server) {
const environment = server.environments.ssr;
const ENDPOINT = `/@vite/transport`;
server.middlewares.use((req, res, next) => {
async function handleInvoke() {
// Quick and dirty body parser
const chunks: Buffer[] = [];
for await (const chunk of req) {
chunks.push(chunk);
}
const json = JSON.parse(Buffer.concat(chunks as Buffer[]).toString());
if (
json.type === "custom" &&
json.event === "vite:invoke" &&
json.data.name === "fetchModule"
) {
const result = await (environment.fetchModule as any)(
...json.data.data
);
res.end(JSON.stringify({ result }));
return;
}
console.error("Unknown invocation");
console.error(json);
res.statusCode = 500;
res.end();
}
try {
if (req.url === ENDPOINT && req.method === "POST") {
handleInvoke().catch(next);
} else {
next();
}
} catch (error) {
next(error);
}
});
const proxy = createProxyServer();
return () => {
server.middlewares.use(async (req, res, next) => {
try {
console.log("Proxying", req.method, req.url);
proxy.web(req, res, { target: "http://localhost:7676" });
} catch (error) {
next(error);
}
});
};
} and on the Fastly side, we use this endpoint as our one way transport mechanism: /// <reference types="@fastly/js-compute" />
import { ModuleRunner } from "vite/module-runner";
const runner = new ModuleRunner({
hmr: false,
transport: {
async invoke(data) {
return fetch("http://localhost:5173/@vite/transport", {
method: "POST",
body: JSON.stringify(data),
}).then((r) => {
if (!r.ok) {
return { error: new Error(`Transport error ${r.status}`) };
}
return r.json() as Promise<any>;
});
},
},
});
addEventListener("fetch", (event) => event.respondWith(handleRequest(event)));
async function handleRequest(event: FetchEvent) {
const module = await runner.import("/src/entry.fastly.ts");
return await module.default(event);
} It could be extended to support two-way communication by implementing a suitable endpoint on the Fastly side as well but I didn't bother since Fastly Compute recreates the entire JavaScript context for every request so HMR is not useful anyway. It refetches and reevaluates every module for every request but it's still much much faster and better than the very slow wasm rebuilds. Just my two cents. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
There is a growing need to standardise the API around environment communication. Even the default SSR environment currently doesn't expose any methods to run the code.
We have decided to implement
RunnableDevEnvironment
for the default SSR environment that exposes therunner
andimport
directly to decrease the boilerplate (no need to runcreateModuleRunner
manually):Taking this idea further, we think it would be good for the ecosystem to have a standard way to run the code via the Fetch API. This is already how most third-party tools operate, and we have seen more and more requests to support this out of the box. This is where the new
FetchableDevEnvironment
comes in.FetchableDevEnvironment
is aDevEnvironment
that has adispatchFetch
method:FetchableDevEnvironment
class is not exposed (only its type). The class is considered an implementation detail and instead, we will expose acreateFetchableDevEnvironment
function to create the environment like we already do withcreateServer
. You can call it inside theenvironments.dev.createEnvironment
config option:dispatchFetch
is not called anywhere by Vite itself. The expectation is that frameworks can use this to run the code in the adapter's environment:We are also thinking about providing utility functions to convert the
http.IncommingMessage
toRequest
andhttp.ServerResponse
toResponse
. We might also need a function to combineResponse
+http.ServerResponse
.Fetchable Module Runner API
To make things easier on the module runner side, we also want to introduce a
createFetchableModuleRunner
function to create a runner that uses globalfetch
to fetch the module information. If globalfetch
is not available, thecreateFetchableModuleRunner
will throw an error.To achieve this, Vite implements a middleware that intercepts all requests to
/@vite/import/{env}/{url}
(the naming is based on already established/@vite/client
and/@vite/env
) and responds with the result ofenvironment.fetchModule
back to the runner:Default SSR environment as
FetchableDevEnvironment
To reduce boilerplate even further, we could also expose the
dispatchFetch
method on the default SSR environment. At the minimum, this would require:entryPoint
option in the SSR section of the configexport default { fetch }
)The internal implementation might look something like this:
Currently, we don't have plans to do this, but any feedback regarding this is welcome.
Previous discussions:
Beta Was this translation helpful? Give feedback.
All reactions