You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -6,102 +6,180 @@ Vite, despite being mainly a frontend tool, has support for transpiling server-s
6
6
7
7
Vite's official SSR guide describes a workflow where Vite's development server is used as a middleware function in a server application made with a [Connect](https://github.com/senchalabs/connect) compatible Node.js framework (like [Express](https://expressjs.com)). If your server-side code needs transpilation (e.g. for TypeScript or JSX), you're required to use another set of tools (say [`ts-node`](https://typestrong.org/ts-node/) and [`nodemon`](https://nodemon.io/)) for development and building. `vavite` enables you to use Vite itself to transpile all of your server-side code.
8
8
9
-
## Operating modes
9
+
## Getting started
10
10
11
-
Vavite has two operating modes, determined by the `type` property of the entries you provide via the `entries` configuration option:
11
+
The easiest way to get started is to use one of the [examples](#examples) as a template. To start from scratch, follow these steps:
12
12
13
-
### Handler mode (`type: "runnable-handler"`)
13
+
1. Install `vavite` as a development dependency in your project.
14
+
```sh
15
+
npm install --save-dev vavite
16
+
```
17
+
2. Add the `vavite` plugin to your Vite config.
14
18
15
-
In this mode, Vavite will import your entry and call the exported `node:http`-compatible request handler for incoming requests. This is the default mode and is suitable for most use cases. In this mode, your entry module will not be loaded until the first request comes in.
19
+
```ts
20
+
// vite.config.ts
21
+
import { defineConfig } from"vite";
22
+
import { vavite } from"vavite";
16
23
17
-
For production, you need to explicitly start the server by calling `app.listen` or similar in your entry module, guarded by `if (import.meta.env.COMMAND === "build") { ... }` to prevent it from running in development.
24
+
exportdefaultdefineConfig({
25
+
appType: "custom", // Prevent Vite from serving index.html for the / route
26
+
plugins: [
27
+
vavite({
28
+
// Optional configuration options go here
29
+
}),
30
+
],
31
+
});
32
+
```
18
33
19
-
### Server mode (`type: "runnable-server"`)
34
+
3. Now you can create a handler or server entry (Vavite looks for `/src/entry.server.{js,ts,jsx,tsx}` by default).
20
35
21
-
In this mode, Vavite will run your entry which is expected to start a server on its own, on a separate port from the Vite's dev server. Vavite will proxy incoming requests to that server. This mode is less efficient and less well tested but it can be useful if you need control over server creation even in development or you want to use an HTTP serving library that is not compatible with `node:http`, like `Bun.serve`.
36
+
## Entry types
22
37
23
-
## Examples
38
+
Vavite recognizes two types of entries specified by the `type` property of the entries you provide via the `entries` configuration option: handler entries (`"runnable-handler"`, the default) and server entries (`"runnable-server"`).
24
39
25
-
The easiest way to start with Vavite is to follow the examples:
40
+
Handler entries are expected to export a `node:http`-compatible request handler which will be added to Vite's dev server as a middleware. Server entries are expected to start a server on their own, on a separate port from the Vite's dev server. Vavite will proxy incoming requests to that server.
26
41
27
-
- Handler entry setups:
28
-
- Server-only setups:
29
-
-[simple-standalone](/examples/simple-standalone): Simple `node:http` server example ([Stackblitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/simple-standalone))
30
-
-[express](/examples/express): Integrating with Express ([Stackblitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/express))
31
-
-[koa](/examples/koa): Integrating with Koa ([Stackblitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/koa))
32
-
-[fastify](/examples/fastify): Integrating with Fastify ([Stackblitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/fastify))
33
-
-[hapi](/examples/hapi): Integrating with Hapi ([Stackblitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/hapi))
34
-
-[Nest.js](/examples/nestjs): [Nest.js](https://nestjs.com/) with Express ([Stackblitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/nestjs))
35
-
- SSR setups
36
-
-[ssr-react-express](/examples/ssr-react-express): React SSR with Express ([Stackblitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/ssr-react-express))
37
-
- Other examples
38
-
-[resource-cleanup](/examples/resource-cleanup): Demonstrating patterns for cleaning up resources on hot reload
39
-
-[ws](/examples/ws): WebSocket server example ([Stackblitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/ws))
40
-
- Server entry setups:
41
-
-[express-server](/examples/express-server): Integrating with Express in server mode ([Stackblitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/express-server))
42
-
-[bun-server](/examples/bun-server): Integrating with `Bun.serve` in server mode
42
+
### Handler entries (`type: "runnable-handler"`)
43
43
44
-
## Usage
44
+
Handler entries are expected to export a `node:http`-compatible request handler. For this entry type, during develompent (`vite dev`) Vavite will import your entry and call the exported handler for each incoming request. For production, you need to explicitly start the server.
45
+
46
+
The simplest handler entry looks like this:
47
+
48
+
```ts
49
+
// entry.server.ts
50
+
51
+
// Alternatively, you can put add these to the `types` option of your tsconfig.json
console.log("Server is listening on http://localhost:3000");
77
+
});
78
+
}
47
79
48
-
```sh
49
-
npm install --save-dev vavite
80
+
// Enable hot module replacement
81
+
if (import.meta.hot) {
82
+
import.meta.hot.accept();
83
+
}
50
84
```
51
85
52
-
### Server-only setup
86
+
The above example uses `node:http` but it's possible to get a hold of a compatible handler from most Node.js server frameworks. See the examples for Express, Fastify, Koa, Hapi, and Nest.js for more details.
87
+
88
+
### Server entries (`type: "runnable-server"`)
53
89
54
-
In this setup, Vavite will route requests to your handler or server before most of Vite's own middlewares, effectively bypassing Vite's most client-side features. You also need `appType: "custom"` and a `builder.buildApp` option to only build the server environment.
90
+
Server entries are expected to start an HTTP server (on a separate port from the Vite's dev server) and process incoming requests. For development, Vavite will proxy incoming requests to that server. For production, your server will be the one directly receiving incoming requests.
91
+
92
+
Proxying is less efficient than direct function calls and is less well tested. But it is useful when you need control over server creation even in development or when you want to use an HTTP serving library that is not compatible with `node:http`, like `Bun.serve`.
93
+
94
+
Since this isn't the default entry type, you need to specify the entry type and the address it will be running on explicitly in the Vite config:
55
95
56
96
```ts
57
97
// vite.config.ts
98
+
99
+
// Alternatively, you can put add these to the `types` option of your tsconfig.json
100
+
/// <referencetypes="vite/client" />
101
+
/// <referencetypes="vavite/types" />
102
+
58
103
import { defineConfig } from"vite";
59
104
import { vavite } from"vavite";
60
105
61
106
exportdefaultdefineConfig({
62
-
appType: "custom",
63
-
builder: {
64
-
async buildApp(builder) {
65
-
awaitbuilder.build(builder.environments.ssr!);
66
-
},
67
-
},
68
-
plugins: [vavite()],
107
+
appType: "custom", // Prevent Vite from serving index.html for the / route
108
+
plugins: [
109
+
vavite({
110
+
entries: [
111
+
{
112
+
entry: "/src/entry.server",
113
+
type: "runnable-server",
114
+
proxyOptions: {
115
+
target: "http://localhost:3000",
116
+
},
117
+
},
118
+
],
119
+
}),
120
+
],
69
121
});
70
122
```
71
123
72
-
Then add a `src/entry.server.ts` (or name it explicitly via Vavite's `entries` option):
124
+
And the server entry itself looks like this:
73
125
74
126
```ts
75
127
// entry.server.ts
128
+
import { createServer } from"node:http";
76
129
77
-
// Alternatively, you can put the following in the `types` option of your tsconfig.json
Each entry has an optional `order` property which can be either `"pre"` or `"post"`. It determines whether the entry will be placed before or after Vite's own middlewares. `"pre"` entries will run before Vite's middlewares while `"post"` entries will run after. If not specified, Vavite will try to automatically determine the order based on whether you have configured a client entry or not. If you have a client entry, the default order will be `"post"`, otherwise it will be `"pre"`.
84
147
85
-
// Add your middleware and routes here
148
+
`"pre"` entries are useful for server-only setups where you don't need Vite's client-side features or you need some processing before Vite's middlewares.
86
149
87
-
// Default export a Connect-compatible handler for dev
88
-
exportapp.getNodeHttpHandler();
150
+
`"post"` entries are useful for SSR setups where you want to leverage Vite's asset transformation pipeline. In this case, you can import client assets in your server code and Vite will transform them correctly in development and production.
89
151
90
-
if (import.meta.env.COMMAND==="build") {
91
-
// Start the server in production mode
92
-
app.listen(3000, () => {
93
-
console.log("Server is listening on http://localhost:3000");
94
-
});
95
-
}
152
+
You can mark an entry with `final: true` to stop the request processing chain. Vavite will mark an entry as final by default if it is the last entry in the chain (respecting the pre/post order).
96
153
97
-
if (import.meta.hot) {
98
-
import.meta.hot.accept();
99
-
}
154
+
In development, non-final entries can pass unhandled requests to the next entry or Vite's own middlewares. This can be useful, for example, when you want to selectively pass some requests to Vite's own middlewares from `"pre"` entries. `/@vite/client` is a common example of this, since it needs to be handled by Vite's own middlewares for client-side HMR to work.
155
+
156
+
To forward unhandled requests in handler entries, just call the `next` function provided as the third argument. For server entries, return a response with status code 404 and the header `Vavite-Try-Next-Upstream` set to `true`. Note that the latter is less well tested and some proxy options might not work as expected when you use this feature.
157
+
158
+
In production, Vite's middleware stack will not exist so you will need to handle the processing order yourself. Typically, you will start a server for the frontmost entry and pass unhandled requests to the next handler by placing it in the middleware stack or to the next server by proxying.
159
+
160
+
### Server-only setup
161
+
162
+
In this setup, Vavite will route requests to your handler or server before most of Vite's own middlewares, effectively bypassing Vite's most client-side features. Typically, you also need `appType: "custom"` and a `builder.buildApp` option to only build the server environment.
163
+
164
+
```ts
165
+
// vite.config.ts
166
+
import { defineConfig } from"vite";
167
+
import { vavite } from"vavite";
168
+
169
+
exportdefaultdefineConfig({
170
+
appType: "custom",
171
+
builder: {
172
+
async buildApp(builder) {
173
+
awaitbuilder.build(builder.environments.ssr!);
174
+
},
175
+
},
176
+
plugins: [vavite()],
177
+
});
100
178
```
101
179
102
180
### SSR setup
103
181
104
-
In this setup, Vavite will place your server handler after Vite's asset transformation pipeline. It will do it automatically if it detects a client entry in your Vite config. If it fails for any reason, use `vavite({ entries: [{ entry: "/src/entry.server", order: "post" }] })` to force it to insert the handler in the correct position.
182
+
In this setup, Vavite will place your server handler after Vite's asset transformation pipeline.
You can use `vite build --app` to build both environments or you can provide a `builder.buildApp` option to control the order of the builds.
139
217
140
-
### Other features
218
+
In production, Vite's asset transformation pipeline will not exist. Instead, you will typically serve the built client assets with a static file serving middleware.
219
+
220
+
## Passing unhandled requests to the next entry or Vite
221
+
222
+
## Examples
223
+
224
+
- Handler entry setups:
225
+
- Server-only setups:
226
+
-[simple-standalone](/examples/simple-standalone): Simple `node:http` server example ([Stackblitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/simple-standalone))
227
+
-[express](/examples/express): Integrating with Express ([Stackblitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/express))
228
+
-[koa](/examples/koa): Integrating with Koa ([Stackblitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/koa))
229
+
-[fastify](/examples/fastify): Integrating with Fastify ([Stackblitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/fastify))
230
+
-[hapi](/examples/hapi): Integrating with Hapi ([Stackblitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/hapi))
231
+
-[Nest.js](/examples/nestjs): [Nest.js](https://nestjs.com/) with Express ([Stackblitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/nestjs))
232
+
- SSR setups
233
+
-[ssr-react-express](/examples/ssr-react-express): React SSR with Express ([Stackblitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/ssr-react-express))
234
+
- Other examples
235
+
-[resource-cleanup](/examples/resource-cleanup): Demonstrating patterns for cleaning up resources on hot reload
236
+
-[ws](/examples/ws): WebSocket server example ([Stackblitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/ws))
237
+
- Server entry setups:
238
+
-[express-server](/examples/express-server): Integrating with Express in server mode ([Stackblitz](https://stackblitz.com/github/cyco130/vavite/tree/main/examples/express-server))
239
+
-[bun-server](/examples/bun-server): Integrating with `Bun.serve` in server mode
240
+
241
+
## Other features and considerations
242
+
243
+
### Environment information and Vite dev server access
141
244
142
245
By default, Vavite will expose some information about the environment your code is running on:
143
246
@@ -232,13 +335,12 @@ All packages under the `@vavite` namespace and the `vavite` CLI command are now
232
335
233
336
### Other changes needed in the Vite config
234
337
235
-
- Set `appType: "custom"` unless you really want Vite to server`index.html` for the `/` route.
338
+
- Set `appType: "custom"` unless you really want Vite to serve`index.html` for the `/` route.
236
339
- Remove `buildSteps` and use `vite build --app` or the `builder.buildApp` Vite config option to orchestrate the build process programmatically.
237
340
238
341
### Changes needed in your server code
239
342
240
-
- Do not call the `next` function from your exported handler entry, just end the response with a 404.
241
-
- Explicitly add code to start the server in production: `if (import.meta.env.COMMAND === "serve") { startMyServer(myHandler); }`
343
+
- For handler entry setups, explicitly add code to start the server in production: `if (import.meta.env.COMMAND === "serve") { startMyServer(myHandler); }`
242
344
- Import `viteDevServer` from `vavite:vite-dev-server` instead of `vavite/vite-dev-server`.
243
345
- Add `if (import.meta.hot) { import.meta.hot.accept(); }` to your server entry file for better performance.
0 commit comments