Skip to content

Commit ecf5ec1

Browse files
authored
docs: organize "Environment API for Frameworks" page based on communication levels (vitejs#20388)
1 parent 588b3b5 commit ecf5ec1

File tree

2 files changed

+51
-70
lines changed

2 files changed

+51
-70
lines changed

docs/guide/api-environment-frameworks.md

Lines changed: 49 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@ Resources:
1313
Please share your feedback with us.
1414
:::
1515

16-
## Environments and Frameworks
16+
## DevEnvironment Communication Levels
1717

18-
The implicit `ssr` environment and other non-client environments use a `RunnableDevEnvironment` by default during dev. While this requires the runtime to be the same with the one the Vite server is running in, this works similarly with `ssrLoadModule` and allows frameworks to migrate and enable HMR for their SSR dev story. You can guard any runnable environment with an `isRunnableDevEnvironment` function.
18+
Since environments may run in different runtimes, communication against the environment may have constraints depending on the runtime. To allow frameworks to write runtime agnostic code easily, the Environment API provides three kinds of communication levels.
19+
20+
### `RunnableDevEnvironment`
21+
22+
`RunnableDevEnvironment` is an environment that can communicate arbitrary values. The implicit `ssr` environment and other non-client environments use a `RunnableDevEnvironment` by default during dev. While this requires the runtime to be the same with the one the Vite server is running in, this works similarly with `ssrLoadModule` and allows frameworks to migrate and enable HMR for their SSR dev story. You can guard any runnable environment with an `isRunnableDevEnvironment` function.
1923

2024
```ts
2125
export class RunnableDevEnvironment extends DevEnvironment {
@@ -43,49 +47,6 @@ if (isRunnableDevEnvironment(server.environments.ssr)) {
4347
The `runner` is evaluated lazily only when it's accessed for the first time. Beware that Vite enables source map support when the `runner` is created by calling `process.setSourceMapsEnabled` or by overriding `Error.prepareStackTrace` if it's not available.
4448
:::
4549

46-
Frameworks that communicate with their runtime via the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch) can utilize the `FetchableDevEnvironment` that provides a standardized way of handling requests via the `handleRequest` method:
47-
48-
```ts
49-
import {
50-
createServer,
51-
createFetchableDevEnvironment,
52-
isFetchableDevEnvironment,
53-
} from 'vite'
54-
55-
const server = await createServer({
56-
server: { middlewareMode: true },
57-
appType: 'custom',
58-
environments: {
59-
custom: {
60-
dev: {
61-
createEnvironment(name, config) {
62-
return createFetchableDevEnvironment(name, config, {
63-
handleRequest(request: Request): Promise<Response> | Response {
64-
// handle Request and return a Response
65-
},
66-
})
67-
},
68-
},
69-
},
70-
},
71-
})
72-
73-
// Any consumer of the environment API can now call `dispatchFetch`
74-
if (isFetchableDevEnvironment(server.environments.custom)) {
75-
const response: Response = await server.environments.custom.dispatchFetch(
76-
new Request('/request-to-handle'),
77-
)
78-
}
79-
```
80-
81-
:::warning
82-
Vite validates the input and output of the `dispatchFetch` method: the request must be an instance of the global `Request` class and the response must be the instance of the global `Response` class. Vite will throw a `TypeError` if this is not the case.
83-
84-
Note that although the `FetchableDevEnvironment` is implemented as a class, it is considered an implementation detail by the Vite team and might change at any moment.
85-
:::
86-
87-
## Default `RunnableDevEnvironment`
88-
8950
Given a Vite server configured in middleware mode as described by the [SSR setup guide](/guide/ssr#setting-up-the-dev-server), let's implement the SSR middleware using the environment API. Remember that it doesn't have to be called `ssr`, so we'll name it `server` in this example. Error handling is omitted.
9051

9152
```js
@@ -142,41 +103,61 @@ app.use('*', async (req, res, next) => {
142103
})
143104
```
144105
145-
## Runtime Agnostic SSR
146-
147-
Since the `RunnableDevEnvironment` can only be used to run the code in the same runtime as the Vite server, it requires a runtime that can run the Vite Server (a runtime that is compatible with Node.js). This means that you will need to use the raw `DevEnvironment` to make it runtime agnostic.
106+
### `FetchableDevEnvironment`
148107
149-
:::info `FetchableDevEnvironment` proposal
108+
:::info
150109
151-
The initial proposal had a `run` method on the `DevEnvironment` class that would allow consumers to invoke an import on the runner side by using the `transport` option. During our testing we found out that the API was not universal enough to start recommending it. At the moment, we are looking for feedback on [the `FetchableDevEnvironment` proposal](https://github.com/vitejs/vite/discussions/18191).
110+
We are looking for feedback on [the `FetchableDevEnvironment` proposal](https://github.com/vitejs/vite/discussions/18191).
152111
153112
:::
154113
155-
`RunnableDevEnvironment` has a `runner.import` function that returns the value of the module. But this function is not available in the raw `DevEnvironment` and requires the code using the Vite's APIs and the user modules to be decoupled.
114+
`FetchableDevEnvironment` is an environment that can communicate with its runtime via the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch) interface. Since the `RunnableDevEnvironment` is only possible to implement in a limited set of runtimes, we recommend to use the `FetchableDevEnvironment` instead of the `RunnableDevEnvironment`.
156115
157-
For example, the following example uses the value of the user module from the code using the Vite's APIs:
116+
This environment provides a standardized way of handling requests via the `handleRequest` method:
158117
159118
```ts
160-
// code using the Vite's APIs
161-
import { createServer } from 'vite'
162-
163-
const server = createServer()
164-
const ssrEnvironment = server.environment.ssr
165-
const input = {}
119+
import {
120+
createServer,
121+
createFetchableDevEnvironment,
122+
isFetchableDevEnvironment,
123+
} from 'vite'
166124

167-
const { createHandler } = await ssrEnvironment.runner.import('./entrypoint.js')
168-
const handler = createHandler(input)
169-
const response = handler(new Request('/'))
125+
const server = await createServer({
126+
server: { middlewareMode: true },
127+
appType: 'custom',
128+
environments: {
129+
custom: {
130+
dev: {
131+
createEnvironment(name, config) {
132+
return createFetchableDevEnvironment(name, config, {
133+
handleRequest(request: Request): Promise<Response> | Response {
134+
// handle Request and return a Response
135+
},
136+
})
137+
},
138+
},
139+
},
140+
},
141+
})
170142

171-
// -------------------------------------
172-
// ./entrypoint.js
173-
export function createHandler(input) {
174-
return function handler(req) {
175-
return new Response('hello')
176-
}
143+
// Any consumer of the environment API can now call `dispatchFetch`
144+
if (isFetchableDevEnvironment(server.environments.custom)) {
145+
const response: Response = await server.environments.custom.dispatchFetch(
146+
new Request('/request-to-handle'),
147+
)
177148
}
178149
```
179150
151+
:::warning
152+
Vite validates the input and output of the `dispatchFetch` method: the request must be an instance of the global `Request` class and the response must be the instance of the global `Response` class. Vite will throw a `TypeError` if this is not the case.
153+
154+
Note that although the `FetchableDevEnvironment` is implemented as a class, it is considered an implementation detail by the Vite team and might change at any moment.
155+
:::
156+
157+
### raw `DevEnvironment`
158+
159+
If the environment does not implement the `RunnableDevEnvironment` or `FetchableDevEnvironment` interfaces, you need to set up the communication manually.
160+
180161
If your code can run in the same runtime as the user modules (i.e., it does not rely on Node.js-specific APIs), you can use a virtual module. This approach eliminates the need to access the value from the code using Vite's APIs.
181162
182163
```ts
@@ -197,9 +178,7 @@ const input = {}
197178

198179
// use exposed functions by each environment factories that runs the code
199180
// check for each environment factories what they provide
200-
if (ssrEnvironment instanceof RunnableDevEnvironment) {
201-
ssrEnvironment.runner.import('virtual:entrypoint')
202-
} else if (ssrEnvironment instanceof CustomDevEnvironment) {
181+
if (ssrEnvironment instanceof CustomDevEnvironment) {
203182
ssrEnvironment.runEntrypoint('virtual:entrypoint')
204183
} else {
205184
throw new Error(`Unsupported runtime for ${ssrEnvironment.name}`)

docs/guide/api-environment-runtimes.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ function createWorkerdDevEnvironment(
110110
}
111111
```
112112

113+
There are [multiple communication levels for the `DevEnvironment`](/guide/api-environment-frameworks#devenvironment-communication-levels). To make it easier for frameworks to write runtime agnostic code, we recommend to implement the most flexible communication level possible.
114+
113115
## `ModuleRunner`
114116

115117
A module runner is instantiated in the target runtime. All APIs in the next section are imported from `vite/module-runner` unless stated otherwise. This export entry point is kept as lightweight as possible, only exporting the minimal needed to create module runners.

0 commit comments

Comments
 (0)