vikejs / vike-node

🔨 Node integration for Vike
MIT License
20 stars 3 forks source link

npm version

vike-node

Node integration for Vike.

With this extension, your server-side code is transpiled with Vite.
In development, the server process is restarted when a change is detected in some of your server files.

Installation
Standalone build
External packages
Caching and compression
Custom pageContext
Framework examples
Migration guide


Installation

  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/connect'
    
    startServer()
    
    function startServer() {
     const app = express()
     app.use(vike())
     const port = process.env.PORT || 3000
     app.listen(port, () => console.log(`Server running at http://localhost:${port}`))
    }

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:

// vite.config.js

import vikeNode from 'vike-node/plugin'

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

Caching and compression:

In production, vike-node:

On a request, if the asset(.js, .css) is not in the cache, vike-node compresses it with a fast compression level, sends it in the response, then recompresses it with a high compression level and finally caches the compressed data.
You can disable compression/caching:

app.use(
  vike({
    compress: false,
    static: {
      cache: false
    }
  })
)

Custom pageContext:

You can define custom pageContext properties:

app.use(
  vike({
    pageContext: (req) => ({
      user: req.user
    })
  })
)

Framework examples:

vike-node includes middlewares for the most popular web frameworks:

Express:

// server/index.js

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

startServer()

function startServer() {
  const app = express()
  app.use(vike())
  const port = process.env.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.register(vike())
  const port = +(process.env.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 = +(process.env.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 = process.env.PORT || 3000
  const server = createServer(toNodeListener(app)).listen(port)
  server.on('listening', () => {
    console.log(`Server running at http://localhost:${port}`)
  })
}

Elysia (Bun):

// server/index.js

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

startServer()

function startServer() {
  const app = new Elysia()
  app.use(vike())
  const port = +(process.env.PORT || 3000)
  app.listen(port, () => console.log(`Server running at http://localhost:${port}`))
}

Migration guide:

// server/index.js

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

- 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": {
- "dev": "node ./server",
+ "dev": "vite",
}