注册插件 #

一、注册方式 #

1.1 基本注册 #

使用 register 方法注册插件:

javascript
const fastify = require('fastify')()

fastify.register(require('@fastify/cors'))

fastify.listen({ port: 3000 })

1.2 带选项注册 #

javascript
fastify.register(require('@fastify/cors'), {
  origin: '*',
  methods: ['GET', 'POST', 'PUT', 'DELETE']
})

1.3 前缀注册 #

javascript
fastify.register(require('./routes/api'), {
  prefix: '/api/v1'
})

1.4 异步注册 #

javascript
await fastify.register(require('@fastify/mongodb'), {
  url: 'mongodb://localhost:27017/mydb'
})

console.log('MongoDB plugin registered')

二、自动加载 #

2.1 使用@fastify/autoload #

javascript
const fastify = require('fastify')()
const path = require('path')

fastify.register(require('@fastify/autoload'), {
  dir: path.join(__dirname, 'plugins')
})

fastify.register(require('@fastify/autoload'), {
  dir: path.join(__dirname, 'routes')
})

2.2 目录结构 #

text
app/
├── plugins/
│   ├── database.js
│   ├── auth.js
│   └── cache.js
├── routes/
│   ├── users.js
│   └── products.js
└── app.js

2.3 自动加载选项 #

javascript
fastify.register(require('@fastify/autoload'), {
  dir: path.join(__dirname, 'plugins'),
  ignorePattern: /.*\.test\.js$/,
  dirNameRoutePrefix: (folderName) => {
    return folderName === 'api' ? '' : folderName
  },
  options: {
    database: { url: process.env.DB_URL }
  }
})

2.4 嵌套目录 #

text
routes/
├── api/
│   ├── v1/
│   │   ├── users.js    → /api/v1/users
│   │   └── products.js → /api/v1/products
│   └── v2/
│       └── users.js    → /api/v2/users
└── web/
    └── pages.js        → /web/pages

三、核心插件 #

3.1 @fastify/cors #

跨域资源共享:

javascript
fastify.register(require('@fastify/cors'), {
  origin: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 86400
})

自定义配置:

javascript
fastify.register(require('@fastify/cors'), {
  origin: (origin, cb) => {
    const allowedOrigins = ['https://example.com', 'https://api.example.com']
    
    if (!origin || allowedOrigins.includes(origin)) {
      cb(null, true)
    } else {
      cb(new Error('Not allowed'), false)
    }
  }
})

3.2 @fastify/helmet #

安全头设置:

javascript
fastify.register(require('@fastify/helmet'), {
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'unsafe-inline'"],
      styleSrc: ["'self'", "'unsafe-inline'"]
    }
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  }
})

3.3 @fastify/jwt #

JWT认证:

javascript
fastify.register(require('@fastify/jwt'), {
  secret: process.env.JWT_SECRET,
  sign: {
    expiresIn: '7d'
  }
})

fastify.post('/login', async (request, reply) => {
  const token = fastify.jwt.sign({ userId: 1 })
  return { token }
})

fastify.get('/protected', {
  preHandler: async (request, reply) => {
    await request.jwtVerify()
  }
}, async (request, reply) => {
  return { user: request.user }
})

3.4 @fastify/cookie #

Cookie处理:

javascript
fastify.register(require('@fastify/cookie'), {
  secret: 'my-secret-key',
  parseOptions: {
    httpOnly: true,
    secure: true,
    sameSite: 'strict'
  }
})

fastify.get('/set-cookie', async (request, reply) => {
  reply.setCookie('sessionId', 'abc123', {
    httpOnly: true,
    secure: true,
    maxAge: 3600
  })
  return { message: 'Cookie set' }
})

fastify.get('/get-cookie', async (request, reply) => {
  return { sessionId: request.cookies.sessionId }
})

3.5 @fastify/static #

静态文件服务:

javascript
fastify.register(require('@fastify/static'), {
  root: path.join(__dirname, 'public'),
  prefix: '/static/',
  maxAge: 31536000,
  decorateReply: true
})

fastify.register(require('@fastify/static'), {
  root: path.join(__dirname, 'uploads'),
  prefix: '/uploads/',
  decorateReply: false
})

3.6 @fastify/multipart #

文件上传:

javascript
fastify.register(require('@fastify/multipart'), {
  limits: {
    fileSize: 10 * 1024 * 1024,
    files: 5
  }
})

fastify.post('/upload', async (request, reply) => {
  const data = await request.file()
  
  const buffer = await data.toBuffer()
  
  return {
    filename: data.filename,
    mimetype: data.mimetype,
    size: buffer.length
  }
})

3.7 @fastify/rate-limit #

请求限流:

javascript
fastify.register(require('@fastify/rate-limit'), {
  max: 100,
  timeWindow: '1 minute',
  keyGenerator: (request) => {
    return request.ip
  }
})

fastify.get('/limited', {
  config: {
    rateLimit: {
      max: 10,
      timeWindow: '1 minute'
    }
  }
}, async (request, reply) => {
  return { message: 'Rate limited route' }
})

3.8 @fastify/swagger #

API文档:

javascript
fastify.register(require('@fastify/swagger'), {
  openapi: {
    openapi: '3.0.0',
    info: {
      title: 'My API',
      description: 'API documentation',
      version: '1.0.0'
    },
    servers: [
      { url: 'http://localhost:3000' }
    ]
  }
})

fastify.register(require('@fastify/swagger-ui'), {
  routePrefix: '/documentation',
  uiConfig: {
    docExpansion: 'list'
  }
})

fastify.get('/users', {
  schema: {
    description: 'Get all users',
    tags: ['Users'],
    response: {
      200: {
        type: 'array',
        items: {
          type: 'object',
          properties: {
            id: { type: 'integer' },
            name: { type: 'string' }
          }
        }
      }
    }
  }
}, async (request, reply) => {
  return [{ id: 1, name: 'John' }]
})

四、数据库插件 #

4.1 MongoDB #

javascript
fastify.register(require('@fastify/mongodb'), {
  url: 'mongodb://localhost:27017/mydb',
  database: 'mydb'
})

fastify.get('/users', async (request, reply) => {
  const users = await fastify.mongo.db.collection('users').find({}).toArray()
  return users
})

4.2 PostgreSQL #

javascript
fastify.register(require('@fastify/postgres'), {
  connectionString: 'postgresql://user:pass@localhost:5432/mydb'
})

fastify.get('/users', async (request, reply) => {
  const result = await fastify.pg.query('SELECT * FROM users')
  return result.rows
})

4.3 Redis #

javascript
fastify.register(require('@fastify/redis'), {
  host: 'localhost',
  port: 6379
})

fastify.get('/cache', async (request, reply) => {
  const cached = await fastify.redis.get('key')
  
  if (cached) {
    return JSON.parse(cached)
  }
  
  const data = { message: 'Hello' }
  await fastify.redis.set('key', JSON.stringify(data), 'EX', 3600)
  return data
})

五、会话插件 #

5.1 @fastify/session #

javascript
fastify.register(require('@fastify/cookie'))
fastify.register(require('@fastify/session'), {
  secret: 'a-very-long-secret-key-here',
  cookie: {
    secure: false,
    maxAge: 3600000
  },
  saveUninitialized: false
})

fastify.post('/login', async (request, reply) => {
  request.session.userId = 1
  return { message: 'Logged in' }
})

fastify.get('/profile', async (request, reply) => {
  if (!request.session.userId) {
    reply.code(401).send({ error: 'Not logged in' })
    return
  }
  return { userId: request.session.userId }
})

fastify.post('/logout', async (request, reply) => {
  request.session.destroy()
  return { message: 'Logged out' }
})

5.2 @fastify/secure-session #

javascript
fastify.register(require('@fastify/secure-session'), {
  secret: 'a-very-long-secret-key-here',
  salt: 'salt-value'
})

fastify.post('/login', async (request, reply) => {
  request.session.set('userId', 1)
  return { message: 'Logged in' }
})

fastify.get('/profile', async (request, reply) => {
  const userId = request.session.get('userId')
  if (!userId) {
    reply.code(401).send({ error: 'Not logged in' })
    return
  }
  return { userId }
})

六、表单插件 #

6.1 @fastify/formbody #

javascript
fastify.register(require('@fastify/formbody'))

fastify.post('/form', async (request, reply) => {
  return { body: request.body }
})

6.2 表单验证 #

javascript
fastify.post('/login', {
  schema: {
    body: {
      type: 'object',
      required: ['username', 'password'],
      properties: {
        username: { type: 'string' },
        password: { type: 'string', minLength: 6 }
      }
    }
  }
}, async (request, reply) => {
  const { username, password } = request.body
  return { username }
})

七、视图插件 #

7.1 @fastify/view #

javascript
fastify.register(require('@fastify/view'), {
  engine: {
    ejs: require('ejs')
  },
  root: path.join(__dirname, 'views'),
  layout: 'layout.ejs'
})

fastify.get('/page', async (request, reply) => {
  return reply.view('index.ejs', { title: 'Hello', message: 'World' })
})

7.2 多模板引擎 #

javascript
fastify.register(require('@fastify/view'), {
  engine: {
    ejs: require('ejs'),
    pug: require('pug'),
    handlebars: require('handlebars')
  },
  root: path.join(__dirname, 'views')
})

fastify.get('/ejs', async (request, reply) => {
  return reply.view('index.ejs', { title: 'EJS' })
})

fastify.get('/pug', async (request, reply) => {
  return reply.view('index.pug', { title: 'Pug' })
})

八、压缩插件 #

8.1 @fastify/compress #

javascript
fastify.register(require('@fastify/compress'), {
  global: true,
  threshold: 1024,
  encodings: ['gzip', 'deflate', 'br']
})

fastify.get('/large-data', async (request, reply) => {
  return { data: 'Large response data...' }
})

九、健康检查插件 #

9.1 @fastify/under-pressure #

javascript
fastify.register(require('@fastify/under-pressure'), {
  maxEventLoopDelay: 1000,
  maxHeapUsedBytes: 100000000,
  maxRssBytes: 100000000,
  message: 'Server under pressure',
  retryAfter: 50
})

fastify.get('/health', async (request, reply) => {
  return fastify.memoryUsage()
})

十、插件注册最佳实践 #

10.1 注册顺序 #

javascript
const fastify = require('fastify')()

fastify.register(require('@fastify/sensible'))

fastify.register(require('@fastify/cors'))
fastify.register(require('@fastify/helmet'))

fastify.register(require('./plugins/database'))
fastify.register(require('./plugins/redis'))
fastify.register(require('./plugins/auth'))

fastify.register(require('./routes'))

fastify.listen({ port: 3000 })

10.2 环境区分 #

javascript
if (process.env.NODE_ENV === 'development') {
  fastify.register(require('@fastify/swagger'))
  fastify.register(require('@fastify/swagger-ui'))
}

if (process.env.NODE_ENV === 'production') {
  fastify.register(require('@fastify/compress'))
}

10.3 错误处理 #

javascript
fastify.register(require('@fastify/mongodb'), {
  url: process.env.MONGODB_URL
}).after(err => {
  if (err) {
    fastify.log.error('MongoDB connection failed:', err)
    throw err
  }
  fastify.log.info('MongoDB connected')
})

十一、总结 #

本章我们学习了:

  1. 注册方式:基本注册、带选项注册、前缀注册
  2. 自动加载:@fastify/autoload的使用
  3. 核心插件:cors、helmet、jwt、cookie、static等
  4. 数据库插件:MongoDB、PostgreSQL、Redis
  5. 会话插件:session、secure-session
  6. 表单插件:formbody
  7. 视图插件:view
  8. 压缩插件:compress
  9. 健康检查:under-pressure
  10. 最佳实践:注册顺序、环境区分、错误处理

接下来让我们学习如何编写自定义插件!

最后更新:2026-03-28