cloudflare / chanfana

OpenAPI 3 and 3.1 schema generator and validator for Hono, itty-router and more!
https://chanfana.pages.dev
MIT License
275 stars 37 forks source link

Can itty use OAS 3.1 directly? #67

Closed tforster closed 11 months ago

tforster commented 1 year ago

We use an API-design-first approach where we design all our APIs resulting in complete OAS schemas ready for developers to use.

Instead of using

static schema = {
    tags: ['ToDo'],
    summary: 'List all ToDos',
    parameters: {
      page: Query(Int, {
        description: 'Page number',
        default: 1,
        required: false,
      }),
    },
    responses: {
      '200': {
        schema: {
          currentPage: 1,
          nextPage: 2,
          results: ['lorem'],
        },
      },
    },
  }

I was hoping to do something like

import openapi from "openapi.json"; // Obtained from our library of new and existing OAS schemas
static schema = openapi.paths["/todos"].get;

Unfortunately, I get Uncaught TypeError: Cannot read properties of undefined (reading 'generated') which as far as I can tell is because the full OAS 3.1 spec is more verbose than the itty expectation. For instance, a simple /users endpoint has a response

        "responses": {
          "200": {
            "description": "User details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "username"
                  ],
                  "properties": {
... snip

Trimming out content and application/json, and adjusting the closing braces, works. Similarly, the itty parameters don't follow OAS either since OAS doesn't generate this syntax.

      page: Query(Int, {
        description: 'Page number',
        default: 1,
        required: false,
      }),
    },

Other than manually editing very extensive OAS files, is there any workaround? Are there any plans to support OAS properly in the future?

G4brym commented 1 year ago

Hey @tforster unfortunately that use case is currently not supported, we will take this into consideration in the next major release.

Best workaround i can think right now includes disabling the parameters and request body validations This way you will be able to call your endpoints in the /docs path and deploy your worker


export class ToDoList extends OpenAPIRoute {
    static getParsedSchema(): Record<any, any> {
        return {
            "tags": [
                "ToDo"
            ],
            "summary": "List all ToDos",
            "parameters": [
                {
                    "description": "Page number",
                    "required": false,
                    "schema": {
                        "type": "integer",
                        "description": "Page number",
                        "default": 1
                    },
                    "name": "page",
                    "in": "query"
                }
            ],
            "responses": {
                "200": {
                    "description": "Successful Response",
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "currentPage": {
                                        "type": "number",
                                        "example": 1
                                    },
                                    "nextPage": {
                                        "type": "number",
                                        "example": 2
                                    },
                                    "results": {
                                        "type": "array",
                                        "items": {
                                            "type": "string",
                                            "example": "lorem"
                                        }
                                    }
                                },
                                "required": [
                                    "currentPage",
                                    "nextPage",
                                    "results"
                                ]
                            }
                        }
                    }
                }
            },
            "operationId": "post_ToDoList"
        }
    }

    async execute(...args: any[]) {
        // @ts-ignore
        const resp = await this.handle(...args)

        if (!(resp instanceof Response) && typeof resp === 'object') {
            return this.jsonResp({data: resp})
        }

        return resp
    }

    async handle(
        request: Request,
        env: any,
        context: any
    ) {

        return {
            currentPage: 1,
            nextPage: 2,
            results: ['lorem', 'ipsum'],
        }
    }
}
G4brym commented 11 months ago

Hey there, i've just published the new v1.0.0 release that brings support for Open API 3.1 by default Try upgrading to the latest version and check if your use case is already supported, if not please re open this issue to let us know Migration guide to 1.0.0 available here