+serverEntry
Define +serverEntry.js to control how your server starts in production: Vike builds it into dist/server/index.mjs, the file you run in production.
// +serverEntry.js
// Your +server.js exports, e.g. `{ fetch }`
import server from 'vike:server'
// startServer() can be anything, typically a deployment or server tool
startServer({
port: 3000,
onRequest: (request) => server.fetch(request)
})// +serverEntry.ts
// Your +server.js exports, e.g. `{ fetch }`
import server from 'vike:server'
// startServer() can be anything, typically a deployment or server tool
startServer({
port: 3000,
onRequest: (request) => server.fetch(request)
})# Build: +serverEntry.ts => dist/server/index.mjs
$ vike build
# Run +serverEntry.ts
$ node dist/server/index.mjsIt gives you full control over your server production entry, for example to:
- Deploy to a platform without built-in support, e.g. AWS Lambda (see example below)
- Use another server framework or runtime API such as
Bun.serve()andDeno.serve() - Use all CPU cores with Node.js's
clustermodule
Without
+serverEntry.js,dist/server/index.mjsis Vike's built-in server entry: a srvx server entry with first-class support for Node.js, Deno, and Bun (and potentially future runtimes). See also: Integration >+server.
Vike uses Universal Deploy under the hood: it provides Vike's built-in server and integrates with deployment providers (e.g. Cloudflare and Vercel).
vike:server
import server from 'vike:server' is your +server.js exports, e.g. { fetch }. (If you don't define +server.js, then it's Vike's built-in server.)
You can use it to handle requests before they reach +server.js:
// +serverEntry.js
import { serve } from 'srvx'
import server from 'vike:server'
serve({
port: 3000,
fetch(request) {
if (new URL(request.url).pathname === '/health') return new Response('OK')
return server.fetch(request)
}
})// +serverEntry.ts
import { serve } from 'srvx'
import server from 'vike:server'
serve({
port: 3000,
fetch(request) {
if (new URL(request.url).pathname === '/health') return new Response('OK')
return server.fetch(request)
}
})Example: AWS Lambda
AWS Lambda calls the handler() function exported by dist/server/index.mjs. You can implement it natively or with srvx.
With AWS Lambda, you usually serve the static assets
dist/client/with a CDN, such as Amazon CloudFront.
Native
// +serverEntry.js
import server from 'vike:server'
// AWS Lambda calls handler() for each request (Lambda function URL or API Gateway HTTP API)
export const handler = async (event) => {
// AWS Lambda event => Request
const url = new URL(`https://${event.requestContext.domainName}${event.rawPath}`)
url.search = event.rawQueryString
const headers = new Headers(event.headers)
if (event.cookies) headers.set('cookie', event.cookies.join('; '))
const body = event.body
? Buffer.from(event.body, event.isBase64Encoded ? 'base64' : 'utf8')
: undefined
const request = new Request(url, { method: event.requestContext.http.method, headers, body })
// Your +server.js exports
const response = await server.fetch(request)
// Response => AWS Lambda result
return {
statusCode: response.status,
headers: Object.fromEntries([...response.headers].filter(([name]) => name !== 'set-cookie')),
cookies: response.headers.getSetCookie(),
body: Buffer.from(await response.arrayBuffer()).toString('base64'),
isBase64Encoded: true
}
}// +serverEntry.ts
import type { APIGatewayProxyHandlerV2 } from 'aws-lambda'
import server from 'vike:server'
// AWS Lambda calls handler() for each request (Lambda function URL or API Gateway HTTP API)
export const handler: APIGatewayProxyHandlerV2 = async (event) => {
// AWS Lambda event => Request
const url = new URL(`https://${event.requestContext.domainName}${event.rawPath}`)
url.search = event.rawQueryString
const headers = new Headers(event.headers as Record<string, string>)
if (event.cookies) headers.set('cookie', event.cookies.join('; '))
const body = event.body ? Buffer.from(event.body, event.isBase64Encoded ? 'base64' : 'utf8') : undefined
const request = new Request(url, { method: event.requestContext.http.method, headers, body })
// Your +server.js exports
const response = await server.fetch(request)
// Response => AWS Lambda result
return {
statusCode: response.status,
headers: Object.fromEntries([...response.headers].filter(([name]) => name !== 'set-cookie')),
cookies: response.headers.getSetCookie(),
body: Buffer.from(await response.arrayBuffer()).toString('base64'),
isBase64Encoded: true
}
}srvx
// +serverEntry.js
import { toLambdaHandler } from 'srvx/aws-lambda'
import server from 'vike:server'
// AWS Lambda calls handler() for each request
export const handler = toLambdaHandler({ fetch: server.fetch })// +serverEntry.ts
import { toLambdaHandler } from 'srvx/aws-lambda'
import server from 'vike:server'
// AWS Lambda calls handler() for each request
export const handler = toLambdaHandler({ fetch: server.fetch })Example: Bun
You can call Bun.serve() yourself or use srvx (which uses Bun.serve() when running with Bun).
$ vike build
$ bun dist/server/index.mjsYou don't need
+serverEntry.jsto use Bun: without it,$ bun dist/server/index.mjsruns Vike's built-in server entry powered by srvx which automatically usesBun.serve()under the hood.
Native
// +serverEntry.js
import server from 'vike:server'
Bun.serve({
port: 3000,
async fetch(request) {
// Serve the static assets (dist/client/)
const file = Bun.file(`${import.meta.dirname}/../client${new URL(request.url).pathname}`)
if (await file.exists()) return new Response(file)
// Your +server.js exports
return server.fetch(request)
}
})// +serverEntry.ts
import server from 'vike:server'
Bun.serve({
port: 3000,
async fetch(request) {
// Serve the static assets (dist/client/)
const file = Bun.file(`${import.meta.dirname}/../client${new URL(request.url).pathname}`)
if (await file.exists()) return new Response(file)
// Your +server.js exports
return server.fetch(request)
}
})srvx
// +serverEntry.js
import { serve } from 'srvx'
import { staticMiddleware } from 'srvx/static'
import server from 'vike:server'
serve({
port: 3000,
// Serve the static assets (dist/client/)
middleware: [staticMiddleware({ dir: `${import.meta.dirname}/../client` })],
// Your +server.js exports
fetch: server.fetch
})// +serverEntry.ts
import { serve } from 'srvx'
import { staticMiddleware } from 'srvx/static'
import server from 'vike:server'
serve({
port: 3000,
// Serve the static assets (dist/client/)
middleware: [staticMiddleware({ dir: `${import.meta.dirname}/../client` })],
// Your +server.js exports
fetch: server.fetch
})Development
+serverEntry.js is only used in production. In development, $ vike dev uses Vite's development server with your +server.js (as usual, with HMR).
Static assets
When using +serverEntry.js, it's your responsibility to serve the static assets dist/client/.
If you don't use
+serverEntry.js, then Vike's built-in server automatically serves the static assets.
See
+precompressto generate.br/.gzfiles for your static assets at build time.
Pre-rendering
Pre-rendering doesn't execute +serverEntry.js.
If all your pages are pre-rendered,
dist/server/is still kept (Vike otherwise removes it, see API >+prerender>keepDistServer).