ExpressKit OpenApi Integration
This package provides OpenAPI/Swagger integration for ExpressKit, automatically generating API documentation from your route contracts and authentication handlers.
Quick Start
-
Add the integration to an existing ExpressKit project:
npm install @gravity-ui/expresskit-api
-
Wrap your routes before passing them to ExpressKit:
Simple approach (without global auth handlers):
import {
ExpressKit,
withContract,
AppRoutes,
RouteContract,
AuthPolicy,
} from '@gravity-ui/expresskit';
import {NodeKit} from '@gravity-ui/nodekit';
import {z} from 'zod';
import {createOpenApiRegistry, bearerAuth, apiKeyAuth} from '@gravity-ui/expresskit-api';
const {registerRoutes} = createOpenApiRegistry({title: 'Super API'});
const apiKeyHandler = apiKeyAuth(
'apiKeyAuth',
'header',
'X-API-Key',
['read:items'],
)(function authenticate(req, res, next) {
const apiKey = req.headers['x-api-key'];
if (apiKey !== 'valid_api_key') {
res.status(401).json({error: 'Unauthorized: Invalid API key'});
return;
}
next();
});
const CreateItemConfig = {
operationId: 'createItem',
summary: 'Create a new item',
tags: ['Items'],
request: {
body: z.object({
itemName: z.string().min(3, 'Item name must be at least 3 characters long'),
quantity: z.number().int().positive('Quantity must be a positive integer'),
}),
},
response: {
content: {
201: z.object({
itemId: z.string(),
itemName: z.string(),
quantity: z.number().positive(),
}),
},
},
} satisfies RouteContract;
const createItemHandler = withContract(CreateItemConfig)(async (req, res) => {
const {itemName, quantity} = req.body;
const newItem = {
itemId: `item_${Date.now()}`,
itemName,
quantity,
};
res.sendTyped(201, newItem);
});
export const routes: AppRoutes = {
'POST /items': {
handler: createItemHandler,
authHandler: apiKeyHandler,
authPolicy: AuthPolicy.required,
},
};
const app = new ExpressKit(nodekit, registerRoutes(routes, nodekit));
app.run();
Using setup parameter (with global auth handlers support):
import {
ExpressKit,
withContract,
AppRoutes,
RouteContract,
AuthPolicy,
} from '@gravity-ui/expresskit';
import {NodeKit} from '@gravity-ui/nodekit';
import {z} from 'zod';
import {createOpenApiRegistry, bearerAuth} from '@gravity-ui/expresskit-api';
const {registerRoutes} = createOpenApiRegistry({title: 'Super API'});
const globalAuthHandler = bearerAuth('jwtAuth')(function authenticate(req, res, next) {
const token = req.headers.authorization?.replace('Bearer ', '');
if (token !== 'valid_token') {
res.status(401).json({error: 'Unauthorized'});
return;
}
next();
});
const nodekit = new NodeKit({
config: {
appAuthHandler: globalAuthHandler,
appAuthPolicy: AuthPolicy.required,
},
});
const routes: AppRoutes = {
'POST /items': {
handler: createItemHandler,
},
};
const app = new ExpressKit(nodekit, registerRoutes(routes, nodekit));
app.run();
Config
createOpenApiRegistry(config?: OpenApiRegistryConfig) tunes both the generated schema and the Swagger UI mount. Key options:
title | "API Documentation" | Top-level title shown in the UI. |
version | "1.0.0" | Populates info.version. |
description | "Generated API documentation" | Short blurb under the title. |
contact | undefined | {name, email, url} for ownership info. |
license | undefined | {name, url} displayed in the footer. |
servers | [ { url: 'http://localhost:3030' } ] | Servers array for the spec dropdown. |
swaggerUi | {} | Passed straight to swagger-ui-express (customCss, explorer, themes, …). |
enabled | true | Convenience flag—skip calling registerRoutes if you want to hide docs. |
path | '/api/docs' | Mount path for Swagger UI; value is used as-is. |
swaggerJsonPath | undefined | Path relative to mount path where OpenAPI schema is served as JSON. When set, Swagger UI loads the schema from this endpoint instead of embedding it directly. |
Usage example:
const {registerRoutes} = createOpenApiRegistry({
title: 'Super API',
description: 'Internal platform endpoints',
servers: [{url: 'https://api.example.com'}],
swaggerUi: {
explorer: true,
customCss: '.topbar { display: none; }',
},
});
Using swaggerJsonPath for async schema loading:
const {registerRoutes} = createOpenApiRegistry({
title: 'Super API',
path: '/api/docs',
swaggerJsonPath: '/swagger.json',
});
Security Schemes for OpenAPI Documentation
ExpressKit supports automatic generation of security requirements in OpenAPI documentation based on the authentication handlers used in your routes.
Features
- HOC Wrappers:
withSecurityScheme allows you to add security metadata to any authentication handler.
- Predefined Security Schemes: Ready-to-use wrappers for common authentication types:
bearerAuth: JWT/Bearer token authentication
apiKeyAuth: API key authentication
basicAuth: Basic authentication
oauth2Auth: OAuth2 authentication
oidcAuth: OpenID Connect authentication
- Automatic Documentation: Security requirements are automatically included in OpenAPI documentation.
Basic Usage
import {bearerAuth} from 'expresskit';
import jwt from 'jsonwebtoken';
const jwtAuthHandler = bearerAuth('myJwtAuth')(function authenticate(req, res, next) {
next();
});
const routes = {
'GET /api/protected': {
handler: protectedRouteHandler,
authHandler: jwtAuthHandler,
},
};
Available Security Scheme Types
Bearer Token Authentication
const jwtAuthHandler = bearerAuth(
'jwtAuth',
['read:users', 'write:users'],
)(authFunction);
API Key Authentication
const apiKeyHandler = apiKeyAuth(
'apiKeyAuth',
'header',
'X-API-Key',
['read', 'write'],
)(authFunction);
Basic Authentication
const basicAuthHandler = basicAuth(
'basicAuth',
['read', 'write'],
)(authFunction);
OAuth2 Authentication
const oauth2Handler = oauth2Auth(
'oauth2Auth',
{
implicit: {
authorizationUrl: 'https://example.com/oauth/authorize',
scopes: {
read: 'Read access',
write: 'Write access',
},
},
},
['read', 'write'],
)(authFunction);
OpenID Connect Authentication
const oidcHandler = oidcAuth(
'oidcAuth',
'https://example.com/.well-known/openid-configuration',
['profile', 'email'],
)(authFunction);
Custom Security Schemes
If you need a custom security scheme, you can use the withSecurityScheme function directly:
import {withSecurityScheme} from 'expresskit';
const customAuthHandler = withSecurityScheme({
name: 'myCustomScheme',
scheme: {
type: 'http',
scheme: 'digest',
description: 'Digest authentication',
},
scopes: ['read', 'write'],
})(authFunction);
Styling Swagger UI
Customize the Swagger UI via swaggerUi options or by bringing in theme helpers such as swagger-themes:
import {SwaggerTheme, SwaggerThemeNameEnum} from 'swagger-themes';
const theme = new SwaggerTheme();
const {registerRoutes} = createOpenApiRegistry({
swaggerUi: {
explorer: true,
customCss: theme.getBuffer(SwaggerThemeNameEnum.DARK),
},
});
See src/example/index.ts for a more elaborate setup that combines authentication examples with custom styling.