openapi-express-types
v1.1.0
Published
Type-safe Express route handlers based on OpenAPI schemas
Maintainers
Readme
openapi-express-types
Type-safe Express route handlers based on OpenAPI schemas. This library provides compile-time type safety for Express routes using TypeScript types generated from OpenAPI specifications.
Features
- 🔒 Full type safety - Request bodies, query parameters, path parameters, and responses are all typed
- 🚀 Zero runtime overhead - Purely TypeScript types with no runtime validation
- 🔄 Automatic path conversion - Converts OpenAPI paths (
/users/{id}) to Express paths (/users/:id) - ⚡ Async error handling - Automatic Promise rejection handling for route handlers
- 🎯 IDE autocomplete - Full IntelliSense support for routes and parameters
- 🔧 Express compatible - Works with existing Express middleware and plugins
- 📦 Built-in CLI - Includes
openapi-typescriptfor generating types from your OpenAPI schema
Installation
npm install openapi-express-typesQuick Start
- Generate TypeScript types from your OpenAPI schema:
npx openapi-express-types generate schema/openapi.yml -o src/generated/schema.ts- Use the generated types with Express:
import express from 'express';
import { openapiExpress } from 'openapi-express-types';
import { paths } from './generated/schema';
const app = openapiExpress<paths>();
app.get('/users/{userId}', async (req, res) => {
// req.params.userId is typed as string
// res.json() expects the correct response type
const user = await getUserById(req.params.userId);
res.json(user);
});CLI Usage
The package includes a CLI wrapper around openapi-typescript:
# Generate types from a local file
npx openapi-express-types generate schema/openapi.yml -o src/generated/schema.ts
# Generate types from a URL
npx openapi-express-types generate https://api.example.com/openapi.json -o types.ts
# Show help
npx openapi-express-types --help
# Show openapi-typescript options
npx openapi-express-types generate --helpAPI Usage
Basic Example
import express from 'express';
import { openapiExpress } from 'openapi-express-types';
import { paths } from './generated/schema'; // Generated by the CLI
// Create a type-safe Express app
const app = openapiExpress<paths>();
// Type-safe route handlers
app.get('/users/{userId}', async (req, res) => {
// req.params.userId is typed as string
// res.json() expects the response type defined in your OpenAPI schema
const user = await getUserById(req.params.userId);
res.json(user); // Type-checked against OpenAPI response schema
});
app.post('/users', async (req, res) => {
// req.body is typed according to your OpenAPI requestBody schema
const newUser = await createUser(req.body);
res.status(201).json(newUser);
});
// Use regular Express middleware
app.use(express.json());
app.use('/health', (req, res) => res.send('OK'));
app.listen(3000);With Express OpenAPI Validator
This library pairs well with express-openapi-validator for runtime validation:
import express from 'express';
import OpenApiValidator from 'express-openapi-validator';
import { openapiExpress } from 'openapi-express-types';
import { paths } from './generated/schema';
const app = openapiExpress<paths>();
// Add runtime validation
app.use(express.json());
app.use(OpenApiValidator.middleware({
apiSpec: './schema/openapi.yml',
validateRequests: true,
validateResponses: true,
}));
// Routes have both compile-time and runtime safety
app.get('/users/{userId}', async (req, res) => {
const user = await getUserById(req.params.userId);
res.json(user);
});Complete Example with Error Handling
import express from 'express';
import { openapiExpress } from 'openapi-express-types';
import { paths } from './generated/schema';
const app = openapiExpress<paths>();
// Query parameters are typed
app.get('/users', async (req, res) => {
// req.query is typed based on OpenAPI parameters
const { limit, offset } = req.query;
const users = await getUsers({ limit, offset });
res.json(users);
});
// Path parameters are automatically extracted and typed
app.get('/users/{userId}/posts/{postId}', async (req, res) => {
// Both userId and postId are typed as string
const { userId, postId } = req.params;
const post = await getPost(userId, postId);
res.json(post);
});
// Request body is typed for mutations
app.put('/users/{userId}', async (req, res) => {
const { userId } = req.params;
// req.body is typed according to OpenAPI requestBody
const updatedUser = await updateUser(userId, req.body);
res.json(updatedUser);
});
// Error handling is automatic
app.delete('/users/{userId}', async (req, res) => {
await deleteUser(req.params.userId);
res.status(204).send(); // Default response codes based on method
});
// Global error handler
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: 'Internal server error' });
});API Reference
openapiExpress<P>(app?)
Creates a type-safe Express wrapper.
- P: The paths type generated by
openapi-typescript - app: Optional Express instance (creates new one if not provided)
Returns an object with typed route methods and Express compatibility methods.
Route Methods
All standard HTTP methods are supported with full type inference:
get(path, handler)post(path, handler)put(path, handler)delete(path, handler)patch(path, handler)options(path, handler)head(path, handler)
Handler Types
Route handlers receive typed req and res objects:
interface TypedRequest<Params, ResponseBody, RequestBody, Query> {
params: Params; // Path parameters
body: RequestBody; // Request body (for POST, PUT, PATCH)
query: Query; // Query parameters
// ... other Express request properties
}
interface TypedResponse<ResponseBody> {
json(body: ResponseBody): void;
// ... other Express response methods
}Default Response Codes
The library automatically maps HTTP methods to default response status codes:
- GET → 200
- POST → 201
- PUT → 200
- DELETE → 204
- PATCH → 200
- OPTIONS → 200
- HEAD → 200
Advanced Usage
Custom Response Status Codes
The library infers the response type based on the status code. If your OpenAPI schema defines different response schemas for different status codes, TypeScript will enforce the correct type:
app.post('/users', async (req, res) => {
try {
const user = await createUser(req.body);
res.status(201).json(user); // 201 response schema
} catch (error) {
res.status(400).json({ error: error.message }); // 400 response schema
}
});Using with Existing Express App
import express from 'express';
import { openapiExpress } from 'openapi-express-types';
const existingApp = express();
// ... configure existing app ...
const app = openapiExpress<paths>(existingApp);
// Now use typed routesIntegration with Authentication Middleware
const authMiddleware = (req, res, next) => {
// Authentication logic
next();
};
// Apply middleware before defining routes
app.use(authMiddleware);
// Or apply to specific routes
app.get('/protected/{id}', authMiddleware, async (req, res) => {
// Route handler with authentication
});TypeScript Configuration
Ensure your tsconfig.json has strict type checking enabled:
{
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}Tips and Best Practices
- Generate types regularly: Re-run
npx openapi-express-types generatewhenever your OpenAPI schema changes - Use with validation: Combine with
express-openapi-validatorfor runtime validation - Organize routes: Group related routes in separate files and import the typed app
- Error handling: The library wraps all handlers in Promise.resolve().catch(), so async errors are automatically caught
- Type imports: Import your generated types at the top of files for better readability
- Add to npm scripts: Add the generation command to your package.json:
{ "scripts": { "generate-types": "openapi-express-types generate schema/openapi.yml -o src/generated/schema.ts" } }
Example Project Structure
src/
├── api/
│ ├── routes/
│ │ ├── users.ts
│ │ └── posts.ts
│ └── server.ts
├── generated/
│ └── schema.ts # Generated by the CLI
├── services/
│ └── database.ts
└── index.ts
schema/
└── openapi.yml # Your OpenAPI specificationLicense
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
