@fastify/swagger

repository·main·Indexed 22 days ago

https://github.com/fastify/fastify-swagger

A Fastify plugin that serves Swagger (OpenAPI v2) or OpenAPI v3 documentation. It supports dynamic generation of schemas from Fastify route schemas or serving existing static schema files. Version 9.8.1 focuses on generating valid specifications; the Swagger UI frontend is provided by the separate @fastify/swagger-ui plugin.

Tokens
4.9K
Snippets
14
Records
17
Agent score
78%

What's inside @fastify/swagger

  1. Understand registration modes: Dynamic vs Static

    main

    When registering @fastify/swagger, you can choose between two modes:

    1. Dynamic (Default): Automatically generates API schemas from your Fastify route schemas. You can configure the output to be either Swagger (OpenAPI v2) using the swagger option or OpenAPI v3 using the openapi option.
    2. Static: Serves an existing Swagger or OpenAPI schema file. You must provide the file path via specification.path.

    Use Dynamic mode for most use cases where you want your documentation to stay in sync with your code automatically. Use Static mode if you already have a standalone specification file.

    // Dynamic mode (default)
    await fastify.register(require('@fastify/swagger'), {
      swagger: { info: { title: 'My API', version: '1.0.0' } }
    });
    
    // Static mode
    await fastify.register(require('@fastify/swagger'), {
      mode: 'static',
      specification: {
        path: './path/to/spec.yaml',
        baseDir: '/absolute/path/to/specs'
      }
    });
  2. How to work with $refs in dynamic mode

    main
    When using the /docs/json endpoint in dynamic mode, the generated swagger.json file will have all references ($ref) resolved into a single file. This means you do not need to manually manage external reference files if you are consuming the output from this specific endpoint in dynamic mode.
  3. Migrating from @fastify/swagger version 7 to 8

    main

    In version 8, @fastify/swagger was refactored to focus exclusively on generating valid swagger/openapi-specifications. The responsibility for serving the Swagger UI frontend was moved to a new plugin: @fastify/swagger-ui.

    Key Changes:

    • Plugin Split: You must now register both @fastify/swagger (for the spec) and @fastify/swagger-ui (for the UI).
    • Option Relocation: All configuration options previously passed to @fastify/swagger that related to the Swagger UI frontend must now be passed to @fastify/swagger-ui.
    • Removed Option: The exposeRoute option has been removed.
    • Static Mode Requirement: If using mode: 'static' with external schema files referenced via $ref, you must ensure the baseDir option in @fastify/swagger-ui matches the specification.baseDir option in @fastify/swagger.
    import Fastify from 'fastify'
    import fastifySwagger from '@fastify/swagger'
    import fastifySwaggerUi from '@fastify/swagger-ui'
    
    const fastify = new Fastify()
    
    // 1. Register the spec generator
    await fastify.register(fastifySwagger, {
      mode: 'dynamic',
      openapi: { /* ... */ }
    })
    
    // 2. Register the UI server
    await fastify.register(fastifySwaggerUi, {
      routePrefix: '/documentation'
    })
  4. Register @fastify/swagger with @fastify/autoload

    main

    When using @fastify/autoload to load routes, ensure you register @fastify/swagger manually before calling fastify.register for the autoload plugin. This guarantees that the plugin is initialized before the routes in your directory are loaded.

    const fastify = require('fastify')()
    const path = require('path') // Note: path must be required
    
    await fastify.register(require('@fastify/swagger'))
    
    fastify.register(require("@fastify/autoload"), {
      dir: path.join(__dirname, 'routes')
    })
    
    await fastify.ready()
    fastify.swagger()
  5. Configure response descriptions and content types

    main

    Response Descriptions

    To provide a description for a response, use the description field in the schema.

    • If you want different descriptions for the response itself and the response body, use x-response-description for the response-level description.

    Content Types (OpenAPI v3 only)

    To support multiple content types (e.g., application/json and application/vnd.v1+json), use the content key in the response schema.

    Empty Body Responses

    For responses with no body (like 204 No Content), set type: 'null' to prevent Fastify schema compilation errors.

    // Different descriptions for response and body
    fastify.get('/responseDescription', {
      schema: {
        response: {
          200: {
            'x-response-description': 'response description',
            description: 'schema description',
            type: 'string'
          }
        }
      }
    }, handler)
    
    // Multiple content types (OpenAPI v3)
    fastify.get('/multi-content', {
      schema: {
        response: {
          200: {
            content: {
              'application/json': { schema: { type: 'object' } },
              'application/xml': { schema: { type: 'object' } }
            }
          }
        }
      }
    }, handler)
    
    // Empty body (204)
    fastify.get('/empty', {
      schema: {
        response: {
          204: {
            type: 'null',
            description: 'No Content'
          }
        }
      }
    }, handler)
  6. Add examples to schemas

    main

    You can add examples to your schemas to improve documentation clarity.

    1. JSON Schema style: Use an examples array. When generating OpenAPI v3, these are automatically converted into named examples objects.
    2. OpenAPI style: Use the x-examples field (requires adding the keyword to your Fastify AJV instance) to provide named examples with summaries and descriptions.
    // JSON Schema style (converts to OpenAPI examples)
    fastify.route({
      method: 'POST',
      url: '/',
      schema: {
        body: {
          type: 'object',
          properties: {
            foo: { type: 'string' }
          },
          examples: [{ foo: 'bar' }]
        }
      }
    })
    
    // OpenAPI style using x-examples
    // Note: Requires ajv.addKeyword({ keyword: 'x-examples' })
    fastify.route({
      method: 'POST',
      url: '/feed',
      schema: {
        body: {
          type: 'object',
          'x-examples': {
            Cats: {
              summary: 'Feed cats',
              value: { animals: ['Tom'] }
            }
          }
        }
      }
    })
  7. Register @fastify/swagger as a Fastify plugin

    main

    To enable Swagger/OpenAPI generation in your Fastify application, register the @fastify/swagger plugin. The plugin supports two modes: dynamic (default) and static.

    • dynamic mode: Generates the OpenAPI documentation dynamically based on the current state of your routes.
    • static mode: Generates the documentation based on a static configuration.

    Ensure you are using Fastify version 5.x for compatibility.

    const fastify = require('fastify')()
    const fastifySwagger = require('@fastify/swagger')
    
    fastify.register(fastifySwagger, {
      mode: 'dynamic' // or 'static'
    })
  8. Configure OpenAPI v3 with @fastify/swagger

    main

    To use OpenAPI v3, register @fastify/swagger and provide an openapi configuration object. This object allows you to define global metadata like info, servers, tags, components (for security schemes), and externalDocs.

    Important: You must register @fastify/swagger before any routes are defined to ensure the plugin can discover and document them. After the application is ready, call fastify.swagger() to generate the documentation.

    const fastify = require('fastify')()
    
    await fastify.register(require('@fastify/swagger'), {
      openapi: {
        openapi: '3.0.0',
        info: {
          title: 'Test swagger',
          description: 'Testing the Fastify swagger API',
          version: '0.1.0'
        },
        servers: [
          {
            url: 'http://localhost:3000',
            description: 'Development server'
          }
        ],
        tags: [
          { name: 'user', description: 'User related end-points' },
          { name: 'code', description: 'Code related end-points' }
        ],
        components: {
          securitySchemes: {
            apiKey: {
              type: 'apiKey',
              name: 'apiKey',
              in: 'header'
            }
          }
        },
        externalDocs: {
          url: 'https://swagger.io',
          description: 'Find more info here'
        }
      }
    })
    
    fastify.put('/some-route/:id', {
      schema: {
        description: 'post some data',
        tags: ['user', 'code'],
        summary: 'qwerty',
        security: [{ apiKey: [] }],
        params: {
          type: 'object',
          properties: {
            id: {
              type: 'string',
              description: 'user id'
            }
          }
        },
        body: {
          type: 'object',
          properties: {
            hello: { type: 'string' },
            obj: {
              type: 'object',
              properties: {
                some: { type: 'string' }
              }
            }
          }
        },
        response: {
          201: {
            description: 'Successful response',
            type: 'object',
            properties: {
              hello: { type: 'string' }
            }
          },
          default: {
            description: 'Default response',
            type: 'object',
            properties: {
              foo: { type: 'string' }
            }
          }
        }
      }
    }, (req, reply) => { }})
    
    await fastify.ready()
    fastify.swagger()
  9. Create multiple Swagger documents using the decorator option

    main

    By default, @fastify/swagger decorates your Fastify instance with fastify.swagger(). You can create multiple independent documentation sets by providing a unique string to the decorator option during registration. This allows you to have, for example, an 'internal' and an 'external' API documentation side-by-side.

    // Register internal docs
    await fastify.register(require('@fastify/swagger'), {
      decorator: 'internalSwagger',
      // ... config
    })
    
    // Register external docs
    await fastify.register(require('@fastify/swagger'), {
      decorator: 'externalSwagger',
      // ... config
    })
    
    // Access them via the custom names
    fastify.internalSwagger()
    fastify.externalSwagger()
  10. Transform route schemas and URLs in dynamic mode

    main

    In dynamic mode, you can use the transform option to provide a synchronous function that modifies a route's URL and schema before the documentation is generated. This is useful for converting non-standard schemas (like Joi) to JSON schema, altering URLs, or hiding routes based on logic.

    If an endpoint has a local config.swaggerTransform function, it will override the global transform function.

    await fastify.register(require('@fastify/swagger'), {
      transform: ({ schema, url, route, swaggerObject }) => {
        // Example: Hide internal routes
        if (url.startsWith('/internal')) {
          schema.hide = true
        }
        return { schema, url }
      }
    })
    
    // Or locally on a specific route
    fastify.get('/local', {
      config: {
        swaggerTransform: ({ schema, url }) => {
          return { schema, url }
        }
      }
    }, handler)
  11. Modify the final Swagger/OpenAPI object with transformObject

    main

    Use the transformObject option to pass a synchronous function that modifies the entire swaggerObject or openapiObject after all routes have been processed but before it is rendered.

    await fastify.register(require('@fastify/swagger'), {
      swagger: { info: { title: 'Original Title' } },
      transformObject ({ swaggerObject }) => {
        swaggerObject.info.title = 'Transformed Title';
        return swaggerObject;
      }
    })