npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@a_jackie_z/fastify

v1.4.2

Published

A collection of Fastify plugins and utilities for building robust web applications.

Downloads

1,380

Readme

@a_jackie_z/fastify

A collection of Fastify plugins and utilities for building robust web applications with built-in support for dynamic JWT authentication, rate limiting, Swagger documentation, and Zod validation.

Installation

# For server-side Fastify applications
npm install @a_jackie_z/fastify

# For frontend applications (types and schemas only)
npm install @a_jackie_z/fastify-types

Note: If you're building a frontend application (React, Vue, etc.) and only need API types and response schemas, install @a_jackie_z/fastify-types instead. It's frontend-compatible and has no Node.js dependencies.

Quick Start

Basic Setup

import { createFastify, runFastify } from '@a_jackie_z/fastify'
import { formatSuccess } from '@a_jackie_z/fastify-types'
import { z } from 'zod'

const app = await createFastify()

app.route({
  method: 'GET',
  url: '/hello',
  schema: {
    response: {
      200: z.object({
        status: z.number(),
        success: z.boolean(),
        data: z.object({
          message: z.string(),
        }),
      }),
    },
  },
  handler: async (_request, reply) => {
    return reply.send(formatSuccess(200, { message: 'Hello World!' }))
  },
})

await runFastify(app, '0.0.0.0', 3000)

Package Architecture

This package is split into two complementary packages for better frontend compatibility:

@a_jackie_z/fastify (Server Package)

For: Node.js server applications Includes:

  • Fastify server creation and configuration (createFastify, runFastify)
  • JWT service with token generation/verification (FastifyJwtService)
  • Server-side crypto utilities (generateId, generateSessionToken)
  • Fastify plugins and middleware
  • Server-specific types (CreateFastifyOptions, FastifyServer)

@a_jackie_z/fastify-types (Types Package)

For: Frontend applications (React, Vue, etc.) and API contracts Includes:

  • Response types (SuccessResponse, ErrorResponse, ValidationDetail)
  • Response formatters (formatSuccess, formatError)
  • Zod schemas (successResponseSchema, errorResponseSchema)
  • JWT configuration types (TokenTypeConfig, SignTokenOptions)
  • Error utilities (createError, HTTP_STATUS_CODES)
  • No Node.js dependencies - browser compatible
// Server code
import { createFastify, generateId } from '@a_jackie_z/fastify'
import { formatSuccess } from '@a_jackie_z/fastify-types'

// Frontend code  
import { successResponseSchema, type SuccessResponse } from '@a_jackie_z/fastify-types'

Features

  • Dynamic JWT Authentication - Flexible multi-token-type JWT system with per-type configuration
  • Multiple Secret Keys - Support for key rotation with kid-based secret selection
  • Per-Type Token Configuration - Configure header names, expiration, algorithm, issuer per token type
  • Issuer Validation - Optional allowedIss validation for enhanced security
  • Payload Schema Validation - Zod schema validation for token payloads
  • Flexible Route Protection - Combine global required types with route-specific types
  • Rate Limiting - Global rate limiting for API protection
  • Swagger Documentation - Auto-generated API documentation with dynamic security schemes
  • Zod Integration - Type-safe request/response validation
  • Standardized Response Formatting - Consistent response shapes with formatSuccess and formatError utilities
  • Automatic Error Handling - Field-level validation errors, auth errors, and rate limit errors with consistent formatting
  • Custom Logger Support - Integrate any Fastify-compatible logger
  • Health Check Plugin - Ready-to-use health check endpoint
  • Plugin Helper - Utility for creating reusable Fastify plugins

Examples

1. Complete Setup with All Features

import { createFastify, runFastify } from '@a_jackie_z/fastify'
import { z } from 'zod'

const app = await createFastify({
  // Optional: Integrate your logger
  // logger: yourFastifyLogger,
  
  // Rate limiting
  rateLimit: {
    global: {
      max: 100,
      timeWindow: '1 minute',
    },
  },
  
  // Dynamic JWT authentication with multiple token types
  jwt: {
    // Multiple secrets for key rotation
    secrets: {
      v1: 'your-secret-key-v1',
      v2: 'your-secret-key-v2',
    },
    defaultKeyId: 'v2', // Use v2 for signing new tokens
    
    // Define token types with individual configurations
    tokenTypes: {
      access: {
        headerName: 'authorization',
        expiresIn: '15m',
        algorithm: 'HS256',
        iss: 'auth-service',
        allowedIss: ['auth-service'],
        payloadSchema: z.object({
          identityId: z.string(),
          role: z.string().optional(),
        }),
      },
      refresh: {
        headerName: 'authorization',
        expiresIn: '7d',
        algorithm: 'HS256',
      },
      service: {
        headerName: 'x-service-authorization',
        expiresIn: '1h',
        algorithm: 'HS256',
        iss: 'gateway',
        allowedIss: ['gateway', 'internal-service'],
      },
    },
    
    // Token types always checked (unless route has jwtTypes: false)
    requiredTypes: ['service'],
    
    debug: true, // Enable detailed error messages in development
  },
  
  // Swagger documentation
  swagger: {
    title: 'My API',
    version: '1.0.0',
    description: 'API documentation',
    routePrefix: '/docs',
  },
})

await runFastify(app, '0.0.0.0', 3000)

JWT Authentication

Overview

The JWT system supports dynamic token types where each type can have its own:

  • Header name (e.g., authorization, x-service-authorization)
  • Expiration time
  • JWT algorithm, issuer (iss), and audience (aud)
  • Allowed issuers for verification
  • Zod schema for payload validation

Token Type Configuration

Each token type is configured independently:

// TokenTypeConfig is available in @a_jackie_z/fastify-types
import type { TokenTypeConfig } from '@a_jackie_z/fastify-types'

interface TokenTypeConfig {
  headerName: string          // Header to extract token from
  expiresIn: string          // Token expiration (e.g., '15m', '7d', '1h')
  payloadSchema?: z.ZodSchema // Optional Zod schema for payload validation
  algorithm?: Algorithm       // JWT signing algorithm (default: 'HS256')
  iss?: string               // Token issuer
  aud?: string               // Token audience
  allowedIss?: string[]      // Allowed issuers for verification (bypass if undefined)
  header?: {                 // Optional JWT header configuration
    typ?: string             // Token type (typically 'JWT')
    cty?: string             // Content type (e.g., 'application/json')
    [key: string]: any       // Custom header claims
  }
}

Multiple Secret Keys (Key Rotation)

Support multiple secrets for key rotation using kid-based selection:

jwt: {
  secrets: {
    v1: 'old-secret-key',
    v2: 'new-secret-key',
    v3: 'future-secret-key',
  },
  defaultKeyId: 'v2', // New tokens signed with v2
  // Tokens with kid=v1, v2, or v3 can all be verified
}

Route Protection

Routes can specify which token types they require:

// Public route - no authentication
app.get('/public', {
  config: { jwtTypes: false }
}, handler)

// Protected by access token only
app.get('/user-data', {
  config: { jwtTypes: ['access'] }
}, handler)

// Protected by both access and service tokens
app.get('/gateway-protected', {
  config: { jwtTypes: ['access', 'service'] }
}, handler)

// No config - uses requiredTypes from global config
app.get('/auto-protected', handler)

Global Required Types

The requiredTypes option specifies token types that are ALWAYS checked on all routes (unless explicitly bypassed with jwtTypes: false):

jwt: {
  requiredTypes: ['service'], // Always verify service token
  tokenTypes: { /* ... */ }
}

// Route A: No jwtTypes specified
// Verifies: ['service']
app.get('/api/data', handler)

// Route B: jwtTypes: ['access']
// Verifies: ['service', 'access'] (both combined!)
app.get('/api/user', { config: { jwtTypes: ['access'] } }, handler)

// Route C: jwtTypes: false
// Verifies: nothing (explicitly public)
app.get('/public', { config: { jwtTypes: false } }, handler)

2. Authentication Flow - Login Route

import { formatSuccess, formatError } from '@a_jackie_z/fastify-types'

app.route({
  method: 'POST',
  url: '/auth/login',
  config: {
    jwtTypes: false, // Public route
  },
  schema: {
    body: z.object({
      username: z.string(),
      password: z.string(),
    }),
    response: {
      200: z.object({
        status: z.number(),
        success: z.boolean(),
        data: z.object({
          accessToken: z.string(),
          refreshToken: z.string(),
        }),
      }),
      401: z.object({
        status: z.number(),
        success: z.boolean(),
        error: z.string(),
        message: z.string(),
      }),
    },
  },
  handler: async (request, reply) => {
    const { username, password } = request.body

    // Validate credentials
    if (username === 'admin' && password === 'secret') {
      // Generate tokens using jwtService
      const accessToken = app.jwtService.generateToken('access', {
        identityId: 'user-123',
        role: 'admin',
      })
      
      const refreshToken = app.jwtService.generateToken('refresh', {
        identityId: 'user-123',
      })
      
      return reply.send(formatSuccess(200, { accessToken, refreshToken }))
    }

    return reply.status(401).send(
      formatError(401, 'Unauthorized', 'Invalid credentials')
    )
  },
})

3. Protected Route - Accessing JWT Payloads

app.route({
  method: 'GET',
  url: '/user/profile',
  config: {
    jwtTypes: ['access'], // Requires access token (+ any global requiredTypes)
  },
  schema: {
    response: {
      200: z.object({
        status: z.number(),
        success: z.boolean(),
        data: z.object({
          identityId: z.string(),
          role: z.string(),
        }),
      }),
    },
  },
  handler: async (request, reply) => {
    // Access verified token payloads from Map
    const accessPayload = request.jwtPayloads.get('access')
    const servicePayload = request.jwtPayloads.get('service') // If in requiredTypes
    
    return reply.send(formatSuccess(200, {
      identityId: accessPayload.identityId,
      role: accessPayload.role,
    }))
  },
})

4. Service-to-Service Authentication

Configure service tokens for microservice communication:

jwt: {
  tokenTypes: {
    service: {
      headerName: 'x-service-authorization',
      expiresIn: '1h',
      algorithm: 'HS256',
      iss: 'gateway',
      allowedIss: ['gateway', 'auth-service'],
      payloadSchema: z.object({
        serviceId: z.string(),
      }),
    },
  },
  requiredTypes: ['service'], // All routes require service token
}

// Gateway generates service token
const serviceToken = app.jwtService.generateToken('service', {
  serviceId: 'gateway',
})

// Forward to backend service with service token
fetch('http://backend-service/api/data', {
  headers: {
    'Authorization': `Bearer ${userAccessToken}`,
    'X-Service-Authorization': serviceToken, // No Bearer prefix for custom headers
  },
})

5. Token Refresh Flow

app.route({
  method: 'POST',
  url: '/auth/refresh',
  config: {
    jwtTypes: false, // Public endpoint
  },
  schema: {
    body: z.object({
      refreshToken: z.string(),
    }),
  },
  handler: async (request, reply) => {
    const { refreshToken } = request.body
    
    try {
      // Verify refresh token
      const payload = app.jwtService.verifyToken('refresh', refreshToken)
      
      // Generate new tokens
      const newAccessToken = app.jwtService.generateToken('access', {
        identityId: payload.identityId,
      })
      
      const newRefreshToken = app.jwtService.generateToken('refresh', {
        identityId: payload.identityId,
      })
      
      return reply.send(formatSuccess(200, {
        accessToken: newAccessToken,
        refreshToken: newRefreshToken,
      }))
    } catch (err) {
      return reply.status(401).send(
        formatError(401, 'Unauthorized', 'Invalid or expired refresh token')
      )
    }
  },
})

6. Custom Authorization Logic

Implement custom authorization in route handlers:

app.route({
  method: 'DELETE',
  url: '/admin/users/:id',
  config: {
    jwtTypes: ['access'],
  },
  handler: async (request, reply) => {
    const accessPayload = request.jwtPayloads.get('access')
    
    // Check role
    if (accessPayload.role !== 'admin') {
      return reply.status(403).send(
        formatError(403, 'Forbidden', 'Admin role required')
      )
    }
    
    // Check permissions
    if (!accessPayload.permissions?.includes('delete')) {
      return reply.status(403).send(
        formatError(403, 'Forbidden', 'Delete permission required')
      )
    }
    
    // Proceed with deletion
    const { id } = request.params
    await deleteUser(id)
    
    return reply.send(formatSuccess(200, { message: 'User deleted' }))
  },
})

7. Issuer Validation

Enforce issuer validation for enhanced security:

jwt: {
  tokenTypes: {
    access: {
      headerName: 'authorization',
      expiresIn: '15m',
      iss: 'auth-service',              // Tokens generated with iss='auth-service'
      allowedIss: ['auth-service'],     // Only accept tokens from auth-service
    },
    service: {
      headerName: 'x-service-authorization',
      expiresIn: '1h',
      iss: 'gateway',
      allowedIss: ['gateway', 'legacy-gateway'], // Accept from multiple sources
    },
  },
}

// If allowedIss is undefined, issuer validation is bypassed

8. Payload Schema Validation

Define Zod schemas for runtime payload validation:

import { z } from 'zod'

jwt: {
  tokenTypes: {
    access: {
      headerName: 'authorization',
      expiresIn: '15m',
      payloadSchema: z.object({
        identityId: z.string().uuid(),
        role: z.enum(['user', 'admin', 'moderator']),
        permissions: z.array(z.string()).optional(),
        exp: z.number(), // JWT standard field
      }),
    },
  },
}

// Tokens with invalid payloads will be rejected during verification

9. JWT Header Configuration

Configure standard and custom JWT header claims per token type:

import { z } from 'zod'

jwt: {
  tokenTypes: {
    access: {
      headerName: 'authorization',
      expiresIn: '15m',
      algorithm: 'HS256',
      header: {
        typ: 'JWT',                    // Standard: Token type
        cty: 'application/json',       // Standard: Content type
        ver: '2.0',                    // Custom: API version
        ctx: 'web',                    // Custom: Client context
      },
    },
    service: {
      headerName: 'x-service-authorization',
      expiresIn: '1h',
      algorithm: 'HS256',
      header: {
        typ: 'JWT',
        env: 'production',             // Custom: Environment
      },
    },
  },
}

// Generated tokens will include these headers
// Verification will validate headers match the configuration

Standard JWT Headers:

  • typ - Token type (typically 'JWT')
  • cty - Content type for nested JWTs or specific content
  • alg - Algorithm (set via algorithm field)
  • kid - Key ID (automatically set from defaultKeyId)

Custom Headers:

  • Any additional key-value pairs for application-specific needs
  • Examples: ver (version), ctx (context), env (environment), app (application ID)

Header Validation:

  • During token verification, headers are validated against the configured values
  • Tokens with mismatched headers will be rejected
  • Provides additional security layer for token authenticity

10. Health Check Plugin

import { createFastify, runFastify, healthPlugin } from '@a_jackie_z/fastify'

const app = await createFastify()

// Register health check plugin
await app.register(healthPlugin)

// Health endpoint available at: GET /v1/health
// Response: { status: 200, message: 'ok' }

await runFastify(app, '0.0.0.0', 3000)

10. Creating Custom Plugins

import { createFastify, runFastify, createFastifyPlugin } from '@a_jackie_z/fastify'
import { z } from 'zod'

// Create a reusable plugin
const myPlugin = createFastifyPlugin((app) => {
  app.get('/plugin-route', {
    schema: {
      response: {
        200: z.object({
          message: z.string(),
        }),
      },
    },
    handler: async () => {
      return { message: 'From plugin' }
    },
  })
})

// Use the plugin
const app = await createFastify()
await app.register(myPlugin)

await runFastify(app, '0.0.0.0', 3000)

11. Zod Schema Validation

app.route({
  method: 'POST',
  url: '/users',
  schema: {
    body: z.object({
      name: z.string().min(1).max(100),
      email: z.string().email(),
      age: z.number().min(0).max(150).optional(),
    }),
    response: {
      201: z.object({
        id: z.string(),
        name: z.string(),
        email: z.string(),
      }),
      400: z.object({
        error: z.string(),
      }),
    },
  },
  handler: async (request, reply) => {
    const user = request.body
    reply.status(201)
    return {
      id: 'generated-id',
      ...user,
    }
  },
})

12. Response Formatting

All responses should follow a consistent format using the provided utility functions:

Success Response Format

import { formatSuccess } from '@a_jackie_z/fastify-types'

app.route({
  method: 'GET',
  url: '/users/:id',
  schema: {
    params: z.object({
      id: z.string(),
    }),
    response: {
      200: z.object({
        status: z.number(),
        success: z.boolean(),
        data: z.object({
          id: z.string(),
          name: z.string(),
          email: z.string(),
        }),
      }),
    },
  },
  handler: async (request, reply) => {
    const { id } = request.params
    const user = await findUser(id) // Your logic here
    
    return reply.status(200).send(
      formatSuccess(200, user)
    )
    // Returns: { status: 200, success: true, data: { id, name, email } }
  },
})

Error Response Format

import { formatError } from '@a_jackie_z/fastify-types'

app.route({
  method: 'DELETE',
  url: '/users/:id',
  handler: async (request, reply) => {
    const { id } = request.params
    
    const user = await findUser(id)
    if (!user) {
      return reply.status(404).send(
        formatError(404, 'Not Found', `User with ID ${id} not found`)
      )
      // Returns: { status: 404, success: false, error: 'Not Found', message: '...' }
    }
    
    await deleteUser(id)
    return reply.send(formatSuccess(200, { message: 'User deleted' }))
  },
})

Automatic Validation Error Formatting

Validation errors are automatically formatted with field-level details:

app.route({
  method: 'POST',
  url: '/users',
  config: { jwtTypes: false },
  schema: {
    body: z.object({
      username: z.string().min(3, 'Username must be at least 3 characters'),
      email: z.string().email('Invalid email format'),
      age: z.number().int().min(18, 'Must be at least 18 years old'),
    }),
  },
  handler: async (request, reply) => {
    // If validation fails, automatic error response:
    // {
    //   status: 400,
    //   success: false,
    //   error: 'Validation Error',
    //   message: 'Request validation failed',
    //   details: [
    //     { field: 'username', message: 'Username must be at least 3 characters' },
    //     { field: 'email', message: 'Invalid email format' },
    //     { field: 'age', message: 'Must be at least 18 years old' }
    //   ]
    // }
    
    return reply.send(formatSuccess(200, request.body))
  },
})

Automatic Error Handling

All errors follow the standardized format automatically:

  • Validation Errors (400): Field-level details with field name and message
  • Authentication Errors (401): JWT token missing or invalid
  • Authorization Errors (403): Insufficient permissions
  • Not Found Errors (404): Resource not found
  • Rate Limit Errors (429): Too many requests
  • Server Errors (500): Internal server errors

All errors are automatically logged via fastify.log.error with request context.

// Authentication error (automatic)
// GET /protected without JWT token
// { status: 401, success: false, error: 'Unauthorized', message: 'Invalid or missing JWT token' }

// Authorization error (automatic)
// GET /admin without admin role
// { status: 403, success: false, error: 'Forbidden', message: 'Authorization failed' }

// Rate limit error (automatic)
// Too many requests
// { status: 429, success: false, error: 'Too Many Requests', message: 'Rate limit exceeded' }

Configuration Options

CreateFastifyOptions

interface CreateFastifyOptions {
  logger?: FastifyServerOptions['loggerInstance']
  rateLimit?: {
    global?: RateLimitPluginOptions
  }
  jwt?: {
    secrets: Record<string, string>        // Multiple secrets for key rotation
    defaultKeyId: string                   // Default secret key ID for signing
    tokenTypes: Record<string, TokenTypeConfig>  // Token type configurations
    requiredTypes?: string[]               // Token types always checked (unless jwtTypes: false)
    debug?: boolean                        // Enable detailed error messages
  }
  swagger?: {
    title: string
    version: string
    description: string
    routePrefix?: string                   // Default: '/docs/'
  }
}

interface TokenTypeConfig {
  headerName: string          // Header to extract token from
  expiresIn: string          // Token expiration (e.g., '15m', '7d', '1h')
  payloadSchema?: z.ZodSchema // Optional Zod schema for payload validation
  algorithm?: Algorithm       // JWT signing algorithm (default: 'HS256')
  iss?: string               // Token issuer
  aud?: string               // Token audience
  allowedIss?: string[]      // Allowed issuers for verification (bypass if undefined)
  header?: {                 // Optional JWT header configuration
    typ?: string             // Token type (typically 'JWT')
    cty?: string             // Content type
    [key: string]: any       // Custom header claims
  }
}

Route JWT Configuration

Routes configure JWT requirements using jwtTypes:

// Public route - no JWT required
config: { jwtTypes: false }

// Protected route - requires specific token types
config: { jwtTypes: ['access'] }

// Multiple token types required
config: { jwtTypes: ['access', 'service'] }

// No config - uses global requiredTypes (if configured)
// No config specified

API Reference

Server Functions (@a_jackie_z/fastify)

createFastify(options?: CreateFastifyOptions): Promise<FastifyServer>

Creates and configures a Fastify server instance with Zod support and optional plugins.

runFastify(fastify: FastifyServer, host: string, port: number): Promise<void>

Starts the Fastify server. Handles errors and exits the process if the server fails to start.

Parameters:

  • fastify - The Fastify server instance
  • host - The host to bind to (e.g., '0.0.0.0' or 'localhost')
  • port - The port number to listen on

Example:

const app = await createFastify()
await runFastify(app, '0.0.0.0', 3000)

Server-Side Crypto Utilities

import { generateId, generateSessionToken, generateSecureString } from '@a_jackie_z/fastify'

// Generate 16-character alphanumeric ID
const id = generateId() // "A1b2C3d4E5f6G7h8"

// Generate 64-character hex session token  
const token = generateSessionToken() // "abc123...def789"

// Generate custom secure string
const customId = generateSecureString(8, '0123456789') // "42875391"

Response Formatting Functions (@a_jackie_z/fastify-types)

formatSuccess<T>(status: number, data: T): SuccessResponse<T>

Creates a standardized success response.

Parameters:

  • status - HTTP status code (e.g., 200, 201)
  • data - Response data of any type

Returns: SuccessResponse<T>

{
  status: number
  success: true
  data: T
}

Example:

return reply.send(formatSuccess(200, { id: '123', name: 'John' }))
// Returns: { status: 200, success: true, data: { id: '123', name: 'John' } }

formatError(status: number, error: string, message: string, details?: ValidationDetail[]): ErrorResponse

Creates a standardized error response.

Parameters:

  • status - HTTP status code (e.g., 400, 404, 500)
  • error - Error type/name (e.g., 'Not Found', 'Validation Error')
  • message - Human-readable error message
  • details - Optional array of validation error details

Returns: ErrorResponse

{
  status: number
  success: false
  error: string
  message: string
  details?: ValidationDetail[]
}

Example:

return reply.status(404).send(
  formatError(404, 'Not Found', 'User not found')
)
// Returns: { status: 404, success: false, error: 'Not Found', message: 'User not found' }

Response Type Interfaces (@a_jackie_z/fastify-types)

SuccessResponse<T>

interface SuccessResponse<T> {
  status: number
  success: true
  data: T
}

ErrorResponse

interface ErrorResponse {
  status: number
  success: false
  error: string
  message: string
  details?: ValidationDetail[]
}

ValidationDetail

interface ValidationDetail {
  field: string
  message: string
}

Zod Schema Helpers (@a_jackie_z/fastify-types)

For defining response schemas with Zod validation:

successResponseSchema<T>(dataSchema: T): ZodObject

Creates a Zod schema for standardized success responses.

Parameters:

  • dataSchema - Zod schema for the data payload

Returns: Zod object schema matching SuccessResponse<T>

Example:

import { z } from 'zod'
import { successResponseSchema } from '@a_jackie_z/fastify-types'

const userSchema = z.object({
  id: z.string(),
  name: z.string(),
})

const loginResponseSchema = {
  body: z.object({
    username: z.string(),
    password: z.string(),
  }),
  response: {
    200: successResponseSchema(z.object({
      accessToken: z.string(),
      refreshToken: z.string(),
    })),
  },
}

errorResponseSchema: ZodObject

Zod schema for standardized error responses. Matches ErrorResponse interface.

Example:

import { errorResponseSchema } from '@a_jackie_z/fastify-types'

const myRouteSchema = {
  response: {
    200: successResponseSchema(z.object({ success: z.boolean() })),
    400: errorResponseSchema,
    401: errorResponseSchema,
    404: errorResponseSchema,
  },
}

JWT Service API

FastifyJwtService

The FastifyJwtService is automatically decorated on your Fastify instance when JWT is configured. Access it via app.jwtService.

Methods

generateToken(typeName: string, payload: Record<string, any>): string

Generate a JWT token for a specific token type.

const accessToken = app.jwtService.generateToken('access', {
  identityId: 'user-123',
  role: 'admin',
})

const serviceToken = app.jwtService.generateToken('service', {
  serviceId: 'gateway',
})

verifyToken(typeName: string, token: string): any

Verify and decode a JWT token for a specific token type. Returns the payload if valid, throws error if invalid.

try {
  const payload = app.jwtService.verifyToken('access', token)
  console.log(payload.identityId)
} catch (err) {
  console.error('Invalid token:', err.message)
}

extractTokenFromHeader(headerValue: string | string[] | undefined, typeName: string): string | null

Extract a token from a header value for a specific token type.

const token = app.jwtService.extractTokenFromHeader(
  request.headers.authorization,
  'access'
)

getTokenTypeConfig(typeName: string): TokenTypeConfig | undefined

Get the configuration for a specific token type.

const config = app.jwtService.getTokenTypeConfig('access')
console.log(config.expiresIn) // '15m'

getTokenTypeNames(): string[]

Get all configured token type names.

const types = app.jwtService.getTokenTypeNames()
console.log(types) // ['access', 'refresh', 'service']

Testing Your API

  1. Start your server
  2. Access Swagger documentation at http://localhost:3000/docs
  3. Test authentication:
# Login
curl -X POST http://localhost:3000/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"secret"}'

# Use access token
curl http://localhost:3000/user/profile \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

# Service-to-service (with both tokens)
curl http://localhost:3000/api/data \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "X-Service-Authorization: YOUR_SERVICE_TOKEN"

License

MIT