@sleeksky/alt-swagger
Version:
A fluent, programmatic API for generating OpenAPI 3.0 specifications with TypeScript support. Define REST API endpoints, schemas, parameters, and security using a simple, chainable syntax.
605 lines (459 loc) • 13.6 kB
Markdown
# Alt Swagger
A fluent, programmatic API for generating OpenAPI 3.0 specifications. Quickly define your REST API endpoints with request/response schemas, parameters, security, and more using a simple, chainable syntax.
Built on top of [@sleeksky/alt-schema](https://github.com/sleeksky-dev/alt-schema) for intuitive JSON schema definitions.
## Features
- 🚀 **Fluent API**: Chain methods to define endpoints declaratively
- ⚡ **Path Shorthand**: Define query, headers, body, and tags directly in the path (`/users?limit:i^auth??{name:s}#Users`)
- 📝 **Schema-first**: Define request/response schemas using simple string syntax
- 🔒 **Security**: Built-in support for authentication schemes
- 🏷️ **Rich Metadata**: Tags, summaries, descriptions, deprecation
- 🔄 **Reusable Schemas**: Define and reference schemas across endpoints
- 🎯 **TypeScript Support**: Full TypeScript definitions included
- 📊 **OpenAPI 3.0**: Generates valid OpenAPI 3.0 specifications
## Installation
```bash
npm install @sleeksky/alt-swagger
# or
yarn add @sleeksky/alt-swagger
# or
pnpm add @sleeksky/alt-swagger
```
## Quick Start
```javascript
const swagger = require('@sleeksky/alt-swagger');
// Define your API
swagger.server('https://api.example.com', 'Production API');
// Define endpoints with shorthand notation
swagger.get('/users?limit:i:10,offset:i:0#Users')
.summary('Get users')
.res(200, '[{id:i,name:s,email:s}]');
swagger.post('/users#Users')
.summary('Create user')
.req('{name:s,email:s}')
.res(201, '{id:i,name:s,email:s}')
.res(400, '{error:s}');
// Generate OpenAPI spec
const spec = swagger.swaggerDoc('User API', '1.0.0');
console.log(JSON.stringify(spec, null, 2));
```
## Schema Syntax
Alt Swagger uses a compact string syntax for defining JSON schemas, powered by alt-schema:
### Basic Types
- `s` - string
- `i` - integer
- `n` - number
- `b` - boolean
- `o` - object
- `a` - array
### Examples
```javascript
// Simple object
'{name:s, age:i}'
// With default values
'{name:s:John, age:i:25}'
// Optional fields (prefixed with ?)
'{name:s, age:?i, email:?s}'
// Nested objects
'{user:{name:s, age:i}, active:b}'
// Arrays
'[{id:i, name:s}]'
// Complex nested structure
'{id:i, profile:{name:s, settings:{theme:s:dark, notifications:b:true}}}'
```
## API Reference
### Core Methods
#### `swagger.server(url, description?)`
Define API servers.
```javascript
swagger.server('https://api.example.com', 'Production');
swagger.server('https://staging.api.example.com', 'Staging');
```
#### `swagger.tag(name, description?)`
Define tags for grouping endpoints.
```javascript
swagger.tag('Users', 'User management endpoints');
swagger.tag('Posts', 'Blog post endpoints');
```
### HTTP Methods
All HTTP methods return a fluent API for chaining:
#### `swagger.get(path)`
#### `swagger.post(path)`
#### `swagger.put(path)`
#### `swagger.patch(path)`
#### `swagger.del(path)` (or `swagger.delete(path)`)
```javascript
swagger.get('/users/{id}')
.summary('Get user by ID')
.res(200, '{id:i, name:s, email:s}')
.res(404, '{error:s}');
```
### Path Parameters
Parameters in curly braces are automatically detected:
```javascript
// /users/{id} automatically creates path parameter 'id'
swagger.get('/users/{id}')
.res(200, '{id:i, name:s}');
// With default values
swagger.get('/users/{id:123}')
.res(200, '{id:i, name:s}');
```
### Path Shorthand Notation
You can define query parameters, headers, request body, and tags directly in the path string for more concise endpoint definitions.
**Format**: `path?query^header??requestBody#tag`
#### Query Parameters (`?`)
Add query parameters after `?` in the path:
```javascript
// Shorthand for .query('limit:i:10,offset:i:0')
swagger.get('/users?limit:i:10,offset:i:0')
.res(200, '[{id:i, name:s}]');
// Equivalent to:
swagger.get('/users')
.query('limit:i:10,offset:i:0')
.res(200, '[{id:i, name:s}]');
```
#### Header Parameters (`^`)
Add header parameters after `^` in the path:
```javascript
// Shorthand for .header('authorization,x-api-key:?')
swagger.get('/users^authorization,x-api-key:?')
.res(200, '[{id:i, name:s}]');
// Equivalent to:
swagger.get('/users')
.header('authorization,x-api-key:?')
.res(200, '[{id:i, name:s}]');
```
#### Request Body (`??`)
Add request body schema after `??` in the path:
```javascript
// Shorthand for .req('{name:s,email:s}')
swagger.post('/users??{name:s,email:s}')
.res(201, '{id:i, name:s, email:s}');
// Equivalent to:
swagger.post('/users')
.req('{name:s,email:s}')
.res(201, '{id:i, name:s, email:s}');
```
#### Tags (`#`)
Add tags after `#` in the path (must be last):
```javascript
// Shorthand for .tag('Users')
swagger.get('/users#Users')
.res(200, '[{id:i, name:s}]');
// Equivalent to:
swagger.get('/users')
.tag('Users')
.res(200, '[{id:i, name:s}]');
```
#### Combined Shorthand
Use all shorthand notations together:
```javascript
// Full shorthand: path?query^header??body#tag
swagger.post('/users?notify:b^authorization??{name:s,email:s}#Users')
.summary('Create user')
.res(201, '{id:i, name:s, email:s}');
// Works with path parameters too
swagger.put('/users/{id}?fields:s^x-request-id??{name:s}#Users')
.res(200, '{id:i, name:s}');
// Query and tag only
swagger.get('/users?limit:i:10,offset:i:0#Users')
.summary('List users')
.res(200, '[{id:i, name:s}]');
// Header and tag only
swagger.get('/secure^authorization#Auth')
.res(200, '{data:s}');
```
**Note**:
- Tag (`#`) must always be last in the shorthand string
- Explicit method calls (`.query()`, `.header()`, `.req()`, `.tag()`) take precedence over path shorthand if both are used
### Fluent API Methods
#### `.req(schema)`
Define request body schema.
```javascript
swagger.post('/users')
.req('{name:s, email:s, age:?i}')
.res(201, '{id:i, name:s, email:s}');
```
#### `.res(statusCode, schema)`
Define response schema for a status code.
```javascript
swagger.get('/users')
.res(200, '[{id:i, name:s}]')
.res(404, '{error:s}')
.res(500, '{error:s, code:i}');
```
#### `.query(params)`
Add query parameters. Accepts string or array.
```javascript
// Single parameter
swagger.get('/users').query('limit:i:10');
// Multiple parameters (comma-separated)
swagger.get('/users').query('limit:i:10,offset:i:0');
// Array format
swagger.get('/users').query(['limit:i:10', 'offset:i:0']);
// Optional parameters
swagger.get('/users').query('search:?s');
```
#### `.header(params)`
Add header parameters.
```javascript
swagger.get('/users')
.header('authorization')
.header('x-api-key:?');
```
#### `.tag(name)`
Add tags to the endpoint.
```javascript
swagger.get('/users')
.tag('Users')
.tag('Public');
```
#### `.summary(text)`
Add summary to the endpoint.
```javascript
swagger.get('/users')
.summary('Retrieve a list of users');
```
#### `.desc(text)` or `.description(text)`
Add description to the endpoint.
```javascript
swagger.get('/users')
.desc('Returns a paginated list of users with optional filtering');
```
#### `.security(name)`
Apply security scheme to the endpoint.
```javascript
swagger.get('/users')
.security('bearerAuth');
```
#### `.deprecate()`
Mark endpoint as deprecated.
```javascript
swagger.get('/old-endpoint')
.deprecate()
.res(200, '{message:s}');
```
### Schema Management
#### `swagger.ref.schema(name, schema)`
Define reusable schemas.
```javascript
const userSchema = swagger.ref.schema('User', '{id:i, name:s, email:s}');
const errorSchema = swagger.ref.schema('Error', '{error:s, code:i}');
swagger.get('/users')
.res(200, userSchema)
.res(500, errorSchema);
```
### Security
#### `swagger.security(name, options?)`
Define security schemes.
```javascript
// Bearer token
swagger.security('bearerAuth');
// API Key
swagger.security('apiKey', {
type: 'apiKey',
in: 'header',
name: 'X-API-Key'
});
// Basic auth
swagger.security('basicAuth', {
type: 'http',
scheme: 'basic'
});
```
### Document Generation
#### `swagger.swaggerDoc(title?, version?)`
Generate the complete OpenAPI specification.
```javascript
const spec = swagger.swaggerDoc('My API', '1.0.0');
// Returns OpenAPI 3.0 compliant object
```
### Utility Methods
#### `swagger.reset()`
Clear all defined specifications.
```javascript
swagger.reset(); // Start fresh
```
#### `swagger.remove(options)`
Remove endpoints.
```javascript
// Remove specific path
swagger.remove({ path: '/users' });
// Remove by tag
swagger.remove({ tag: 'Deprecated' });
```
## Complete Examples
### Basic CRUD API
```javascript
const swagger = require('@sleeksky/alt-swagger');
swagger.server('https://api.example.com', 'User Management API');
swagger.tag('Users', 'User operations');
// Define schemas
const userSchema = swagger.ref.schema('User', '{id:i, name:s, email:s, created_at:s}');
const createUserSchema = swagger.ref.schema('CreateUser', '{name:s, email:s}');
const errorSchema = swagger.ref.schema('Error', '{error:s, code:i}');
// Security
swagger.security('bearerAuth');
// Endpoints
swagger.get('/users')
.tag('Users')
.summary('List users')
.security('bearerAuth')
.query('limit:?i:10,offset:?i:0')
.res(200, `[${userSchema}]`)
.res(401, errorSchema);
swagger.post('/users')
.tag('Users')
.summary('Create user')
.req(createUserSchema)
.res(201, userSchema)
.res(400, errorSchema);
swagger.get('/users/{id}')
.tag('Users')
.summary('Get user by ID')
.security('bearerAuth')
.res(200, userSchema)
.res(404, errorSchema);
swagger.put('/users/{id}')
.tag('Users')
.summary('Update user')
.security('bearerAuth')
.req(createUserSchema)
.res(200, userSchema)
.res(400, errorSchema)
.res(404, errorSchema);
swagger.del('/users/{id}')
.tag('Users')
.summary('Delete user')
.security('bearerAuth')
.res(204)
.res(404, errorSchema);
const spec = swagger.swaggerDoc('User API', '1.0.0');
```
### Express Integration
```javascript
const express = require('express');
const swaggerUi = require('swagger-ui-express');
const swagger = require('@sleeksky/alt-swagger');
const app = express();
// Define API spec
swagger.server('http://localhost:3000', 'Local API');
swagger.get('/health')
.summary('Health check')
.res(200, '{status:s, timestamp:s}');
swagger.get('/users/{id}')
.summary('Get user')
.res(200, '{id:i, name:s}');
// Serve Swagger UI
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swagger.swaggerDoc('My API')));
// API routes
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
app.get('/users/:id', (req, res) => {
const user = { id: parseInt(req.params.id), name: 'John Doe' };
res.json(user);
});
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
console.log('API docs at http://localhost:3000/api-docs');
});
```
### Advanced Schema Examples
```javascript
const swagger = require('@sleeksky/alt-swagger');
// Complex nested schemas
const profileSchema = swagger.ref.schema('Profile', `
{
personal: {
firstName: s,
lastName: s,
age: ?i,
email: s
},
preferences: {
theme: s:light,
notifications: {
email: b:true,
push: b:false
}
},
tags: [s]
}
`);
// Array responses
swagger.get('/posts')
.summary('Get blog posts')
.query('category:?s,tag:?s,published:?b:true')
.res(200, `
[{
id: i,
title: s,
content: s,
author: {id:i, name:s},
tags: [s],
published: b,
created_at: s
}]
`);
// Union-like responses (use oneOf in OpenAPI)
swagger.post('/upload')
.summary('Upload file')
.req('{file:s, type:s}') // In practice, use multipart/form-data
.res(200, '{id:s, url:s, size:i}')
.res(400, '{error:s, code:s}');
```
## TypeScript Support
Alt Swagger includes full TypeScript definitions:
```typescript
import * as swagger from '@sleeksky/alt-swagger';
swagger.server('https://api.example.com');
swagger.get('/users').res(200, '[{id:number, name:string}]');
const spec: swagger.SwaggerDoc = swagger.swaggerDoc('API');
```
## Integration with Frameworks
### Fastify
```javascript
const fastify = require('fastify')();
const swagger = require('@sleeksky/alt-swagger');
// Define spec
swagger.server('http://localhost:3000');
swagger.get('/users').res(200, '[{id:i, name:s}]');
// Register Swagger
fastify.register(require('@fastify/swagger'), {
specification: {
document: swagger.swaggerDoc('Fastify API')
}
});
```
### Hapi
```javascript
const Hapi = require('@hapi/hapi');
const swagger = require('@sleeksky/alt-swagger');
const server = Hapi.server({ port: 3000 });
// Define spec
swagger.server('http://localhost:3000');
swagger.get('/users').res(200, '[{id:i, name:s}]');
// Use hapi-swagger
server.register([
require('hapi-swagger'),
{
options: {
documentationPage: true,
swaggerOptions: {
spec: swagger.swaggerDoc('Hapi API')
}
}
}
]);
```
## Best Practices
1. **Define schemas first**: Use `swagger.ref.schema()` for reusable schemas
2. **Use meaningful tags**: Group related endpoints with tags
3. **Document thoroughly**: Always include summaries and descriptions
4. **Handle errors**: Define error responses for all endpoints
5. **Version your API**: Use semantic versioning in `swaggerDoc()`
6. **Validate schemas**: Test your generated specs with OpenAPI validators
## Contributing
Contributions are welcome! Please feel free to submit issues and pull requests.
## License
MIT © [SleekSky](https://sleeksky.com)