### OpenAPI 3.0.0 Specification Example Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/forward-why-use-openapi.md This JSON snippet demonstrates a basic OpenAPI 3.0.0 specification for a pet store API. It defines API information, server URLs, paths with GET operations, request parameters, and response schemas for both successful (200 OK) and error scenarios. It also includes component schemas for 'Pet', 'Pets', and 'Error' objects. ```json { "openapi": "3.0.0", "info": { "version": "1.0.0", "title": "Swagger Petstore", "license": { "name": "MIT" } }, "servers": [ { "url": "http://petstore.swagger.io/v1" } ], "paths": { "/pets/{petId}": { "get": { "summary": "Info for a specific pet", "operationId": "showPetById", "tags": ["pets"], "parameters": [ { "name": "petId", "in": "path", "required": true, "description": "The id of the pet to retrieve", "schema": { "type": "number" } } ], "responses": { "200": { "description": "Expected response to a valid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } } } }, "default": { "description": "unexpected error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } } }, "components": { "schemas": { "Pet": { "type": "object", "required": ["id", "name"], "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" }, "tag": { "type": "string" } } }, "Pets": { "type": "array", "maxItems": 100, "items": { "$ref": "#/components/schemas/Pet" } }, "Error": { "type": "object", "required": ["code", "message"], "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" } } } } } } ``` -------------------------------- ### GET /widgets Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/tutorial-automatic-openapi-controllers.md Retrieves a list of widgets, with an optional filter for names containing a specific string. ```APIDOC ## GET /widgets ### Description Retrieves a list of widgets, with an optional filter for names containing a specific string. ### Method GET ### Endpoint /widgets ### Parameters #### Query Parameters - **nameContains** (string) - Optional - Filters widgets by name match ### Request Example ```json { "example": "No request body for GET requests" } ``` ### Response #### Success Response (200) - **widgets** (array) - An array of widget objects #### Response Example ```json { "example": [ { "id": 1, "name": "Example Widget", "description": "This is a sample widget." } ] } ``` ``` -------------------------------- ### Create an API Method with @Get Decorator Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/tutorial-automatic-openapi-controllers.md This example demonstrates creating an API method within a controller using the @Get decorator. It defines an asynchronous method that fetches data and returns it. The @Get decorator specifies the HTTP method, path, and a summary for the OpenAPI documentation. The returned value is automatically serialized as JSON. ```typescript import { Controller, Get } from "@simply-openapi/controllers"; import { getWidgets } from "../widgets"; @Controller("/widgets", { tags: ["widgets"] }) class WidgetController { @Get("/", { summary: "Gets all widgets" }) async getWidgets() { const widgets = await getWidgets(); return widgets; } } ``` -------------------------------- ### GET /widgets Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/tutorial-automatic-openapi-controllers.md Retrieves a list of all widgets. This endpoint is documented using the @JsonResponse decorator with a schema for the response body. ```APIDOC ## GET /widgets ### Description Retrieves a list of all widgets. ### Method GET ### Endpoint /widgets ### Parameters #### Query Parameters None #### Request Body None ### Request Example None ### Response #### Success Response (200) - **widgets** (array) - An array of widget objects. - **id** (integer) - The unique identifier for the widget. - **name** (string) - The name of the widget. #### Response Example ```json [ { "id": 1, "name": "Example Widget" } ] ``` ``` -------------------------------- ### GET /widgets/{widget_id} Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/tutorial-automatic-openapi-controllers.md Retrieves a specific widget by its unique ID. ```APIDOC ## GET /widgets/{widget_id} ### Description Retrieves a specific widget by its unique ID. ### Method GET ### Endpoint /widgets/{widget_id} ### Parameters #### Path Parameters - **widget_id** (integer) - Required - The ID of the widget to fetch ### Request Example ```json { "example": "No request body for GET requests" } ``` ### Response #### Success Response (200) - **widget** (object) - The fetched widget object #### Error Response (404) - **description** - Widget not found #### Response Example ```json { "example": { "id": 1, "name": "Example Widget", "description": "This is a sample widget." } } ``` ``` -------------------------------- ### Combine Authenticators and Controllers for OpenAPI Spec (TypeScript) Source: https://context7.com/sunsetfi/simply-openapi/llms.txt This snippet shows how to generate an OpenAPI specification and a router by combining custom authenticators and controllers using Simply-OpenAPI. It demonstrates the final setup step after defining authentication logic and protected routes. ```typescript import { Authenticator, AuthenticationController, OperationRequestContext, HttpBearerAuthenticationCredentials, Controller, Get, RequireAuthentication, BindSecurity, createOpenAPIFromControllers, createRouterFromSpec, } from "@simply-openapi/controllers"; import { Unauthorized } from "http-errors"; interface User { id: string; username: string; scopes: string[]; } // Mock verifyJWT function async function verifyJWT(token: HttpBearerAuthenticationCredentials): Promise { if (token === "valid-jwt-token") { return { id: "123", username: "testuser", scopes: ["read", "admin"], }; } return false; } // Bearer token authenticator @Authenticator("bearerAuth", { type: "http", scheme: "bearer", bearerFormat: "JWT", }) class BearerAuthenticator implements AuthenticationController { async authenticate( token: HttpBearerAuthenticationCredentials, scopes: string[], ctx: OperationRequestContext ): Promise { const user = await verifyJWT(token); if (!user) return false; if (!scopes.every(scope => user.scopes.includes(scope))) { throw new Unauthorized("Insufficient permissions"); } return user; } } // API Key authenticator @Authenticator("apiKeyAuth", { type: "apiKey", name: "X-API-Key", in: "header", }) class ApiKeyAuthenticator implements AuthenticationController { async authenticate( apiKey: string, scopes: string[], ctx: OperationRequestContext ): Promise<{ apiKey: string } | false> { const validKeys = ["secret-key-1", "secret-key-2"]; if (!validKeys.includes(apiKey)) return false; return { apiKey }; } } // Protected controller @Controller("/secure") @RequireAuthentication("bearerAuth", ["read"]) class SecureController { @Get("/profile") async getProfile( @BindSecurity("bearerAuth") user: User ) { return { id: user.id, username: user.username }; } @Get("/admin") @RequireAuthentication("bearerAuth", ["admin"]) async adminEndpoint( @BindSecurity("bearerAuth") user: User ) { return { admin: true, user: user.username }; } } // Setup const spec = createOpenAPIFromControllers( { title: "Secure API", version: "1.0.0" }, [BearerAuthenticator, ApiKeyAuthenticator, new SecureController()] ); const router = createRouterFromSpec(spec); console.log("OpenAPI Spec generated."); console.log("Router created."); ``` -------------------------------- ### Create Widget with 201 Status and Location Header (TypeScript) Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/tutorial-automatic-openapi-controllers.md This example demonstrates how to create a new widget using a POST request. It utilizes the HandlerResult class to return a 201 CREATED status code and set the 'Location' header to the URL of the newly created widget. This is useful for RESTful APIs following standard conventions for resource creation. ```typescript import { Controller, Post, JsonResponse, EmptyResponse, QueryParam, PathParam, RequiredJsonBody, HandlerResult } from "@simply-openapi/controllers"; import { NotFound } from "http-errors"; import { widgetSchema, getWidgets } from "../widgets"; @Controller("/widgets", {tags: ["widgets"]}) class WidgetController { ... @Post("/", { summary: "Create a widget" }) @JsonResponse( 201, widgetSchema, { description: "The widget was created" } ) @EmptyResponse(400, { description: "Bad Request" }) async addWidget( @RequiredJsonBody( creatableWidgetSchema, { description: "The ID of the widget to fetch" } ) body: CreatableWidget ) { const widget = await addWidget(body); return HandlerResult .status(201) .header("Location", `${baseUrl}/widgets/${widget.id}`) .json(widget) } } ``` -------------------------------- ### Transform Widget Schemas with Typebox Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/reducing-interface-and-schema-duplication.md This example illustrates how Typebox can be used to create variations of a base schema, such as arrays of widgets, schemas for creating widgets (omitting the ID), and schemas for patching widgets (partial and omitting the ID). It also shows how these schemas can be integrated into Simply-OpenAPI controllers. ```typescript import { Type, Static } from "@sinclair/typebox"; // Assuming widgetSchema is defined as in the previous example // export const widgetSchema = Type.Object({...}); // export type Widget = Static; const widgetArraySchema = Type.Array(widgetSchema); type WidgetArray = Static; const creatableWidgetSchema = Type.Omit(widgetSchema, ["id"]); type CreatableWidget = Static; const patchableWidgetSchema = Type.Partial( Type.Omit(widgetSchema, ["id"]) ); type PatchableWidget = Static; // Example controller integration (requires @simply-openapi/controllers) // import { Controller, Get, Post, Patch, JsonResponse, RequiredJsonBody } from "@simply-openapi/controllers"; // @Controller("/widgets", { tags: [ "Widgets" ]}) // export class WidgetsController { // @Get("/") // @JsonResponse(200, widgetArraySchema) // async getWidgets() { // // ... implementation // } // @Post("/") // @JsonResponse(201, widgetSchema) // async createWidget( // @RequiredJsonBody(creatableWidgetSchema) // body: CreatableWidget // ) { // // ... implementation // } // @Patch("/{widget_id}") // @JsonResponse(200, widgetSchema) // async patchWidget( // @RequiredJsonBody(patchableWidgetSchema) // body: PatchableWidget // ) { // // ... implementation // } // } ``` -------------------------------- ### GET /{widgetId} Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/tutorial-automatic-openapi-controllers.md Retrieves a specific widget by its ID. This endpoint demonstrates how to reference OpenAPI schema components for path parameters. ```APIDOC ## GET /{widgetId} ### Description Retrieves a specific widget by its ID. ### Method GET ### Endpoint /widgets/{widgetId} ### Parameters #### Path Parameters - **widgetId** (integer) - Required - The ID of the widget to retrieve. (Referenced from #/components/schemas/widgetId) #### Query Parameters None #### Request Body None ### Request Example (No request body for GET requests) ### Response #### Success Response (200) - **widget** (object) - The requested widget object. #### Response Example ```json { "widget": { "id": 1, "name": "Sample Widget", "description": "Details of the sample widget." } } ``` ``` -------------------------------- ### Generated OpenAPI PathItem for Widgets Endpoint Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/tutorial-automatic-openapi-controllers.md Illustrates the resulting OpenAPI PathItem object for the '/widgets' GET endpoint after applying the @JsonResponse decorator. This shows how the response schema is integrated into the OpenAPI specification. ```json { "...": "...", "/widgets": { "get": { "operationId": "WidgetController.getWidgets", "responses": { "200": { "description": "All widgets", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer", "minimum": 1 }, "name": { "type": "string", "minLength": 1 } }, "required": [ "id", "name" ] } } } } } }, "summary": "Gets all widgets", "tags": [ "widgets" ] } } } ``` -------------------------------- ### TypeScript: Define Widget Controller with Path Parameter Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/tutorial-automatic-openapi-controllers.md This TypeScript code extends the WidgetController to include a GET endpoint for fetching a specific widget by its ID. It uses the `@PathParam` decorator to capture the 'widget_id' from the URL path. This decorator documents the path parameter and validates its type as an integer. ```typescript import { Controller, Get, JsonResponse, EmptyResponse, QueryParam, PathParam } from "@simply-openapi/controllers"; import { NotFound } from "http-errors"; import { widgetSchema, getWidgets } from "../widgets"; @Controller("/widgets", {tags: ["widgets"]}) class WidgetController { ... @Get("/{widget_id}", { summary: "Gets a widget by id" }) @JsonResponse( 200, widgetSchema, { description: "The fetched widget" } ) @EmptyResponse(404, { description: "Widget not found" }) async getWidgets( @PathParam( "widget_id", "integer", { description: "The ID of the widget to fetch" } ) widgetId: number ) { const widget = await getWidgetById(widgetId); if (!widget) { throw new NotFound(); } return widgets; } } ``` -------------------------------- ### TypeScript: Define Widget Controller with Query Parameter Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/tutorial-automatic-openapi-controllers.md This TypeScript code defines a WidgetController that handles GET requests to '/widgets'. It uses the `@QueryParam` decorator to accept an optional 'nameContains' query parameter for filtering widgets. The decorator also documents the parameter in the OpenAPI specification and validates incoming requests. ```typescript import { Controller, Get, JsonResponse, QueryParam, } from "@simply-openapi/controllers"; import { widgetSchema, getWidgets } from "../widgets"; @Controller("/widgets", { tags: ["widgets"] }) class WidgetController { @Get("/", { summary: "Gets all widgets" }) @JsonResponse( 200, { type: "array", items: widgetSchema }, { description: "All widgets" }, ) async getWidgets( @QueryParam( "nameContains", { type: "string", minLength: 1 }, { description: "Filters widgets by name match" }, ) nameContains: string | undefined, ) { const widgets = await getWidgets({ nameContains }); return widgets; } } ``` -------------------------------- ### Implement HTTP Basic Authentication Controller Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/adding-authentication.md Provides a TypeScript example for an HTTP Basic authentication controller using `@simply-openapi/controllers`. The `authenticate` method receives decoded username and password, retrieves user data, verifies the password, checks scopes, and returns the user object upon successful authentication, or false if authentication fails. It relies on external functions like `getUserByUsername` and `checkPassword`. ```typescript import { Authenticator, AuthenticationController, HttpBasicAuthenticationCredentials, OperationRequestContext, } from "@simply-openapi/controllers"; @Authenticator("myAuth", { type: "http", scheme: "basic", }) class MyAuthenticator implements AuthenticationController { async authenticate( value: HttpBasicAuthenticationCredentials, scopes: string[], ctx: OperationRequestContext, ) { const user = await getUserByUsername(value.username); if (!checkPassword(value.password, user.hashedPassword)) { return false; } if (!scopes.every((scope) => user.scopes.includes(scope))) { return false; } return user; } } ``` -------------------------------- ### Handling Headers and Cookies Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/tutorial-automatic-openapi-controllers.md Demonstrates the use of decorators for handling cookies and headers, including required and optional variants. ```APIDOC ## Handling Headers and Cookies ### Description This section explains how to use decorators to access and validate cookies and headers passed in incoming requests. These are treated similarly to other parameters. ### Method N/A (Decorator Usage) ### Endpoint N/A (Decorator Usage) ### Parameters #### Path Parameters N/A #### Query Parameters N/A #### Request Body N/A ### Request Example ```typescript import { Controller, Get, Cookie, Header } from "@simply-openapi/controllers"; @Controller() class ExampleController { @Get("/items") async getItems( @Cookie("session_id") sessionId?: string, @Header("X-Request-ID") requestId?: string, @RequiredCookie("user_token") userToken: string ) { // Handler logic using sessionId, requestId, and userToken return { sessionId, requestId, userToken }; } } ``` ### Response #### Success Response (200) - **sessionId** (string) - Optional. The session identifier cookie. - **requestId** (string) - Optional. The request ID header. - **userToken** (string) - The required user token cookie. #### Response Example ```json { "sessionId": "abc123xyz", "requestId": "req-456", "userToken": "token789" } ``` ### Available Decorators - `@Cookie(name, schema, spec?)` - `@RequiredCookie(name, schema, spec?)` - `@Header(name, schema, spec?)` - `@RequiredHeader(name, schema, spec?)` ``` -------------------------------- ### Handling Multi-Type Request Bodies Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/tutorial-automatic-openapi-controllers.md Demonstrates how to handle requests that can accept different media types in the body using the `@Body` decorator. ```APIDOC ## Handling Multi-Type Request Bodies ### Description This section explains how to use the `@Body` decorator to handle requests with varying content types. Only the body matching the specified media type will be populated; others will be undefined. Additional middleware may be required for non-JSON content types. ### Method POST ### Endpoint `/` ### Parameters #### Path Parameters N/A #### Query Parameters N/A #### Request Body - **fooBody** (FooType | undefined) - The body content if the `application/foo+json` media type is used. - **barBody** (BarType | undefined) - The body content if the `application/bar+json` media type is used. ### Request Example ```typescript import { Controller, Post, Body } from "@simply-openapi/controllers"; // Assuming fooTypeSchema, barTypeSchema, FooType, and BarType are defined elsewhere @Controller() class MultiBodyController { @Post("/") async postRequest( @Body("application/foo+json", fooTypeSchema) fooBody: FooType | undefined, @Body("application/bar+json", barTypeSchema) barBody: BarType | undefined ) { if (fooBody) { // Handle fooBody return { message: "Received foo body" }; } else if (barBody) { // Handle barBody return { message: "Received bar body" }; } // Handle cases where neither body matches or is present return { message: "No matching body received" }; } } ``` ### Response #### Success Response (200) - **message** (string) - A confirmation message indicating which body type was processed or if none matched. #### Response Example ```json { "message": "Received foo body" } ``` ``` -------------------------------- ### Get Cookie Value Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/api-reference/contexts.md Retrieves the value of a specified cookie. Returns undefined if the cookie is not set. ```javascript getCookie(name) ``` -------------------------------- ### Handling JSON Request Bodies Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/tutorial-automatic-openapi-controllers.md Illustrates how to use decorators for handling required and optional JSON request bodies with schema validation. ```APIDOC ## Handling JSON Request Bodies ### Description This section covers the use of `@RequiredJsonBody` and `@OptionalJsonBody` decorators for defining and validating JSON payloads sent to API endpoints. Invalid bodies will result in a 400 error. ### Method POST ### Endpoint `/widgets` ### Parameters #### Path Parameters N/A #### Query Parameters N/A #### Request Body - **body** (CreatableWidget) - Required - The properties of the new widget to create. ### Request Example ```typescript import { Controller, Post, JsonResponse, EmptyResponse, RequiredJsonBody } from "@simply-openapi/controllers"; import { widgetSchema, addWidget, creatableWidgetSchema // Assuming this schema is defined elsewhere } from "../widgets"; @Controller("/widgets", {tags: ["widgets"]}) class WidgetController { @Post("/", { summary: "Create a widget" }) @JsonResponse( 201, widgetSchema, { description: "The widget was created." } ) @EmptyResponse(400, { description: "Bad Request" }) async addWidget( @RequiredJsonBody( creatableWidgetSchema, { description: "The properties of the new widget to create." } ) body: CreatableWidget // Assuming CreatableWidget type is defined elsewhere ) { return await addWidget(body); } } ``` ### Response #### Success Response (201) - **Widget Object** (object) - The newly created widget, conforming to `widgetSchema`. #### Response Example ```json { "id": 1, "name": "Example Widget" } ``` ``` -------------------------------- ### Get Request Data Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/api-reference/contexts.md Retrieves the request data for a specified key. Returns undefined if the data is not set. ```javascript getRequestData(key) ``` -------------------------------- ### Get Path Parameter Value Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/api-reference/contexts.md Retrieves the value of a specified path parameter. Returns undefined if the parameter is not set. ```javascript getPathParam(name) ``` -------------------------------- ### Products Controller API Source: https://context7.com/sunsetfi/simply-openapi/llms.txt This section demonstrates the ProductsController, showcasing the use of various parameter decorators for extracting and validating data from requests. ```APIDOC ## GET /products/{category}/{productId} ### Description Get product details by category and product ID, with support for optional query, header, and cookie parameters. ### Method GET ### Endpoint /products/{category}/{productId} ### Parameters #### Path Parameters - **category** (string, enum: ["electronics", "clothing", "food"]) - Required - Product category - **productId** (integer, minimum: 1) - Required - Product ID #### Query Parameters - **includeReviews** (boolean) - Optional - Include product reviews - **currency** (string, enum: ["USD", "EUR", "GBP"]) - Required - Currency for pricing #### Header Parameters - **X-Request-ID** (string, format: uuid) - Optional - Unique request identifier #### Cookie Parameters - **session** (string) - Optional - User session identifier ### Response #### Success Response (200) - **object** (object) - Product details - **category** (string) - **productId** (number) - **includeReviews** (boolean) - **currency** (string) - **requestId** (string) - **hasSession** (boolean) ### Request Example `GET /products/electronics/42?includeReviews=true¤cy=USD` `Headers: X-Request-ID: 550e8400-e29b-41d4-a716-446655440000` ### Response Example ```json { "category": "electronics", "productId": 42, "includeReviews": true, "currency": "USD", "requestId": "550e8400-e29b-41d4-a716-446655440000", "hasSession": true } ``` ``` -------------------------------- ### Creating OpenAPI Spec from Controllers Source: https://context7.com/sunsetfi/simply-openapi/llms.txt Demonstrates how to use `createOpenAPIFromControllers` to generate an OpenAPI 3.1 specification from decorated controller classes. This includes defining schemas, creating controllers with decorators for routing and response handling, and then generating the spec and an Express router. ```APIDOC ## POST /widgets ### Description Retrieves a list of widgets, with optional filtering by status and a limit on the number of results. ### Method GET ### Endpoint /widgets ### Parameters #### Query Parameters - **status** (string) - Optional - Filter by status. Allowed values: "active", "inactive". - **limit** (integer) - Optional - Limit the number of results returned. Must be at least 1. ### Request Example ```json { "example": "No request body for GET request" } ``` ### Response #### Success Response (200) - **type** (array) - Description: All widgets. - **items** (object) - Schema for individual widget objects. - **id** (string) - The unique identifier of the widget. - **name** (string) - The name of the widget. - **status** (string) - The status of the widget. Allowed values: "active", "inactive". #### Response Example ```json { "example": [ { "id": "123e4567-e89b-12d3-a456-426614174000", "name": "Widget 1", "status": "active" } ] } ``` ``` -------------------------------- ### GET /pets/{petId} Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/forward-why-use-openapi.md Retrieves information about a specific pet by its ID. Supports JSON responses and validates input parameters against the defined schema. ```APIDOC ## GET /pets/{petId} ### Description Retrieves information about a specific pet using its unique identifier. ### Method GET ### Endpoint /pets/{petId} ### Parameters #### Path Parameters - **petId** (number) - Required - The ID of the pet to retrieve. #### Query Parameters None #### Request Body None ### Request Example None ### Response #### Success Response (200) - **Pet** (object) - A JSON object representing the pet, containing 'id' (integer), 'name' (string), and optionally 'tag' (string). #### Response Example ```json { "id": 1, "name": "Buddy", "tag": "dog" } ``` #### Error Response (default) - **Error** (object) - A JSON object containing 'code' (integer) and 'message' (string) describing the error. #### Error Response Example ```json { "code": 404, "message": "Pet not found" } ``` ``` -------------------------------- ### Documenting API Responses with Decorators in TypeScript Source: https://context7.com/sunsetfi/simply-openapi/llms.txt Demonstrates using @JsonResponse, @EmptyResponse, and @Response decorators to document API responses for OpenAPI generation. It shows how to specify status codes, schemas, descriptions, and content types. Response validation can be enabled during router creation. ```typescript import { Controller, Get, Post, Delete, JsonResponse, EmptyResponse, Response, PathParam, createOpenAPIFromControllers, createRouterFromSpec, errorObjectsToMessage, } from "@simply-openapi/controllers"; import HttpStatusCodes from "http-status-codes"; const userSchema = { type: "object", properties: { id: { type: "string" }, email: { type: "string", format: "email" }, name: { type: "string" }, }, required: ["id", "email", "name"], }; const errorSchema = { type: "object", properties: { code: { type: "string" }, message: { type: "string" }, }, }; @Controller("/users") class UsersController { // JSON response with schema @Get("/{id}") @JsonResponse(200, userSchema, { description: "User found" }) @JsonResponse(404, errorSchema, { description: "User not found" }) async getUser(@PathParam("id", "string") id: string) { return { id, email: "user@example.com", name: "John Doe" }; } // Empty response (no body) @Delete("/{id}") @EmptyResponse(204, { description: "User deleted successfully" }) @EmptyResponse(404, { description: "User not found" }) async deleteUser(@PathParam("id", "string") id: string) { // Return nothing for 204 return undefined; } // Custom response with multiple content types @Get("/{id}/export") @Response( 200, { "application/json": { schema: userSchema }, "text/csv": { schema: { type: "string" } }, }, { description: "Exported user data" } ) async exportUser(@PathParam("id", "string") id: string) { return { id, email: "user@example.com", name: "John" }; } } // Enable response validation (useful for development/testing) const spec = createOpenAPIFromControllers({ title: "API", version: "1.0.0" }, [new UsersController()]); const router = createRouterFromSpec(spec, { responseValidation: { required: true, // Require responses to match schema errorHandler: (err, ctx) => { console.error( `Response validation failed for ${ctx.method} ${ctx.path}:`, errorObjectsToMessage(err.errors) ); ctx.res.status(500).json({ error: "Internal validation error" }); }, }, }); ``` -------------------------------- ### Returning JSON Responses Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/tutorial-automatic-openapi-controllers.md Explains the default behavior of returning JSON responses and how it maps to a 200 status code with a JSON body. ```APIDOC ## Returning JSON Responses ### Description When a handler function returns a plain JSON object (or a Promise resolving to one), Simply OpenAPI automatically serializes it into a JSON response with a 200 status code and sets the `Content-Type` header to `application/json`. ### Method N/A (General Response Handling) ### Endpoint N/A (General Response Handling) ### Parameters N/A ### Request Example ```typescript import { Controller, Get, JsonResponse } from "@simply-openapi/controllers"; @Controller() class DefaultResponseController { @Get("/data") @JsonResponse(200, { type: "object", properties: { message: { type: "string" } } }) // Example schema async getData() { // Returning a plain object will result in a JSON response return { message: "Success!" }; } } ``` ### Response #### Success Response (200) - **message** (string) - A success message. #### Response Example ```json { "message": "Success!" } ``` ``` -------------------------------- ### POST /widgets - Create a widget Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/tutorial-automatic-openapi-controllers.md This endpoint creates a new widget and returns a 201 CREATED status code with a Location header pointing to the newly created widget. ```APIDOC ## POST /widgets ### Description Creates a new widget and returns its details along with a 201 status code and a Location header. ### Method POST ### Endpoint /widgets ### Parameters #### Query Parameters None #### Request Body - **body** (CreatableWidget) - Required - The details of the widget to create. ### Request Example ```json { "name": "My Widget" } ``` ### Response #### Success Response (201) - **id** (number) - The unique identifier of the created widget. - **name** (string) - The name of the created widget. #### Response Example ```json { "id": 1, "name": "My Widget" } ``` #### Error Response (400) - **description**: Bad Request ``` -------------------------------- ### Get Query Parameter Value Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/api-reference/contexts.md Retrieves the value of a specified query parameter. Returns undefined if the parameter is not set. If the parameter is set multiple times, an array of values is returned. ```javascript getQuery(name) ``` -------------------------------- ### Get Header Value Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/api-reference/contexts.md Retrieves the value of a specified header, ignoring case. Returns undefined if the header is not set. If the header is set multiple times, an array of values is returned. ```javascript getHeader(name) ``` -------------------------------- ### HTTP Method Decorators (@Get, @Post, etc.) Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/api-reference/decorators.md Decorators for defining HTTP methods and paths for API endpoints. They allow specifying paths with parameters and appending OpenAPI Operation objects for descriptions and tags. ```APIDOC ## @Get, @Post, @Put, @Patch, @Delete, @Head, @Options, @Trace ### Description This family of decorators describes a new path and method in the OpenAPI spec. They define the HTTP method and the endpoint path for an API operation. ### Usage Applied to methods within a controller. ### Parameters 1. **path** (string): Required. The path for the method. Can include express or OpenAPI style parameters. 2. **operationObject** (object): Optional. A partial OpenAPI Operation object to append, typically used for tags and descriptions. ### Example ```typescript import { Get } from "@simply-openapi/controllers" class WidgetsController { @Get("/widgets/{widget_id}", { tags: ["Widgets"], description: "Gets a widget by id" }) getWidgetById(...) { // ... method implementation } } ``` ``` -------------------------------- ### POST /widgets Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/tutorial-automatic-openapi-controllers.md This endpoint allows for the creation of a new widget. It uses Express's Request and Response objects and includes middleware for security. ```APIDOC ## POST /widgets ### Description Creates a new widget. ### Method POST ### Endpoint /widgets ### Parameters #### Query Parameters None #### Request Body - **widget** (object) - Required - The widget object to be created. ### Request Example ```json { "widget": { "name": "Example Widget", "description": "This is a sample widget." } } ``` ### Response #### Success Response (201) - **description**: The widget was created #### Response Example (No specific response body defined, typically returns 201 Created) #### Error Response (400) - **description**: Bad Request ``` -------------------------------- ### GET /authenticated - Using @RequireAuthentication Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/adding-authentication.md This endpoint demonstrates how to use the @RequireAuthentication decorator to ensure a user is authenticated and to provide the authentication result directly to method arguments. It handles cases with a single security scheme. ```APIDOC ## GET /authenticated - Using @RequireAuthentication ### Description This endpoint ensures the user is authenticated and provides the result of the authenticator to the decorated argument. ### Method GET ### Endpoint /authenticated ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```typescript import { Controller, RequireAuthentication, Get, } from "@simply-openapi/controllers"; @Controller() class WidgetController { @Get("/authenticated") getAuthenticated( @RequireAuthentication("myAuth", ["my-scope"]) user: MyAuthUserType, ) { console.log("Got request from", user.username); return "OK"; } } ``` ### Response #### Success Response (200) - **string**: "OK" #### Response Example ```json "OK" ``` ``` -------------------------------- ### OpenAPI: Query Parameter Documentation for Widget Controller Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/tutorial-automatic-openapi-controllers.md This JSON snippet represents the OpenAPI specification for the 'getWidgets' endpoint. It details the 'nameContains' query parameter, including its name, description, and schema, which is used for documentation and request validation. ```json { "...": "...", "/widgets": { "get": { "operationId": "WidgetController.getWidgets", "parameters": [ { "in": "query", "name": "nameContains", "description": "Filters widgets by name match", "schema": { "type": "string", "minLength": 1 } } ], "...": "..." } } } ``` -------------------------------- ### GET /{widget_id} - Using @BindSecurity Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/adding-authentication.md This endpoint illustrates the use of the @BindSecurity decorator to retrieve the resolved security scheme value without performing authentication itself. It's useful for schemes defined in OpenAPI or at the controller level. ```APIDOC ## GET /{widget_id} - Using @BindSecurity ### Description Retrieves the resolved security scheme value using @BindSecurity. This decorator does not authenticate the method but provides the authenticator's value if it was applied via OpenAPI or another decorator. ### Method GET ### Endpoint /widgets/{widget_id} ### Parameters #### Path Parameters - **widget_id** (string) - Required - The ID of the widget to retrieve. #### Query Parameters None #### Request Body None ### Request Example ```typescript import { Controller, RequireAuthentication, Get, BindSecurity } from "@simply-openapi/controllers"; @Controller("/widgets") @RequireAuthentication("myAuth", ["widgets.read"]) class WidgetsController { @Get("/{widget_id}") getWidget( @BindSecurity("myAuth") user: MyAuthUser ) { console.log("Request from user", user.username); // ... implementation to get widget } } ``` ### Response #### Success Response (200) - **any**: Details of the requested widget (implementation specific). #### Response Example ```json { "id": "123", "name": "Example Widget" } ``` ``` -------------------------------- ### Custom OpenAPI Specification Injection with TypeScript Decorators Source: https://context7.com/sunsetfi/simply-openapi/llms.txt Demonstrates using @OpenAPI and @OpenAPIOperation decorators to inject custom OpenAPI fragments, including shared schemas and operation-specific details like external documentation and tags. This allows for fine-grained control over the generated OpenAPI specification beyond what other decorators provide. ```typescript import { Controller, Get, Post, OpenAPI, OpenAPIOperation, JsonResponse, PathParam, RequiredJsonBody, } from "@simply-openapi/controllers"; // Add shared schemas at controller level @Controller("/orders") @OpenAPI({ components: { schemas: { OrderStatus: { type: "string", enum: ["pending", "processing", "shipped", "delivered"], }, Order: { type: "object", properties: { id: { type: "string" }, status: { $ref: "#/components/schemas/OrderStatus" }, items: { type: "array", items: { $ref: "#/components/schemas/OrderItem" } }, total: { type: "number" }, }, }, OrderItem: { type: "object", properties: { productId: { type: "string" }, quantity: { type: "integer", minimum: 1 }, price: { type: "number" }, }, }, }, }, }) // Add common parameters to all operations in controller @OpenAPIOperation({ parameters: [ { name: "X-Correlation-ID", in: "header", schema: { type: "string", format: "uuid" } }, ], }) class OrdersController { @Get("/{orderId}") @JsonResponse(200, { $ref: "#/components/schemas/Order" }) // Reference the schema @OpenAPIOperation({ externalDocs: { description: "Order API documentation", url: "https://docs.example.com/orders", }, }) async getOrder(@PathParam("orderId", "string") orderId: string) { return { id: orderId, status: "processing", items: [{ productId: "prod-1", quantity: 2, price: 29.99 }], total: 59.98, }; } @Post("/") @JsonResponse(201, { $ref: "#/components/schemas/Order" }) @OpenAPIOperation({ summary: "Create a new order", description: "Creates a new order with the specified items", tags: ["orders", "checkout"], // Additional tags }) async createOrder( @RequiredJsonBody({ type: "object", properties: { items: { type: "array", items: { $ref: "#/components/schemas/OrderItem" } }, }, required: ["items"], }) body: { items: Array<{ productId: string; quantity: number; price: number }> } ) { const total = body.items.reduce((sum, item) => sum + item.price * item.quantity, 0); return { id: "order-123", status: "pending", items: body.items, total }; } } ``` -------------------------------- ### Retrieving Request Data with Custom Decorator (TypeScript) Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/request-data.md This example demonstrates creating a custom decorator `RequestUser` using `createRequestDataDecorator` to retrieve specific request data ('x-user'). This data can then be injected as a parameter into controller methods. ```typescript import { createRequestDataDecorator, Get, } from "@simply-openapi/controllers"; const RequestUser = createRequestDataDecorator("x-user"); ... class WidgetsController { @Get("/") getWidgets( @RequestUser() user: any ) { ... } } ``` -------------------------------- ### Create Express Router from OpenAPI Spec (TypeScript) Source: https://github.com/sunsetfi/simply-openapi/blob/main/packages/controllers/docs/dev/creating-express-routes.md Generates an Express router from an OpenAPI specification, automatically implementing validation and request handling from controllers. This is the minimum setup required for a functional router. ```typescript import { createOpenAPIFromControllers, createRouterFromSpec } from "@simply-openapi/controllers"; import express from "express"; const mySpec = createOpenAPIFromControllers(...); const router = createRouterFromSpec(mySpec); const app = express(); app.use(router); app.listen(8080); ```