API Documentation Generation
This document explains how to generate and use the OpenAPI specification for the DeployStack Backend API.
Overview
The DeployStack Backend uses Fastify with Swagger plugins to automatically generate OpenAPI 3.0 specifications. Route schemas are defined using reusable JSON Schema constants for type safety and documentation. This provides:
- Interactive Documentation: Swagger UI interface for testing APIs
- Postman Integration: JSON/YAML specs that can be imported into Postman
- Automated Generation: Specifications are generated from actual route code
- Type Safety: TypeScript interfaces provide compile-time checking
🔒 Security First
IMPORTANT: Before developing any protected API endpoints, read the API Security Best Practices documentation. It covers critical security patterns including:
- Authorization Before Validation: Why
preValidation
must be used instead ofpreHandler
for authorization - Proper Error Responses: Ensuring unauthorized users get 403 Forbidden, not validation errors
- Security Testing: How to test authorization properly
- Common Pitfalls: Security anti-patterns to avoid
Key Rule: Always use preValidation
for authorization checks to prevent information disclosure to unauthorized users.
🔐 Dual Authentication Support
The DeployStack Backend supports dual authentication for API endpoints, allowing both web users (cookie-based) and CLI users (OAuth2 Bearer tokens) to access the same endpoints seamlessly.
Authentication Methods
-
Cookie Authentication (Web Users)
- Session-based authentication using HTTP cookies
- Automatic for web browser requests
- Uses
session
cookie set during login
-
OAuth2 Bearer Token Authentication (CLI Users)
- RFC 6749 compliant OAuth2 implementation with PKCE
- Uses
Authorization: Bearer <token>
header - Scope-based access control
Dual Authentication Middleware
Use these middleware functions to enable dual authentication on endpoints:
import { requireAuthenticationAny, requireOAuthScope } from '../../middleware/oauthMiddleware';
export default async function yourRoute(server: FastifyInstance) {
server.get('/your-endpoint', {
preValidation: [
requireAuthenticationAny(), // Accept either auth method
requireOAuthScope('your:scope') // Enforce OAuth2 scope
],
schema: {
security: [
{ cookieAuth: [] }, // Cookie authentication
{ bearerAuth: [] } // OAuth2 Bearer token
]
}
}, async (request, reply) => {
// Endpoint accessible via both authentication methods
const authType = request.tokenPayload ? 'oauth2' : 'cookie';
const userId = request.user!.id;
});
}
OAuth2 Scopes
Available OAuth2 scopes for fine-grained access control:
mcp:read
- Read MCP server installations and configurationsaccount:read
- Read account informationuser:read
- Read user profile informationteams:read
- Read team memberships and informationoffline_access
- Maintain access when not actively using the application
OAuth2 Flow Endpoints
GET /api/oauth2/auth
- Authorization endpoint (PKCE required)GET /api/oauth2/consent
- User consent pagePOST /api/oauth2/consent
- Process consent decisionPOST /api/oauth2/token
- Token exchange endpoint
Client Configuration
- Client ID:
deploystack-gateway-cli
- Redirect URIs:
http://localhost:8976/oauth/callback
,http://127.0.0.1:8976/oauth/callback
- PKCE: Required (SHA256 method)
- Token Lifetime: 1 hour access tokens, 30 day refresh tokens
Usage Examples
Web Users (Cookie Authentication):
curl -b cookies.txt "http://localhost:3000/api/teams/me/default"
CLI Users (OAuth2 Bearer Token):
curl -H "Authorization: Bearer <access_token>" \
"http://localhost:3000/api/teams/me/default"
Available Commands
1. Generate Complete API Specification
npm run api:spec
This command:
- Starts a temporary server
- Generates both JSON and YAML specifications
- Saves files to
api-spec.json
andapi-spec.yaml
- Provides URLs for interactive documentation
- Automatically shuts down the server
Output:
api-spec.json
- OpenAPI JSON specification (for Postman import)api-spec.yaml
- OpenAPI YAML specification
2. Generate JSON Specification (requires running server)
npm run api:spec:json
Requires the development server to be running (npm run dev
).
3. Generate YAML Specification (requires running server)
npm run api:spec:yaml
Requires the development server to be running (npm run dev
).
Usage Examples
Complete Generation (Recommended)
cd services/backend
npm run api:spec
Manual Generation with Running Server
# Terminal 1: Start the server
cd services/backend
npm run dev
# Terminal 2: Generate specifications
npm run api:spec:json
npm run api:spec:yaml
Accessing Documentation
When the server is running (npm run dev
), you can access:
- Interactive Docs: http://localhost:3000/documentation
- JSON Spec: http://localhost:3000/documentation/json
- YAML Spec: http://localhost:3000/documentation/yaml
Importing into Postman
- Run
npm run api:spec
to generate the specification - Open Postman
- Click "Import"
- Select the generated
api-spec.json
file - All API endpoints will be imported with proper documentation
Route File Structure Rules
IMPORTANT: Every new API endpoint must be created in a separate file following the established directory structure pattern. Do not add route definitions directly to src/routes/index.ts
.
File Structure Requirements
- Separate Files: Each route or group of related routes must be in its own file
- Directory Organization: Group related routes in directories (e.g.,
/auth/
,/users/
,/health/
) - Import Pattern: Routes are imported and registered in
src/routes/index.ts
- Consistent Naming: Use descriptive names that match the route purpose
- Modular Approach: Keep route files small and focused - aim for 1-3 related methods per file maximum
- Maintainability: Avoid large monolithic route files that become difficult to maintain
Correct File Structure
services/backend/src/routes/
├── index.ts # Main routes registration (imports only)
├── health/
│ └── index.ts # Health check endpoints
├── auth/
│ ├── loginEmail.ts # Email login endpoint
│ ├── registerEmail.ts # Email registration endpoint
│ └── logout.ts # Logout endpoint
├── db/
│ ├── status.ts # Database status endpoint
│ └── setup.ts # Database setup endpoint
├── users/
│ └── index.ts # User management endpoints
└── teams/
└── index.ts # Team management endpoints
Modular Route Organization (Recommended)
For complex feature areas, break down routes into smaller, focused files:
services/backend/src/routes/mcp/
├── index.ts # Route registration only
├── categories/
│ ├── create.ts # POST /api/mcp/categories (1 method)
│ ├── update.ts # PUT /api/mcp/categories/{id} (1 method)
│ └── delete.ts # DELETE /api/mcp/categories/{id} (1 method)
├── servers/
│ ├── list.ts # GET /api/mcp/servers (1 method)
│ ├── get.ts # GET /api/mcp/servers/{id} (1 method)
│ ├── search.ts # GET /api/mcp/servers/search (1 method)
│ ├── create-global.ts # POST /api/mcp/servers/global (1 method)
│ ├── update-global.ts # PUT /api/mcp/servers/global/{id} (1 method)
│ └── delete-global.ts # DELETE /api/mcp/servers/global/{id} (1 method)
└── versions/
├── list.ts # GET /api/mcp/servers/{id}/versions (1 method)
├── create.ts # POST /api/mcp/servers/{id}/versions (1 method)
└── update.ts # PUT /api/mcp/servers/{id}/versions/{versionId} (1 method)
Benefits of Modular Approach:
- Easier Maintenance: Small files are easier to understand and modify
- Better Testing: Individual route files can be tested in isolation
- Team Collaboration: Multiple developers can work on different routes without conflicts
- Clear Responsibility: Each file has a single, clear purpose
- Reduced Complexity: Avoid hundreds of lines in single files
Shared Schemas for CRUD Modules (Mandatory)
MANDATORY PATTERN: For route directories that implement complete CRUD operations on a single entity (Create, Read, Update, Delete), you must create a shared schemas.ts
file to eliminate duplication and ensure consistency.
When to Create Shared Schemas
Create a schemas.ts
file when your route directory contains:
- Multiple endpoints operating on the same core entity
- Duplicate schema definitions across route files
- Common response structures (error responses, entity objects)
- Shared parameter validation (ID parameters, common fields)
What to Include in Shared Schemas
Always Share:
- Error response schemas that appear in multiple files
- Core entity object schemas used in responses
- Common parameter schemas (ID validation, shared fields)
- Shared TypeScript interfaces for consistent typing
Keep Endpoint-Specific:
- Request body schemas with different requirements (create vs. update)
- Endpoint-specific success response wrappers
- Unique validation rules specific to individual operations
Implementation Requirements
- File Naming: Use
schemas.ts
in the route directory - Export Pattern: Export const schemas and TypeScript interfaces
- Import Pattern: Import shared components into individual route files
- Documentation: Include clear comments explaining shared vs. specific schemas
Benefits of Shared Schemas
- Single Source of Truth: Entity structure defined once
- Elimination of Duplication: Removes copy-pasted schema definitions
- Improved Maintainability: Changes happen in one place
- Enhanced Consistency: All endpoints use identical shared structures
- Better Type Safety: Shared interfaces prevent type drift
Module Examples Requiring Shared Schemas
/mcp/categories/
- Complete category management CRUD/mcp/servers/
- Server management operations/teams/
- Team management functionality- Any directory with 3+ routes operating on the same entity
Route File Template
Each route file should follow this recommended pattern:
import { type FastifyInstance } from 'fastify'
// Reusable Schema Constants
const REQUEST_SCHEMA = {
type: 'object',
properties: {
name: {
type: 'string',
minLength: 1,
description: 'Name is required'
},
email: {
type: 'string',
format: 'email',
description: 'Valid email required'
}
},
required: ['name', 'email'],
additionalProperties: false
} as const;
const SUCCESS_RESPONSE_SCHEMA = {
type: 'object',
properties: {
success: { type: 'boolean' },
message: { type: 'string' }
},
required: ['success', 'message']
} as const;
const ERROR_RESPONSE_SCHEMA = {
type: 'object',
properties: {
success: { type: 'boolean', default: false },
error: { type: 'string' }
},
required: ['success', 'error']
} as const;
// TypeScript interfaces for type safety
interface RequestBody {
name: string;
email: string;
}
interface SuccessResponse {
success: boolean;
message: string;
}
interface ErrorResponse {
success: boolean;
error: string;
}
export default async function yourRoute(server: FastifyInstance) {
server.post('/your-endpoint', {
preValidation: requirePermission('your.permission'), // Authorization FIRST
schema: {
tags: ['Your Category'],
summary: 'Brief description',
description: 'Detailed description. Requires Content-Type: application/json header when sending request body.',
security: [{ cookieAuth: [] }],
// Fastify validation schema
body: REQUEST_SCHEMA,
// OpenAPI documentation (same schema, reused)
requestBody: {
required: true,
content: {
'application/json': {
schema: REQUEST_SCHEMA
}
}
},
response: {
200: {
...SUCCESS_RESPONSE_SCHEMA,
description: 'Success'
},
400: {
...ERROR_RESPONSE_SCHEMA,
description: 'Bad Request'
},
401: {
...ERROR_RESPONSE_SCHEMA,
description: 'Unauthorized'
},
403: {
...ERROR_RESPONSE_SCHEMA,
description: 'Forbidden'
}
}
}
}, async (request, reply) => {
// TypeScript type assertion (Fastify has already validated)
const { name, email } = request.body as RequestBody;
// Your route logic
const successResponse: SuccessResponse = {
success: true,
message: `User ${name} processed successfully`
};
const jsonString = JSON.stringify(successResponse);
return reply.status(200).type('application/json').send(jsonString);
});
}
Key Requirements:
- ✅ Parameter name:
server: FastifyInstance
(notfastify
) - ✅ Method calls:
server.post()
(notfastify.post()
) - ✅ preValidation first: Authorization before schema
- ✅ Reusable schemas: Define schema constants at the top
- ✅ Single source: Same schema for both
body
andrequestBody
- ✅ TypeScript interfaces: Provide type safety
- ✅ Manual JSON serialization: Use
JSON.stringify()
for responses
Registration in index.ts
Import and register your route in src/routes/index.ts
:
// Import your route
import yourRoute from './your-directory'
export const registerRoutes = (server: FastifyInstance): void => {
server.register(async (apiInstance) => {
// Register your route
await apiInstance.register(yourRoute);
// Other route registrations...
}, { prefix: '/api' });
}
❌ What NOT to Do
// DON'T: Add routes directly to index.ts
export const registerRoutes = (server: FastifyInstance): void => {
server.register(async (apiInstance) => {
// ❌ BAD: Inline route definition
apiInstance.get('/my-endpoint', {
schema: { /* ... */ }
}, async () => {
return { message: 'This should be in a separate file!' }
});
}, { prefix: '/api' });
}
✅ What TO Do
// ✅ GOOD: Import and register separate route files
import myEndpointRoute from './my-endpoint'
export const registerRoutes = (server: FastifyInstance): void => {
server.register(async (apiInstance) => {
// ✅ GOOD: Register imported route
await apiInstance.register(myEndpointRoute);
}, { prefix: '/api' });
}
Benefits of This Structure
- Maintainability: Each endpoint is self-contained and easy to find
- Scalability: Adding new endpoints doesn't clutter the main routes file
- Testing: Individual route files can be tested in isolation
- Code Organization: Related functionality is grouped together
- Team Collaboration: Multiple developers can work on different routes without conflicts
Content-Type Header Requirements
When to Include Content-Type Headers
IMPORTANT: The Content-Type: application/json
header is required for specific HTTP methods when sending request body data.
✅ ALWAYS Include Content-Type for:
- POST requests with request body data
- PUT requests with request body data
- PATCH requests with request body data
❌ NEVER Include Content-Type for:
- GET requests (no request body)
- DELETE requests (typically no request body)
- HEAD requests (no request body)
Correct Client Implementation Pattern
function makeRequest(method, path, data = null, cookies = null) {
const options = {
method,
headers: { 'Accept': 'application/json' }
};
// Set Content-Type for methods that send request body data
if (['POST', 'PUT', 'PATCH'].includes(method.toUpperCase()) && data !== null) {
options.headers['Content-Type'] = 'application/json';
}
// Rest of implementation...
}
❌ Problematic Pattern (Avoid This)
// UNCLEAR: This doesn't indicate WHICH methods need Content-Type
if (data) {
options.headers['Content-Type'] = 'application/json';
}
API Specification Content-Type Documentation
When defining route schemas, explicitly document Content-Type requirements for POST/PUT/PATCH endpoints:
// For endpoints that require Content-Type
const routeSchema = {
tags: ['Category'],
summary: 'Create new item',
description: 'Creates a new item. Requires Content-Type: application/json header when sending request body.',
requestBody: {
required: true,
content: {
'application/json': {
schema: REQUEST_SCHEMA
}
}
},
// ... rest of schema
};
Adding Documentation to Routes
The DeployStack Backend uses reusable JSON Schema constants for both validation and documentation generation. This approach provides a single source of truth for API schemas.
Recommended Approach: Reusable Schema Constants
import { type FastifyInstance } from 'fastify';
// 1. Define reusable JSON Schema constants
const REQUEST_SCHEMA = {
type: 'object',
properties: {
name: {
type: 'string',
minLength: 3,
description: 'The name of the item (min 3 chars)'
},
count: {
type: 'number',
minimum: 1,
description: 'How many items (must be positive)'
},
type: {
type: 'string',
enum: ['mysql', 'sqlite'],
description: 'Database engine type'
}
},
required: ['name', 'count', 'type'],
additionalProperties: false
} as const;
const SUCCESS_RESPONSE_SCHEMA = {
type: 'object',
properties: {
success: {
type: 'boolean',
description: 'Indicates if the operation was successful'
},
itemId: {
type: 'string',
description: 'The UUID of the created/affected item'
},
message: {
type: 'string',
description: 'Optional success message'
}
},
required: ['success', 'itemId']
} as const;
const ERROR_RESPONSE_SCHEMA = {
type: 'object',
properties: {
success: {
type: 'boolean',
default: false,
description: 'Indicates failure'
},
error: {
type: 'string',
description: 'Error message detailing what went wrong'
}
},
required: ['success', 'error']
} as const;
// 2. Define TypeScript interfaces for type safety
interface RequestBody {
name: string;
count: number;
type: 'mysql' | 'sqlite';
}
interface SuccessResponse {
success: boolean;
itemId: string;
message?: string;
}
interface ErrorResponse {
success: boolean;
error: string;
}
// 3. Use schemas in route definition
export default async function yourRoute(server: FastifyInstance) {
server.post('/your-route', {
preValidation: requirePermission('your.permission'),
schema: {
tags: ['Category'], // Your API category
summary: 'Brief description of your endpoint',
description: 'Detailed description of what this endpoint does, its parameters, and expected outcomes. Requires Content-Type: application/json header when sending request body.',
security: [{ cookieAuth: [] }], // Include if authentication is required
// Single schema for both validation AND documentation
body: REQUEST_SCHEMA,
// OpenAPI documentation (same schema, reused)
requestBody: {
required: true,
content: {
'application/json': {
schema: REQUEST_SCHEMA
}
}
},
response: {
200: {
...SUCCESS_RESPONSE_SCHEMA,
description: 'Successful operation'
},
400: {
...ERROR_RESPONSE_SCHEMA,
description: 'Bad Request - Invalid input'
},
401: {
...ERROR_RESPONSE_SCHEMA,
description: 'Unauthorized'
},
403: {
...ERROR_RESPONSE_SCHEMA,
description: 'Forbidden'
}
}
}
}, async (request, reply) => {
// TypeScript type assertion (Fastify has already validated)
const { name, count, type } = request.body as RequestBody;
// Your route handler logic here
const successResponse: SuccessResponse = {
success: true,
itemId: 'some-uuid-v4-here',
message: `Item ${name} processed successfully with ${count} items using ${type}.`
};
const jsonString = JSON.stringify(successResponse);
return reply.status(200).type('application/json').send(jsonString);
});
}
Key Benefits of This Approach
- Single Source of Truth: JSON Schema constants define both validation AND documentation
- Automatic Validation: Fastify automatically validates requests before your handler runs
- No Manual Validation: Remove all manual validation calls and field checks
- Better Error Messages: Fastify provides detailed validation errors automatically
- Type Safety: Handlers receive properly typed, validated data
- Cleaner Code: No redundant validation logic in handlers
- Schema Reuse: Same schema serves both validation and documentation
JSON Response Serialization Pattern
CRITICAL: All API responses must use manual JSON serialization to ensure consistent JSON output.
Required Response Pattern
// ✅ CORRECT: Manual JSON serialization
const successResponse: SuccessResponse = {
success: true,
message: 'Operation completed successfully',
data: { /* your data */ }
};
const jsonString = JSON.stringify(successResponse);
return reply.status(200).type('application/json').send(jsonString);
What NOT to Do
// ❌ WRONG: Direct object response (can cause serialization issues)
return reply.status(200).send({
success: true,
message: 'This might not serialize correctly'
});
// ❌ WRONG: Using reply.send() without JSON.stringify()
const response = { success: true, message: 'Test' };
return reply.status(200).send(response);
Error Response Pattern
All error responses must also use manual JSON serialization:
// ✅ CORRECT: Error response with manual serialization
const errorResponse: ErrorResponse = {
success: false,
error: 'Detailed error message'
};
const jsonString = JSON.stringify(errorResponse);
return reply.status(400).type('application/json').send(jsonString);
Authentication Middleware Pattern
Authentication middleware and hooks must also use this pattern:
// ✅ CORRECT: Authentication error with manual serialization
const errorResponse = {
success: false,
error: 'Unauthorized: Authentication required.'
};
const jsonString = JSON.stringify(errorResponse);
return reply.status(401).type('application/json').send(jsonString);
Why This Pattern is Required
The manual JSON serialization pattern ensures:
- ✅ Consistent, parseable JSON responses
- ✅ Proper
success
/error
properties in all responses - ✅ Reliable client-server communication
- ✅ Passing e2e tests
- ✅ No
"[object Object]"
serialization issues
Why Both body
and requestBody
Properties?
Important: You need BOTH properties for complete functionality:
body
: Enables Fastify's automatic request validation using the JSON schemarequestBody
: Ensures proper OpenAPI specification generation with Content-Type documentation
Without body
, validation won't work. Without requestBody
, your API specification won't properly document the application/json
Content-Type requirement.
What NOT to Do (Anti-patterns)
❌ Don't do manual validation in handlers:
// BAD: Manual validation (redundant)
if (!request.body.name || !request.body.count) {
return reply.status(400).send({ error: 'Required fields missing' });
}
// BAD: Manual field checks (redundant)
if (request.body.name.length < 3) {
return reply.status(400).send({ error: 'Name too short' });
}
// BAD: Manual enum validation (redundant)
if (request.body.type !== 'mysql' && request.body.type !== 'sqlite') {
return reply.status(400).send({ error: 'Invalid database type' });
}
✅ Do trust Fastify's automatic validation:
// GOOD: Trust the validation - if handler runs, data is valid
const { name, count, type } = request.body as RequestBody; // Already validated by Fastify
Validation Flow
The validation chain works as follows:
JSON Schema → Fastify Validation → Handler
- JSON Schema: Define validation rules using JSON Schema
- Fastify Validation: Fastify automatically validates incoming requests
- Handler: Receives validated, typed data
If validation fails, Fastify automatically returns a 400 error before your handler runs.
Real-World Examples
See these files for complete examples of proper validation:
src/routes/admin/email/test.ts
- REFERENCE IMPLEMENTATION showing complete reusable schema constants approachsrc/routes/db/setup.ts
- Database setup with enum validationsrc/routes/db/status.ts
- Simple GET endpoint with response schemassrc/routes/auth/loginEmail.ts
- Login with required string fieldssrc/routes/auth/registerEmail.ts
- Registration with complex validation rules
Example: Logout Route Documentation
The logout route (/api/auth/logout
) demonstrates proper documentation:
const LOGOUT_RESPONSE_SCHEMA = {
type: 'object',
properties: {
success: {
type: 'boolean',
description: 'Indicates if the logout operation was successful'
},
message: {
type: 'string',
description: 'Human-readable message about the logout result'
}
},
required: ['success', 'message'],
examples: [
{
success: true,
message: 'Logged out successfully.'
}
]
} as const;
const logoutSchema = {
tags: ['Authentication'],
summary: 'User logout',
description: 'Invalidates the current user session and clears authentication cookies',
security: [{ cookieAuth: [] }],
response: {
200: LOGOUT_RESPONSE_SCHEMA
}
};
Configuration
Fastify Server Configuration
The Fastify server is configured with custom AJV options to ensure compatibility with JSON Schema validation. This configuration is in src/server.ts
:
const server = fastify({
logger: loggerConfig,
disableRequestLogging: true,
ajv: {
customOptions: {
strict: false, // Allows unknown keywords in schemas
strictTypes: false, // Disables strict type checking
strictTuples: false // Disables strict tuple checking
}
}
})
Why these AJV options are required:
strict: false
: AJV v8+ runs in strict mode by default, which rejects schemas containing unknown keywords. This setting allows more flexible schema definitions.strictTypes: false
: Prevents strict type validation errors that can occur with complex schemas.strictTuples: false
: Allows more flexible tuple handling for array schemas.
Important: These settings don't affect validation behavior - they only allow the schema compilation to succeed. All validation rules defined in your JSON schemas still work exactly as expected.
Swagger Configuration
The Swagger documentation configuration is also in src/server.ts
:
await server.register(fastifySwagger, {
openapi: {
openapi: '3.0.0',
info: {
title: 'DeployStack Backend API',
description: 'API documentation for DeployStack Backend',
version: '0.20.5'
},
servers: [
{
url: 'http://localhost:3000',
description: 'Development server'
}
],
components: {
securitySchemes: {
cookieAuth: {
type: 'apiKey',
in: 'cookie',
name: 'auth_session'
}
}
}
}
});
Troubleshooting
"Route already declared" Error
This happens when trying to manually add routes that Swagger UI already provides. The /documentation/json
and /documentation/yaml
endpoints are automatically created.
"Failed to fetch API spec" Error
Ensure the server is fully started before trying to fetch the specification. The generation script includes a 2-second delay to allow for complete initialization.
Missing Route Documentation
Routes without schema definitions will appear in the specification but with minimal documentation. Add schema objects to routes for complete documentation.
Next Steps
To extend API documentation:
- Add schema definitions to more routes using reusable constants
- Define reusable components in the OpenAPI configuration
- Add request body schemas for POST/PUT endpoints
- Include error response schemas (400, 401, 500, etc.)
- Add parameter validation schemas
Plugin API Routes
Plugin Route Structure
All plugin routes are automatically namespaced under /api/plugin/<plugin-name>/
for security and isolation:
- Core Routes:
/api/auth/*
,/api/users/*
,/api/settings/*
(protected from plugins) - Plugin Routes:
/api/plugin/<plugin-name>/*
(isolated per plugin)
Example Plugin Routes
For a plugin with ID example-plugin
:
GET /api/plugin/example-plugin/examples
GET /api/plugin/example-plugin/examples/:id
POST /api/plugin/example-plugin/examples
PUT /api/plugin/example-plugin/examples/:id
DELETE /api/plugin/example-plugin/examples/:id
Security Benefits
- Route Isolation: Plugins cannot interfere with core routes or each other
- Predictable Structure: All plugin APIs follow the same pattern
- Easy Identification: Plugin ownership is clear from the URL
- Automatic Namespacing: No manual prefix management required
Plugin Route Registration
Plugins register routes using the PluginRouteManager
:
// In plugin's routes.ts file
export async function registerRoutes(routeManager: PluginRouteManager, db: AnyDatabase | null) {
// This becomes /api/plugin/my-plugin/data
routeManager.get('/data', async () => {
return { message: 'Hello from plugin!' };
});
}
Files Generated
api-spec.json
- Complete OpenAPI 3.0 specification in JSON formatapi-spec.yaml
- Complete OpenAPI 3.0 specification in YAML format- Interactive documentation available at
/documentation
when server is running
API Security
Essential security patterns for DeployStack Backend API development, including proper authorization hook usage and security-first development principles.
Backend Authentication System
Technical documentation for the DeployStack backend authentication implementation, including session management, password hashing, and authentication flows.