Skip to content

When a middleware is defined in defineRoute, the route is no longer generated #16

@btnalexandre

Description

@btnalexandre

When a middleware is defined in defineRoute, the route is no longer generated in the Open API JSON.

@omermecitoglu same problem for me but I use route.ts

Despite my defineRoute that I created on /app/api/v1/orders/route.ts, the route doesn't appear in the swagger specs... (I'm on Next.js 14.2.15)

An example :

// app/api/v1/orders/route.ts

import { NextResponse } from "next/server";
import { createWCOrder } from "~/actions/woocommerce/orders/orders";
import { CreateOrderDTO } from "~/actions/woocommerce/orders/orders.schema";
import { validateApiKey } from "~/lib/api";

import defineRoute from "@omer-x/next-openapi-route-handler";
import { z } from "zod";

export const { POST } = defineRoute({
  operationId: "createOrder",
  method: "POST",
  summary: "Create a new order",
  description: "Create a new order from your store",
  tags: ["Orders"],
  requestBody: CreateOrderDTO,
  middleware: (handler) => async (req, context) => {
    const { userId } = await validateApiKey({ req });
    if (!userId) {
      return NextResponse.json({ message: "Unauthorized" }, { status: 401 });
    }

    return handler(req, context);
  },
  action: async ({ body }) => {
    const orderCreatedData = await createWCOrder(body);
    return NextResponse.json(orderCreatedData, { status: 201 });
  },
  responses: {
    200: {
      description: "Order created successfully",
    },
    401: {
      description: "Unauthorized",
    },
    400: {
      description: "Invalid request",
    },
    500: {
      description: "Error creating order",
    },
  },
  handleErrors: (errorType, issues) => {
    switch (errorType) {
      case "PARSE_FORM_DATA":
      case "PARSE_REQUEST_BODY":
      case "PARSE_SEARCH_PARAMS":
        return NextResponse.json(issues, { status: 400 });
      case "PARSE_PATH_PARAMS":
        return new Response(null, { status: 404 });
      case "UNNECESSARY_PATH_PARAMS":
      case "UNKNOWN_ERROR":
        return new Response("Error creating order", { status: 500 });
      default:
        return new Response(null, { status: 500 });
    }
  },
});
// components/ReactSwagger.tsx

"use client";
import SwaggerUI from "swagger-ui-react";
import "swagger-ui-react/swagger-ui.css";

const DocsPage = () => <SwaggerUI url="/api/swagger" />;

export default DocsPage;
// app/api/docs/page.tsx
import ReactSwagger from "~/components/ReactSwagger";

export default async function APIDocsPage() {
  return <ReactSwagger />;
}
// app/api/swagger/route.ts
import generateOpenApiSpec from "@omer-x/next-openapi-json-generator";
import { CreateOrderDTO } from "~/actions/woocommerce/orders/orders.schema";

export const dynamic = "force-static";

async function GET() {
  const spec = await generateOpenApiSpec(
    {
      CreateOrderDTO,
    },
    {
      clearUnusedSchemas: false,
      securitySchemes: {
        apiKey: {
          type: "apiKey",
          in: "header",
          name: "X-API-Key",
        },
      },
    },
  );
  return Response.json(spec);
}

export { GET };

JSON generated on http://localhost:3000/api/swagger :

{
  "openapi": "3.1.0",
  "info": {
    "title": "Test",
    "version": "0.1.0"
  },
  "paths": {

  },
  "components": {
    "schemas": {
      "CreateOrderDTO": {
        "type": "object",
        "properties": {
          "payment_method": {
            "type": "string"
          },
          "payment_method_title": {
            "type": "string"
          },
          "set_paid": {
            "type": "boolean"
          },
          "billing": {
            "type": "object",
            "properties": {
              "first_name": {
                "type": "string"
              },
              "last_name": {
                "type": "string"
              },
              "address_1": {
                "type": "string"
              },
              "address_2": {
                "type": "string"
              },
              "city": {
                "type": "string"
              },
              "postcode": {
                "type": "string"
              },
              "country": {
                "type": "string"
              },
              "state": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "format": "email"
              },
              "phone": {
                "type": "string"
              }
            },
            "required": [
              "first_name",
              "last_name",
              "address_1",
              "city",
              "postcode",
              "country",
              "email"
            ],
            "additionalProperties": false
          },
          "shipping": {
            "type": "object",
            "properties": {
              "first_name": {
                "type": "string"
              },
              "last_name": {
                "type": "string"
              },
              "address_1": {
                "type": "string"
              },
              "address_2": {
                "type": "string"
              },
              "city": {
                "type": "string"
              },
              "postcode": {
                "type": "string"
              },
              "country": {
                "type": "string"
              },
              "state": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "format": "email"
              },
              "phone": {
                "type": "string"
              }
            },
            "required": [
              "first_name",
              "last_name",
              "address_1",
              "city",
              "postcode",
              "country"
            ],
            "additionalProperties": false
          },
          "shipping_method": {
            "type": "object",
            "properties": {
              "carrier": {
                "type": "string",
                "enum": [
                  "mondial_relay",
                  "colissimo"
                ]
              },
              "shipping_point": {
                "type": "object",
                "properties": {
                  "country": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2
                  },
                  "address": {
                    "type": "string"
                  },
                  "postal_code": {
                    "type": "string"
                  },
                  "city": {
                    "type": "string"
                  }
                },
                "required": [
                  "country",
                  "address"
                ],
                "additionalProperties": false
              },
              "total": {
                "type": "string"
              }
            },
            "required": [
              "carrier",
              "total"
            ],
            "additionalProperties": false
          },
          "line_items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "product_id": {
                  "type": "number"
                },
                "quantity": {
                  "type": "number"
                },
                "name": {
                  "type": "string"
                },
                "subtotal": {
                  "type": "string"
                },
                "total": {
                  "type": "string"
                }
              },
              "required": [
                "product_id",
                "quantity"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "payment_method",
          "payment_method_title",
          "set_paid",
          "billing",
          "shipping",
          "shipping_method",
          "line_items"
        ],
        "additionalProperties": false
      }
    },
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key"
      }
    }
  },
  "tags": []
}

Originally posted by @btnalexandre in #20

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingreleased

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions