Exegesis OpenAPI Engine

repository·master·Indexed 40 days ago

https://github.com/exegesis-js/exegesis

A framework-agnostic, server-side implementation of the OpenAPI 3.x specification. Exegesis handles parsing, validating, and routing requests based on an OpenAPI definition, providing core engine capabilities and middleware wrappers for Express and Koa. It features full OpenAPI 3.x.x support, response validation, authentication, and a plugin system for extensibility.

Tokens
17.9K
Snippets
42
Records
79
Agent score
50%

What's inside Exegesis

  1. Overview of Exegesis OpenAPI Engine

    master

    Exegesis is a framework-agnostic, server-side implementation of the OpenAPI 3.x specification. It provides the core engine for parsing API definitions and executing logic based on them. While you can use the core library directly, most developers should use its framework-specific wrappers:

    • exegesis-express: Middleware for Express or Connect.
    • exegesis-koa: Middleware for Koa.

    Key features include:

    • Full OpenAPI 3.x.x support.
    • Built-in support for application/json and application/x-www-form-urlencoded.
    • Response validation.
    • Authentication support.
    • Plugin system for extensibility (e.g., CORS, role-based authorization).
    • Support for custom format validation.
  2. How Exegesis compiles an API

    master

    When you call a compilation function (compileApi or compileRunner), Exegesis produces an ApiInterface object.

    1. The ApiInterface (currently implemented by the oas3/OpenApi class) parses the OpenAPI 3.x document.
    2. It maps incoming requests (method, URL, headers) to a resolvedOperation.
    3. A resolvedOperation is a collection of functions responsible for:
      • Parsing and validating request bodies and parameters.
      • Executing the associated controller logic.

    This abstraction allows Exegesis to potentially support other API description standards in the future by simply providing a different ApiInterface implementation.

  3. Understand the Exegesis parameter object structure

    master

    Exegesis expects a parameters object that organizes incoming request data by their location (in property in OpenAPI). The object contains one key for each location: path, query, header, and cookie. Each key maps to an object where the keys are the parameter names and the values are the parsed data.

    Note that path parameters are extracted as raw strings, and header parameters are provided by Node.js. The goal of the Exegesis engine is to transform these raw inputs into the structured format shown below.

    parameters = {
      path: {
        id: '5acc0981eaad142f3a754c77',
      },
      query: {
        min: 6,
        users: ['tom', 'dick', 'harry'],
        deepObject: { a: 7, b: [2, 4] },
      },
      header: {},
      cookie: {},
    };
  4. Handle special query parameter styles: exploded objects and deepObject

    master

    Exegesis handles two specific query parameter patterns that deviate from standard parsing:

    1. Exploded Objects (explode: true)

    When a query parameter is defined as an object with style: form and explode: true, the parameter name itself does not appear in the query string. Instead, the object properties are expanded directly (e.g., ?a=foo&b=bar).

    • Behavior: The query parser is passed the entire set of extracted values as its value and returns them directly. Validation is responsible for filtering extra fields.

    2. deepObject style

    When the style is set to deepObject, the engine uses the qs library to parse the entire query string.

    • Behavior: It finds the value associated with the parameter name and returns the parsed object. In this mode, %-encoding is handled automatically by qs.

    Note: The same query parsers used for these patterns are also applied to application/x-www-form-urlencoded request bodies.

    # Example of an exploded object configuration
    parameter:
      name: 'myParam'
      in: query
      style: form
      explode: true
      schema:
        type: object
        properties:
          a: { type: string }
          b: { type: string }
  5. How parameter parsing and compilation work

    master

    Because parameter parsing occurs on every incoming request, Exegesis optimizes performance by "compiling" the parser ahead-of-time.

    Parsing Logic

    When compiling a parser for a schema, Exegesis follows these rules based on the target type:

    • Single non-object type: Parses the result into a string or an array of strings and attempts type coercion. If coercion fails, a validation error is thrown.
    • Multiple types (none are 'object'): Parses the result into a string or an array and defers full type coercion to the validation step.
    • Single object type: Attempts to parse the result into an object. If parsing fails (e.g., an array of values has an odd length), a validation error is thrown.
    • Multiple types (including 'object'): Currently, if a schema allows multiple types where at least one is an object, Exegesis will throw an exception during the compilation phase.

    Parser Function Signature

    At runtime, parsers are synchronous functions. They receive:

    1. An object containing values for a specific location (e.g., only path values or only query values). Values are either strings or arrays of strings.
    2. A parameterContext object providing metadata about the parameter's origin and, for query parameters, access to the original query string.
  6. Implement Authenticators for OAS3 Security

    master

    Exegesis uses authenticators to map OpenAPI security schemes to authentication logic. An authenticator is a function (either returning a Promise or using a callback) that attempts to authenticate a request.

    Authenticators receive a pluginContext and an info object. Note that in the pluginContext, body and params are undefined because they haven't been parsed yet, but you can access them using the async functions getRequestBody() and getParams().

    The info object contains either {in, name} (for header/query/cookie fields) or {scheme} (for HTTP authentication schemes per RFC 7235).

    async function sessionAuthenticator(pluginContext, info) {
      const session = pluginContext.req.headers.session;
      if (!session) {
        return { type: 'missing', statusCode: 401, message: 'Session key required' };
      } else if (session === 'secret') {
        return { type: 'success', user: { name: 'jwalton', roles: ['read', 'write'] } };
      } else {
        return { type: 'invalid', statusCode: 401, message: 'Invalid session key' };
      }
    }
    
    const options: exegesis.ExegesisOptions = {
      controllers: path.resolve(__dirname, './controllers'),
      authenticators: {
        sessionKey: sessionAuthenticator,
      },
    };
  7. Understand the OpenAPI 3.x.x object hierarchy in Exegesis

    master

    Exegesis represents a parsed OpenAPI 3.x.x document as a hierarchical structure of objects. When working with the oas3 package, you should model your logic around this tree structure:

    • OpenApi: The root object.
      • Servers: List of server configurations.
      • Paths: A collection of path objects.
        • Path[]: Individual path entries.
          • Operation: The HTTP method (GET, POST, etc.) for a path.
            • Parameter: Parameters associated with the operation.
            • RequestMediaType: Media types for the request body.
            • Responses: A collection of response objects.
              • Response: Individual response definitions.
    OpenApi
    +- Servers
    +- Paths
       +- Path[]
          +- Operation
             +- Parameter
             +- RequestMediaType
             +- Responses
               +- Response
  8. Understand the Exegesis Context object

    master

    The context object is passed to every controller and contains request data, response utilities, and OpenAPI metadata.

    Core Properties

    • context.req: The Node http.IncomingMessage.
    • context.res: An Exegesis Response object (similar to Express res). Use this instead of context.origRes to ensure response validation works.
    • context.origRes: The raw Node http.ServerResponse. Avoid writing directly to this if you want Exegesis to validate your response.
    • context.params: An object containing parsed {query, header, path, server, cookie} parameters.
    • context.parameterLocations: A mirror of context.params containing the locations of the parameters (useful for validation errors).
    • context.requestBody: The parsed message body.
    • context.security: A dictionary of security schemes and their associated {user, roles, scope} objects.
    • context.user: The user object returned by the matched authenticator.
    • context.api: Metadata about the resolved OpenAPI parts, including:
      • openApiDoc: The bundled OpenAPI document.
      • serverObject, pathItemObject, operationObject: The matched OAS objects.
      • Various JSON Pointers (serverPtr, pathItemPath, operationPtr, etc.) to the matched objects.
    • context.route.path: The matched path string.
    • context.baseUrl: The base URL stripped from the request.

    Error Utilities

    • context.makeError(statusCode, message): Creates an Error object with a status. Note: This does not throw; you must throw it yourself.
    • context.makeValidationError(message, location): Creates a validation error. location should be a value from context.parameterLocations. Note: This does not throw; you must throw it yourself.
  9. Use Oas3CompileContext for object metadata

    master

    In the oas3 package, every object in the OpenAPI hierarchy is associated with an Oas3CompileContext. This context provides two types of essential information:

    1. Global Configuration: Information about the overall compilation settings.
    2. Provenance: Metadata regarding the origin of that specific object within the document.
  10. Understand OAS3 parameter styles and locations

    master

    Exegesis supports different parameter parsing styles based on where the parameter is located in the request. When working with OpenAPI 3.0 specifications, ensure the style defined in your OAS3 document matches the allowed locations to ensure correct parsing.

    Supported Styles by Location

    LocationAllowed Styles
    pathmatrix, label, simple
    queryform, spaceDelimited, pipeDelimited, deepObject
    headersimple
    cookieNot currently supported

    Style Definitions

    • matrix: Path-style expansion from RFC6570 ({;var})
    • form: Form-style Query expansion from RFC6570 ({?var})
    • label: Label expansion from RFC6570 ({.var})
    • simple: Simple string expansion from RFC6570 ({var})
    • spaceDelimited: Space-separated values.
    • pipeDelimited: Pipe-separated values.
    • deepObject: qs style form parameters (useful for nested objects in query strings).
  11. Define API paths using OpenAPI and x-exegesis-controller

    master

    Exegesis uses OpenAPI 3.0.3 documents to define API paths, parameters, and responses. To link an OpenAPI operation to a specific JavaScript controller, use the x-exegesis-controller specification extension.

    Key fields for Exegesis:

    • operationId: A unique string identifying the operation.
    • x-exegesis-controller: The name of the controller (module) that implements the operation.
    • parameters: Defines input (e.g., in: query) which Exegesis will automatically validate.
    • responses: Defines the expected output structure for different HTTP status codes.
    paths:
      '/greet':
        get:
          summary: Greets the user
          operationId: getGreeting
          x-exegesis-controller: greetController
          parameters:
            - description: The name of the user to greet.
              name: name
              in: query
              required: true
              schema:
                type: string
          responses:
            200:
              description: A greeting for the user.
              content:
                application/json:
                  schema:
                    type: object
                    required:
                      - message
                    properties:
                      message:
                        type: string
  12. Configure Logical Security Requirements (AND/OR) in OAS3

    master

    Exegesis follows the principle of least privilege: authorization fails if ANY authenticator returns an invalid result. All authenticators run for every request to ensure no invalid credentials exist.

    Logical OR (- A, - B)

    Used when a request is valid if either A or B succeeds, provided neither returns invalid.

    • A runs first.
    • If A succeeds, the process returns A's result.
    • If A returns invalid, the process halts and returns invalid.

    Logical AND (- A, B)

    Used when a request is valid only if both A and B succeed.

    • A runs first.
    • If A succeeds, B runs.
    • The process returns the results of both successful authenticators.
    • If any return invalid, the process halts.

    Complex Logic

    You can nest these to represent (A AND B) OR (C AND D).

    # A OR B
    security:
      - A
      - B
    
    # A AND B
    security:
      - A
        B
    
    # (A AND B) OR (C AND D)
    security:
      - A
        B
      - C
        D