Skip to content

Next.js 自定义服务器 🖥️

🎯 概述

虽然 Next.js 内置服务器已经很强大,但有时需要自定义服务器来实现特殊需求。

📦 基本设置

创建服务器

js
// server.js
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const hostname = 'localhost'
const port = 3000

const app = next({ dev, hostname, port })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  createServer(async (req, res) => {
    try {
      const parsedUrl = parse(req.url, true)
      await handle(req, res, parsedUrl)
    } catch (err) {
      console.error('Error occurred handling', req.url, err)
      res.statusCode = 500
      res.end('internal server error')
    }
  }).listen(port, (err) => {
    if (err) throw err
    console.log(`> Ready on http://${hostname}:${port}`)
  })
})

package.json

json
{
  "scripts": {
    "dev": "node server.js",
    "build": "next build",
    "start": "NODE_ENV=production node server.js"
  }
}

🚀 Express 集成

js
// server.js
const express = require('express')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  const server = express()

  // 自定义路由
  server.get('/api/custom', (req, res) => {
    res.json({ message: 'Custom API' })
  })

  // 所有其他请求交给 Next.js
  server.all('*', (req, res) => {
    return handle(req, res)
  })

  server.listen(3000, (err) => {
    if (err) throw err
    console.log('> Ready on http://localhost:3000')
  })
})

📝 WebSocket 支持

js
// server.js
const { createServer } = require('http')
const { Server } = require('socket.io')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  const server = createServer((req, res) => {
    handle(req, res)
  })

  const io = new Server(server)

  io.on('connection', (socket) => {
    console.log('Client connected')

    socket.on('message', (data) => {
      io.emit('message', data)
    })

    socket.on('disconnect', () => {
      console.log('Client disconnected')
    })
  })

  server.listen(3000)
})

🎨 中间件

js
// server.js
const express = require('express')
const next = require('next')
const compression = require('compression')
const helmet = require('helmet')

const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  const server = express()

  // 安全头部
  server.use(helmet())

  // Gzip 压缩
  server.use(compression())

  // 日志
  server.use((req, res, next) => {
    console.log(`${req.method} ${req.url}`)
    next()
  })

  server.all('*', (req, res) => {
    return handle(req, res)
  })

  server.listen(3000)
})

📚 最佳实践

1. 错误处理

js
server.use((err, req, res, next) => {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})

2. 性能监控

js
server.use((req, res, next) => {
  const start = Date.now()
  res.on('finish', () => {
    const duration = Date.now() - start
    console.log(`${req.method} ${req.url} - ${duration}ms`)
  })
  next()
})

3. 限制

⚠️ 使用自定义服务器会失去:

  • 自动静态优化
  • Serverless 函数
  • 某些 Vercel 功能

🔗 相关资源


下一步:学习 Next.js 环境变量