backend.openapi is the one block whose options are mandatory. For how it sits alongside the rest of the folder config, see the configuration reference.
Required Options
outfile - Path where the spec is written, relative to your kosmo.config.ts.
openapi - OpenAPI version. Use "3.1.0" or any 3.1.x version.
info - API metadata:
title(required) - Name of your APIversion(required) - API version, use semantic versioning
servers - Array of server objects:
url(required) - URL the API is served from, including thebackend.baseprefix. Paths in the spec are relative to this, so getting it wrong is the usual cause of a spec whose endpoints 404 - see Server URLs and Route Pathsdescription(optional) - Human-readable label
Optional Info Properties
summary - One-line summary
description - Detailed description, supports markdown
termsOfService - URL to terms of service
contact - name, url, email
license - name (required), identifier (SPDX), url
Complete Example
const openapiConfig = {
outfile: "openapi.json",
openapi: "3.1.0",
info: {
title: "My SaaS API",
version: "2.1.0",
summary: "RESTful API for My SaaS Platform",
description: `
# API Documentation
This API provides access to all platform features including
user management, billing, and analytics.`,
termsOfService: "https://myapp.com/terms",
contact: {
name: "API Support",
url: "https://myapp.com/support",
email: "api@myapp.com",
},
license: {
name: "Apache 2.0",
url: "https://www.apache.org/licenses/LICENSE-2.0.html",
},
},
// this folder has backend.base "/api", so the dev server carries that prefix -
// in production the API is deployed at the root of its own host and carries none.
servers: [
{ url: "http://localhost:4556/api", description: "Development server" },
{ url: "https://staging-api.myapp.com", description: "Staging environment" },
{ url: "https://api.myapp.com", description: "Production server" },
],
};Server URLs and Route Paths
Paths in the spec are route names, exactly as they appear under api/. A route at api/users/[id]/index.ts becomes /users/{id}, and the index route becomes / - neither carries the folder's backend.base.
That is deliberate, not an omission. In OpenAPI, paths are relative to servers, and the prefix an API answers on is a deployment decision rather than a property of the route.
The same backend may sit behind /api in development, at the root of a dedicated host in production, and under /v2/api behind a gateway - so the prefix belongs to the server entry, and the paths stay the same in all three.
This is why servers is mandatory: it is the only place the prefix is recorded. Give each entry the full prefix, origin plus backend.base:
// folder with backend.base "/api"
servers: [
{ url: "http://localhost:4556/api", description: "Development server" },
// deployed at the root of its own host - no prefix to add
{ url: "https://api.myapp.com", description: "Production server" },
];// folder with backend.base "/admin/api"
servers: [
{ url: "http://localhost:4556/admin/api", description: "Development server" },
{ url: "https://myapp.com/admin/api", description: "Production server" },
];A client built from the spec resolves /users/{id} against whichever server it is pointed at, requesting http://localhost:4556/api/users/42 in development and https://api.myapp.com/users/42 in production - one spec, no per-environment rebuilds.
TIP
If Try it out in Swagger UI returns 404, check the server URL first. A missing backend.base prefix is the usual cause.