Skip to content

vikejs/vike-node

Repository files navigation

npm version

vike-node

Warning

This package is in beta and will have relatively frequent breaking changes.

Server integration for Vike.

With this extension, your server is transpiled with Vite. So that you don't need ts-node/tsx anymore.

In development, the server process is restarted when a change is detected.

Installation
Custom pageContext
Standalone build
External packages
Compression
Version history


Installation

Overview
Add to existing server
Supported servers

Overview

Example of adding vike-node and Express.js to a Vike app that doesn't use a server yet.

Note

  1. npm install vike-node express
  2. Extend vite.config.js:
    // vite.config.js
    
    import vikeNode from 'vike-node/plugin'
    
    export default {
      // ...
      plugins: [vikeNode('server/index.js')]
    }
  3. Create server/index.js:
    // server/index.js
    
    import express from 'express'
    import vike from 'vike-node/express'
    
    startServer()
    
    function startServer() {
      const app = express()
      app.use(vike())
      const port = 3000
      app.listen(port, () => console.log(`Server running at http://localhost:${port}`))
    }
  4. Add production script:
      // package.json
    
      "scripts": {
        "dev": "vite",
        "build": "vite build",
    +   "prod": "NODE_ENV=production node dist/server/index.mjs"
      }

Add to existing server

If you already have a server:

// server/index.js

- import { renderPage } from 'vike/server'
+ import vike from 'vike-node/express'

- if (isProduction) {
-   app.use(express.static(`${root}/dist/client`))
- } else {
-   const vite = await import('vite')
-   const viteDevMiddleware = (
-     await vite.createServer({
-       root,
-       server: { middlewareMode: true }
-     })
-   ).middlewares
-   app.use(viteDevMiddleware)
- }

- app.get('*', async (req, res, next) => {
-   const pageContextInit = {
-     urlOriginal: req.originalUrl
-   }
-   const pageContext = await renderPage(pageContextInit)
-   const { httpResponse } = pageContext
-   if (!httpResponse) {
-     return next()
-   } else {
-     const { statusCode, headers } = httpResponse
-     headers.forEach(([name, value]) => res.setHeader(name, value))
-     res.status(statusCode)
-     httpResponse.pipe(res)
-   }
- })

+ app.use(vike())
  // package.json

  "scripts": {
    "build": "vite build",
-   "dev": "node ./server/index.js",
+   "dev": "vite",
-   "prod": "NODE_ENV=production node ./server/index.js"
+   "prod": "NODE_ENV=production node dist/server/index.mjs"
  }

Supported servers

vike-node includes middlewares for all commonly used server frameworks.

See complete list of supported servers.

Express

// server/index.js

import express from 'express'
import vike from 'vike-node/express'

startServer()

function startServer() {
  const app = express()
  app.use(vike())
  const port = 3000
  app.listen(port, () => console.log(`Server running at http://localhost:${port}`))
}

Fastify

// server/index.js

import fastify from 'fastify'
import vike from 'vike-node/fastify'

startServer()

function startServer() {
  const app = fastify()
  app.all('/*', vike())
  const port = 3000
  app.listen({ port }, () => console.log(`Server running at http://localhost:${port}`))
}

Hono

// server/index.js

import { serve } from '@hono/node-server'
import { Hono } from 'hono'
import vike from 'vike-node/hono'

startServer()

function startServer() {
  const app = new Hono()
  app.use(vike())
  const port = 3000
  serve(
    {
      fetch: app.fetch,
      port
    },
    () => console.log(`Server running at http://localhost:${port}`)
  )
}

H3

// server/index.js

import { createApp, toNodeListener } from 'h3'
import { createServer } from 'http'
import vike from 'vike-node/h3'

startServer()

async function startServer() {
  const app = createApp()
  app.use(vike())
  const port = 3000
  const server = createServer(toNodeListener(app)).listen(port)
  server.on('listening', () => {
    console.log(`Server running at http://localhost:${port}`)
  })
}

Elysia

// server/index.js

import { Elysia } from 'elysia'
import vike from 'vike-node/elysia'

startServer()

function startServer() {
  const app = new Elysia()
  app.get('/*', vike())
  const port = 3000
  app.listen(port, () => console.log(`Server running at http://localhost:${port}`))
}

Custom pageContext:

You can define custom pageContext properties:

import { type RuntimeAdapter } from 'vike-node/express';

app.use(
  vike({
    pageContext(runtime: RuntimeAdapter) {
      return {
        user: runtime.req.user
      }
    }
  })
)

Note

See RuntimeAdapter (vike-node uses universal-middleware under the hood).

Note

The runtime object is also available at pageContext.runtime so that, even without the custom pageContext function above, you can retrieve pageContext.runtime.req.user in Vike hooks and UI components (with usePageContext()).


Standalone build

You can enable standalone builds by setting standalone to true.
After build, the output dist folder will contain everything for a deployment.
With standalone mode, the production environment only needs the dist folder to be present.
Example start script: NODE_ENV=production node dist/server/index.mjs

// vite.config.js

import vikeNode from 'vike-node/plugin'

export default {
  // ...
  plugins: [
    vikeNode({
      entry: 'server/index.js',
      standalone: true
    })
  ]
}

External packages

Packages that import native binaries/custom assets need to be added to external.
When building with standalone enabled, external packages and their assets are copied to the output dist directory.
By default, the external setting includes:

  • sharp
  • @prisma/client
  • @node-rs/*
// vite.config.js

import vikeNode from 'vike-node/plugin'

export default {
  // ...
  plugins: [
    vikeNode({
      entry: 'server/index.js',
      standalone: true,
      external: ['my-rust-package']
    })
  ]
}

Compression

In production, vike-node compresses all Vike responses.

You can disable it:

app.use(
  vike({
    compress: false
  })
)