OpenAPI
The OpenAPI Specification (formerly known as Swagger) is a standardized format for describing, producing, consuming, and visualizing RESTful APIs. In VibeReference applications, OpenAPI provides a structured approach to API documentation and integration.
Introduction to OpenAPI
OpenAPI offers several key advantages:
- Standardized Documentation: A language-agnostic format for describing REST APIs
- Machine-Readable: Can be parsed by tools to generate code, documentation, and tests
- Human-Readable: Uses JSON or YAML format that's accessible to humans
- Ecosystem Support: Wide range of tools for validation, documentation, and code generation
- Contract-First Development: Enables API-first design approaches
The current version is OpenAPI 3.0, though some tools still support the legacy Swagger 2.0 format.
Core Components of OpenAPI
An OpenAPI document consists of several key sections:
1. Info Section
Basic information about the API:
openapi: 3.0.0
info:
title: VibeReference API
description: API for managing VibeReference resources
version: 1.0.0
contact:
name: VibeReference Support
url: https://vibereference.example.com/support
email: support@vibereference.example.com
2. Servers
Defines server URLs for the API:
servers:
- url: https://api.vibereference.example.com/v1
description: Production server
- url: https://staging-api.vibereference.example.com/v1
description: Staging server
- url: http://localhost:3000/api/v1
description: Local development
3. Paths
Endpoints and operations available in the API:
paths:
/users:
get:
summary: Retrieve a list of users
description: Returns a paginated list of users
parameters:
- name: limit
in: query
description: Maximum number of users to return
schema:
type: integer
format: int32
minimum: 1
maximum: 100
default: 20
responses:
'200':
description: A list of users
content:
application/json:
schema:
type: object
properties:
users:
type: array
items:
$ref: '#/components/schemas/User'
pagination:
$ref: '#/components/schemas/Pagination'
post:
summary: Create a new user
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UserInput'
responses:
'201':
description: User created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/User'
4. Components
Reusable schemas, parameters, responses, and more:
components:
schemas:
User:
type: object
properties:
id:
type: string
format: uuid
readOnly: true
name:
type: string
email:
type: string
format: email
role:
type: string
enum: [user, admin]
default: user
created_at:
type: string
format: date-time
readOnly: true
required:
- name
- email
UserInput:
type: object
properties:
name:
type: string
minLength: 2
maxLength: 100
email:
type: string
format: email
role:
type: string
enum: [user, admin]
default: user
required:
- name
- email
Pagination:
type: object
properties:
total:
type: integer
page:
type: integer
limit:
type: integer
has_next:
type: boolean
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
ApiKeyAuth:
type: apiKey
in: header
name: x-api-key
5. Security
Security requirements for the API:
security:
- BearerAuth: []
Implementing OpenAPI in VibeReference
Generate API Documentation Endpoint
Expose your OpenAPI specification as an endpoint:
// app/api/docs/route.ts
import { NextResponse } from 'next/server';
import openApiDocument from '@/lib/openapi/specification.json';
export function GET() {
return NextResponse.json(openApiDocument);
}
Document-Driven Development
- Write OpenAPI Spec First: Define your API contract before implementation
- Generate Types: Use tools to create TypeScript interfaces from schemas
- Validate Requests/Responses: Ensure API calls match the specification
- Automated Testing: Generate tests based on the OpenAPI document
Organizing Large OpenAPI Documents
For complex APIs, split your specification into manageable files:
// lib/openapi/index.ts
import { OpenAPIObject } from 'openapi3-ts';
import info from './info.json';
import servers from './servers.json';
import userPaths from './paths/users.json';
import projectPaths from './paths/projects.json';
import schemas from './schemas/index.json';
import securitySchemes from './security-schemes.json';
const openApiDocument: OpenAPIObject = {
openapi: '3.0.0',
info,
servers,
paths: {
...userPaths,
...projectPaths,
},
components: {
schemas,
securitySchemes,
},
security: [
{ BearerAuth: [] }
]
};
export default openApiDocument;
Generating Types from OpenAPI
Use tools like openapi-typescript to generate TypeScript types:
# Install generator
npm install --save-dev openapi-typescript
# Generate types
npx openapi-typescript ./lib/openapi/specification.json -o ./lib/types/api.ts
Example usage:
// lib/types/api.ts (generated)
export interface components {
schemas: {
User: {
id: string;
name: string;
email: string;
role?: 'user' | 'admin';
created_at?: string;
};
UserInput: {
name: string;
email: string;
role?: 'user' | 'admin';
};
// ...other schemas
};
// ...other components
}
// Using the generated types
import { components } from '@/lib/types/api';
type User = components['schemas']['User'];
type UserInput = components['schemas']['UserInput'];
async function createUser(userData: UserInput): Promise<User> {
// Implementation...
}
API Validation with OpenAPI
Validate request and response data against your schema:
// lib/openapi/validation.ts
import Ajv from 'ajv';
import addFormats from 'ajv-formats';
import openApiDocument from './specification.json';
const ajv = new Ajv({ strict: false });
addFormats(ajv);
// Extract schemas from OpenAPI document
const schemas = openApiDocument.components.schemas;
// Compile validators for each schema
const validators = Object.fromEntries(
Object.entries(schemas).map(([name, schema]) => [
name,
ajv.compile(schema)
])
);
// Validate data against a schema
export function validate(schemaName: string, data: unknown) {
const validator = validators[schemaName];
if (!validator) {
throw new Error(`Schema ${schemaName} not found`);
}
const valid = validator(data);
return {
valid,
errors: validator.errors
};
}
Usage in an API route:
// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { validate } from '@/lib/openapi/validation';
export async function POST(request: NextRequest) {
try {
const body = await request.json();
// Validate request body against OpenAPI schema
const validation = validate('UserInput', body);
if (!validation.valid) {
return NextResponse.json(
{
error: 'Validation failed',
details: validation.errors
},
{ status: 400 }
);
}
// Process validated data
// ...
return NextResponse.json({ success: true }, { status: 201 });
} catch (error) {
return NextResponse.json({ error: 'Bad request' }, { status: 400 });
}
}
Swagger UI Integration
Provide interactive documentation using Swagger UI:
// app/api/swagger/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { readFileSync } from 'fs';
import path from 'path';
export function GET(request: NextRequest) {
const url = new URL(request.url);
const htmlPath = path.join(process.cwd(), 'public', 'swagger-ui', 'index.html');
let html = readFileSync(htmlPath, 'utf-8');
// Replace placeholder with actual URL to OpenAPI document
const apiUrl = `${url.protocol}//${url.host}/api/docs`;
html = html.replace('__OPENAPI_URL__', apiUrl);
return new NextResponse(html, {
headers: {
'Content-Type': 'text/html',
},
});
}
Setup in public/swagger-ui/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>VibeReference API Documentation</title>
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@5.1.0/swagger-ui.css">
<style>
body { margin: 0; }
.swagger-ui .topbar { display: none; }
</style>
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist@5.1.0/swagger-ui-bundle.js"></script>
<script>
window.onload = function() {
window.ui = SwaggerUIBundle({
url: "__OPENAPI_URL__",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
],
layout: "BaseLayout",
withCredentials: true,
});
};
</script>
</body>
</html>
Request Parameters
Document different parameter types:
paths:
/projects/{id}:
get:
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
description: Project unique identifier
- name: include
in: query
schema:
type: array
items:
type: string
enum: [members, tasks, comments]
style: form
explode: false
description: Related resources to include
- name: x-request-id
in: header
schema:
type: string
format: uuid
description: Request ID for tracing
responses:
'200':
description: Project details
content:
application/json:
schema:
$ref: '#/components/schemas/Project'
Authentication & Authorization
Define security schemes and requirements:
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: JWT token obtained from the /login endpoint
ApiKeyAuth:
type: apiKey
in: header
name: x-api-key
description: API key for external service integration
paths:
/public-data:
get:
security: [] # No auth required
responses:
'200':
description: Publicly available data
/users/me:
get:
security:
- BearerAuth: [] # JWT auth required
responses:
'200':
description: Current user information
/webhooks:
post:
security:
- ApiKeyAuth: [] # API key required
responses:
'200':
description: Webhook received
Best Practices
- Keep Schemas DRY: Use
$refto reuse schema components - Document Examples: Provide realistic example values for faster comprehension
- Use Semantic Versioning: Clearly indicate API versions
- Include Error Responses: Document all possible error responses
- Add Descriptions: Provide clear descriptions for all paths, parameters, and schemas
- Authentication: Clearly document security requirements
- Tag Operations: Organize endpoints logically with tags
Common Patterns
Pagination
components:
parameters:
Pagination:
in: query
name: pagination
schema:
type: object
properties:
page:
type: integer
minimum: 1
default: 1
limit:
type: integer
minimum: 1
maximum: 100
default: 20
schemas:
PaginatedResponse:
type: object
properties:
data:
type: array
items:
type: object
pagination:
type: object
properties:
total:
type: integer
page:
type: integer
limit:
type: integer
totalPages:
type: integer
Filtering and Sorting
paths:
/products:
get:
parameters:
- name: category
in: query
schema:
type: string
- name: min_price
in: query
schema:
type: number
format: float
- name: max_price
in: query
schema:
type: number
format: float
- name: sort_by
in: query
schema:
type: string
enum: [price, name, created_at]
default: created_at
- name: sort_order
in: query
schema:
type: string
enum: [asc, desc]
default: desc