hono-openapi

repository·main·Indexed 21 days ago

https://github.com/rhinobase/hono-openapi

An OpenAPI schema generator for Hono that automates the creation of OpenAPI 3.1.0 specifications by extracting metadata from validation schemas. It supports any validation library implementing the Standard Schema specification and provides middlewares like describeRoute, describeResponse, and validator to define API metadata. The library includes tools such as openAPIRouteHandler for serving specs as JSON endpoints and generateSpecs for programmatic generation.

Tokens
3.6K
Snippets
10
Records
17
Agent score
23%

What's inside hono-openapi

  1. Overview of Hono OpenAPI

    main

    Hono OpenAPI is a library designed to automatically generate OpenAPI specifications for Hono applications. It leverages your existing validation schemas to produce the specification, which can then be used to generate client libraries, interactive documentation, and other developer tools.

    Key features:

    • Automatic Specification Generation: Derives OpenAPI details from your Hono routes and validation schemas.
    • Standard Schema Support: Compatible with any validation library that implements the Standard Schema specification.
  2. Handling Zod v4 and ArkType edge cases

    main

    The library includes built-in logic to handle specific schema provider quirks during OpenAPI generation:

    Zod v4 Date Handling

    Zod v4's toJSONSchema throws an error when encountering z.date() because Date cannot be natively represented in JSON Schema. hono-openapi automatically injects:

    • unrepresentable: "any": Prevents the error by producing {} for unrepresentable types.
    • override: A custom function that converts z.date() into { type: "string", format: "date-time" } during the emit phase.

    ArkType Morph Fallback

    For ArkType schemas using morphs (e.g., string.numeric.parse), the library injects a fallback option. This ensures that morphs produce valid JSON Schema instead of throwing a ToJsonSchemaError by returning the base schema.

  3. Use dynamic resolvers in OpenAPI schemas and responses

    main

    The library extends standard OpenAPI types (MediaTypeObject, ResponseObject, and Document) to support ResolverReturnType. This allows you to use the resolver() function output directly in places where a standard JSON schema would normally go.

    Where resolvers can be used:

    1. Media Type Schemas: Inside content[mediaType].schema.
    2. Response Objects: Inside responses[statusCode].content[mediaType].schema.
    3. Global Components: Inside components.responses.

    Resolver Return Type

    A resolver returns an object that can include standard schema properties plus an optional options object:

    • options.media: Overrides the media type of the request body. If not specified, it defaults to application/json for json targets and multipart/form-data for form targets.
    • options: Can include additional ToOpenAPISchemaContext properties.
    // Conceptual example of a resolver-based response
    const response: ResponseObjectWithResolver = {
      description: 'Dynamic response',
      content: {
        'application/json': {
          schema: {
            // This represents the output of a resolver()
            type: 'object',
            properties: { id: { type: 'string' } },
            options: { media: 'application/json' }
          }
        }
      }
    };
  4. How schema resolvers work

    main

    The resolver function is an internal utility used by the middleware to bridge StandardSchemaV1 schemas with OpenAPI/JSON Schema generation. It produces an object containing:

    • vendor: The schema provider name (e.g., zod, arktype).
    • validate: The original validation function.
    • toJSONSchema: A function to convert the schema to JSON Schema.
    • toOpenAPISchema: A function to convert the schema to an OpenAPI-compatible schema.

    When using validator() or describeResponse(), the library uses these resolvers to ensure that even complex schemas (like Zod dates or ArkType morphs) are correctly translated into the OpenAPI specification.

  5. Configure OpenAPI generation options

    main

    When using openAPIRouteHandler or generateSpecs, you can pass a GenerateSpecOptions object to control the output.

    Key configuration properties include:

    • documentation: An object of type OpenAPIV3_1.Document used to provide base information like info, tags, and components (e.g., shared schemas).
    • exclude: A list of paths to exclude from the specification.
    • excludeMethods: An array of HTTP methods to skip (defaults to ['OPTIONS']).
    • excludeTags: An array of tag names to filter out from the final specification.
    • includeEmptyPaths: A boolean to determine if paths without OpenAPI metadata should be included.
    • defaultOptions: A map of method names (e.g., get, post) to DescribeRouteOptions to apply default metadata to all routes of that method.
  6. Configure OpenAPI specification generation with GenerateSpecOptions

    main

    When generating an OpenAPI specification, use the GenerateSpecOptions object to control which routes are included, which methods are excluded, and to provide the base documentation.

    Key configuration properties:

    • documentation: An extended OpenAPI document object (of type DocumentWithResolver) containing your base metadata and components.
    • includeEmptyPaths: If true, includes paths that do not have associated handlers (useful for documenting unimplemented APIs).
    • excludeStaticFile: Determines if static files (paths ending in a period) should be excluded. Defaults to true.
    • exclude: A string, RegExp, or array of strings/RegExps to exclude specific paths.
    • excludeMethods: An array of AllowedMethods to exclude from the spec.
    • excludeTags: An array of tags to exclude.
    • defaultOptions: Default settings applied to all calls to describeRoute.
    const options: GenerateSpecOptions = {
      documentation: {
        info: { title: 'My API', version: '1.0.0' },
        components: {
          responses: {
            'Unauthorized': { description: 'Invalid credentials' }
          }
        }
      },
      exclude: [/\/internal\/.*/, '/health'],
      excludeMethods: ['OPTIONS'],
      includeEmptyPaths: true
    };
  7. Describe a route with `describeRoute()`

    main

    Use describeRoute to attach OpenAPI specification metadata to a Hono route. This is typically used to define general route information like summaries, descriptions, or tags that aren't tied to specific validation schemas.

    import { describeRoute } from "hono-openapi";
    
    app.get("/hello", describeRoute({ 
      summary: "Get greeting",
      description: "Returns a hello world message"
    }), (c) => {
      return c.text("Hello!");
    });
  8. Register custom paths with RegisterSchemaPathOptions

    main

    If you need to manually register paths that are not automatically discovered via Hono handlers, use RegisterSchemaPathOptions.

    Properties:

    • route: The RouterRoute being registered.
    • specs: An optional DescribeRouteOptions object (can include a custom operationId).
    • paths: A partial OpenAPIV3_1.PathsObject defining the path structure.
  9. Describe response schemas with `describeResponse()`

    main

    The describeResponse function wraps a Hono handler to provide detailed OpenAPI documentation for its responses. It allows you to map HTTP status codes to specific schemas using the vSchema key within the media type object.

    To use it, pass your handler as the first argument, followed by a responses object where keys are status codes and values define the response structure. Use vSchema to provide the validation schema that should be used for the OpenAPI documentation.

    import { describeResponse } from "hono-openapi";
    import { z } from "zod";
    
    const UserSchema = z.object({ id: z.string(), name: z.string() });
    
    const handler = async (c: Context) => {
      return c.json({ id: "123", name: "Alice" }, 200);
    };
    
    // Wrap the handler to add OpenAPI documentation
    app.get("/user", describeResponse(
      handler,
      {
        200: {
          content: {
            "application/json": { 
              vSchema: UserSchema 
            }
          }
        },
        404: { description: "User not found" }
      }
    ));
  10. Handle OpenAPI routes with openAPIRouteHandler

    main

    Use openAPIRouteHandler to create a Hono route handler that serves your OpenAPI documentation (e.g., at /openapi.json).

    import { Hono } from 'hono';
    import { openAPIRouteHandler } from 'hono-openapi';
    
    const app = new Hono();
    
    app.get('/openapi.json', openAPIRouteHandler(app));
  11. Manually generate OpenAPI specs with generateSpecs

    main

    The generateSpecs function allows you to programmatically generate an OpenAPI 3.1.0 document from a Hono instance. This is useful if you need to perform additional processing on the spec or serve it through a custom mechanism outside of the standard openAPIRouteHandler.

    It merges route-level metadata, documentation components, and global options into a single OpenAPIV3_1.Document object.

    import { Hono } from 'hono';
    import { generateSpecs } from 'hono-openapi';
    
    const app = new Hono();
    
    // Generate the spec object manually
    const spec = await generateSpecs(app, {
      excludeTags: ['internal'],
      documentation: {
        info: {
          title: 'My API',
          version: '1.0.0'
        }
      }
    });
  12. Generate OpenAPI specifications with generateSpecs

    main

    Use generateSpecs to produce an OpenAPI specification object from your Hono application routes. This is typically used to export the documentation for your API.

    import { generateSpecs } from 'hono-openapi';
    
    const specs = generateSpecs(app);