home / skills / amnadtaowsoam / cerebraskills / fastify-rest-api

fastify-rest-api skill

/03-backend-api/fastify-rest-api

This skill helps you design high-performance Fastify REST APIs with schema validation, TS support, and robust error handling to improve DX and scalability.

npx playbooks add skill amnadtaowsoam/cerebraskills --skill fastify-rest-api

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
6.3 KB
---
name: Fastify REST API Patterns
description: High-performance Node.js web framework with built-in schema validation, JSON serialization, and TypeScript support.
---

# Fastify REST API Patterns

## Overview

Fastify is a high-performance Node.js web framework focused on speed and low overhead, featuring built-in schema validation and serialization, enabling developers to create production-ready APIs quickly.

Fastify consists of:
- **High Performance**: 2x faster than Express
- **JSON Schema Validation**: Built-in validation with JSON Schema
- **Fast Serialization**: Optimized JSON serialization
- **TypeScript Support**: First-class TypeScript support
- **Plugin System**: Extensible plugin architecture
- **Low Overhead**: Minimal request overhead

## Why This Matters

- **Enhance Performance**: Fastify can enhance API performance by 2-3x
- **Reduce Infrastructure Cost**: Higher throughput reduces infrastructure cost
- **Increase Developer Experience**: Built-in validation and serialization improve DX
- **Reduce Bugs**: Type-safe validation reduces bugs
- **Improve Scalability**: High-performance framework improves scalability

---

## Core Concepts

### 1. Application Setup

```typescript
import Fastify from 'fastify'

export async function buildApp(): Promise<FastifyInstance> {
  const app = Fastify({
    logger: {
      level: 'info',
    },
  })

  return app
}
```

### 2. Routing

```typescript
import { FastifyPluginAsync } from 'fastify'

const userRoutes: FastifyPluginAsync = async (fastify) => {
  fastify.get('/users', async (request, reply) => {
    return { users: [] }
  })

  fastify.post('/users', async (request, reply) => {
    return reply.status(201).send({ success: true })
  })
}

export default userRoutes
```

### 3. JSON Schema Validation

```typescript
const userProperties = {
  id: { type: 'string', format: 'uuid' },
  email: { type: 'string', format: 'email' },
  name: { type: 'string', minLength: 2, maxLength: 100 },
}

export const createUserSchema: FastifySchema = {
  body: {
    type: 'object',
    required: ['email', 'password', 'name'],
    properties: {
      email: { type: 'string', format: 'email' },
      password: { type: 'string', minLength: 8 },
      name: { type: 'string', minLength: 2, maxLength: 100 },
    },
  },
  response: {
    201: {
      type: 'object',
      properties: {
        success: { type: 'boolean' },
        data: {
          type: 'object',
          properties: userProperties,
        },
      },
    },
  },
}
```

### 4. Plugins

```typescript
import fp from 'fastify-plugin'

async function databasePlugin(fastify: FastifyInstance) {
  const prisma = new PrismaClient()
  await prisma.$connect()

  fastify.decorate('prisma', prisma)

  fastify.addHook('onClose', async (instance) => {
    await instance.prisma.$disconnect()
  })
}

export default fp(databasePlugin, { name: 'database' })
```

### 5. Hooks

**Hook Types (in order):**
- onRequest
- preParsing
- preValidation
- preHandler
- preSerialization
- onSend
- onResponse

```typescript
export async function authHook(
  request: FastifyRequest,
  reply: FastifyReply
) {
  const authHeader = request.headers.authorization

  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    throw new UnauthorizedError('No token provided')
  }

  const token = authHeader.substring(7)
  const payload = await verifyToken(token)
  request.user = payload
}
```

### 6. Error Handling

```typescript
export class AppError extends Error {
  constructor(
    public message: string,
    public statusCode: number = 500,
    public code?: string
  ) {
    super(message)
    this.name = this.constructor.name
  }
}

app.setErrorHandler((error: FastifyError, request, reply) => {
  request.log.error(error)

  const statusCode = error.statusCode || 500
  const message = error.message || 'Internal Server Error'

  reply.status(statusCode).send({
    success: false,
    error: {
      message,
      statusCode,
      code: error.code,
    },
  })
})
```

### 7. Authentication

```typescript
import fp from 'fastify-plugin'
import jwt from '@fastify/jwt'

export default fp(async (fastify) => {
  await fastify.register(jwt, {
    secret: env.JWT_SECRET,
  })
})

// Usage in routes
fastify.get('/protected', {
  onRequest: async (request, reply) => {
    try {
      await request.jwtVerify()
    } catch (err) {
      reply.send(err)
    }
  }
}, async (request) => {
  return { message: 'Protected data' }
})
```

## Quick Start

1. **Install Fastify:**
   ```bash
   npm install fastify
   npm install -D typescript @types/node tsx
   ```

2. **Create basic server:**
   ```typescript
   import Fastify from 'fastify'
   const app = Fastify()
   app.get('/', async (request, reply) => {
     return { hello: 'world' }
   })
   app.listen({ port: 3000 })
   ```

3. **Add plugins:**
   ```bash
   npm install @fastify/cors @fastify/helmet @fastify/jwt
   ```

4. **Start server:**
   ```bash
   npx tsx src/server.ts
   ```

## Production Checklist

- [ ] Use TypeScript for type safety
- [ ] Implement JSON Schema validation
- [ ] Use Fastify hooks for cross-cutting concerns
- [ ] Implement proper error handling
- [ ] Add authentication and authorization
- [ ] Use plugins for reusable functionality
- [ ] Implement request ID tracking
- [ ] Use async/await for async operations
- [ ] Test endpoints with Jest
- [ ] Use Swagger documentation
- [ ] Add CORS configuration
- [ ] Implement rate limiting
- [ ] Use caching for performance
- [ ] Monitor performance and errors
- [ ] Optimize serialization
- [ ] Use connection pooling

## Anti-patterns

1. **Poor JSON Schema Design**: Don't design JSON schemas incorrectly
2. **Missing Error Handling**: Don't fail to handle errors appropriately
3. **No Request ID**: Don't fail to track request IDs
4. **Poor Performance**: Don't fail to optimize performance
5. **No Monitoring**: Don't fail to monitor performance
6. **Poor TypeScript**: Don't use TypeScript incorrectly

## Integration Points

- **Error Handling**: `03-backend-api/error-handling`
- **Validation**: `03-backend-api/validation`
- **Middleware**: `03-backend-api/middleware`
- **API Design**: `01-foundations/api-design`
- **Monitoring**: `14-monitoring-observability`

## Further Reading

- [Fastify Documentation](https://www.fastify.io/docs/latest/)
- [Fastify Ecosystem](https://www.fastify.io/ecosystem/)
- [Fastify Best Practices](https://github.com/fastify/fastify/blob/main/docs/Guides/Best-Practices.md)

Overview

This skill captures practical patterns for building high-performance REST APIs with Fastify. It focuses on schema-driven validation, fast JSON serialization, TypeScript support, and an extensible plugin/hook model to create production-ready services quickly. The content highlights performance wins, developer ergonomics, and concrete patterns for routing, error handling, and authentication.

How this skill works

The skill outlines standard building blocks: app bootstrap, route registration, JSON Schema for request/response validation, plugins for shared resources, hooks for lifecycle concerns, and a centralized error handler. Examples show how to register plugins (database, jwt), attach hooks (auth, request lifecycle), and return precise responses using typed schemas. It also provides a production checklist and common anti-patterns to avoid.

When to use it

  • Building a new Node.js REST API where performance and low overhead matter
  • When you want automatic request/response validation and clear runtime serialization
  • Migrating from Express to a faster framework while keeping familiar routing patterns
  • When using TypeScript and you need first-class type-safety across routes
  • When you need an extensible plugin system for shared resources like DB or auth

Best practices

  • Use JSON Schema for both request validation and response serialization to prevent runtime errors
  • Register reusable functionality as plugins and clean up resources in onClose hooks
  • Apply hooks (onRequest, preValidation, preHandler) for authentication, logging, and request IDs
  • Centralize error handling with a consistent error shape and proper status codes
  • Benchmark endpoints and optimize serialization and caching for high-throughput paths

Example use cases

  • User service with typed create/read endpoints using JSON Schema and TypeScript
  • Microservice that registers a Prisma database plugin and disconnects on shutdown
  • Authenticated routes that verify JWT in an onRequest hook and populate request.user
  • High-throughput public API where fast JSON serialization and response schemas matter
  • Internal admin API with centralized error handler and request ID tracking for observability

FAQ

Do I have to write JSON Schema for every route?

You should define schemas for inputs and important responses; at minimum validate incoming bodies and critical responses to prevent invalid data and optimize serialization.

How do plugins share resources like a DB client?

Create a fastify plugin that decorates the instance with the client, connect on register, and disconnect in an onClose hook to ensure clean lifecycle management.