Bun.js OpenAPI and Apidog Integration
Overview
This skill covers OpenAPI specification creation and Apidog integration for Bun.js TypeScript backend applications. Learn how to document APIs with OpenAPI 3.0, use Apidog-specific extensions, import specifications via REST API, and maintain synchronized API documentation.
When to use this skill:
- Creating OpenAPI specifications for API documentation
- Synchronizing API specs with Apidog projects
- Importing endpoints and schemas to Apidog
- Managing API documentation lifecycle
See also:
- dev:bunjs - Core Bun patterns, HTTP servers, database access
- dev:bunjs-architecture - Layered architecture, camelCase conventions
- dev:bunjs-production - Production deployment patterns
Why Apidog
Apidog is a comprehensive API development platform that combines:
- API Design - Visual OpenAPI editor
- API Documentation - Auto-generated, always up-to-date docs
- API Testing - Built-in testing tools
- API Mocking - Mock servers for frontend development
- Team Collaboration - Shared workspace for teams
Environment Variables
Required:
APIDOG_PROJECT_ID=your-project-id # From Apidog project settings APIDOG_API_TOKEN=your-api-token # From Apidog account settings
How to get these:
- APIDOG_PROJECT_ID: Open your Apidog project β Settings β Project ID
- APIDOG_API_TOKEN: Apidog Account β Settings β API Tokens β Generate Token
OpenAPI Spec Creation
Basic Structure
openapi: 3.0.0 info: title: My API version: 1.0.0 description: API for managing resources servers: - url: https://api.example.com/v1 description: Production server - url: https://staging-api.example.com/v1 description: Staging server - url: http://localhost:3000 description: Development server components: schemas: # Define reusable data models here securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT paths: # Define API endpoints here
Field Naming: camelCase (CRITICAL)
ALWAYS use camelCase for all JSON API field names in OpenAPI specs.
components: schemas: User: type: object required: - userId - emailAddress properties: userId: # β camelCase type: string format: uuid emailAddress: # β camelCase type: string format: email firstName: # β camelCase type: string lastName: # β camelCase type: string phoneNumber: # β camelCase type: string isActive: # β camelCase boolean type: boolean createdAt: # β camelCase timestamp type: string format: date-time updatedAt: # β camelCase timestamp type: string format: date-time # β WRONG: snake_case # user_id, email_address, first_name, created_at # β WRONG: PascalCase # UserId, EmailAddress, FirstName, CreatedAt
Why camelCase:
- Native to JavaScript/JSON ecosystem
- Industry standard (Google, Microsoft, AWS)
- TypeScript friendly (1:1 mapping)
- OpenAPI/Swagger convention
- Auto-generated clients expect it
Schema Design
Define reusable schemas in components.schemas:
components: schemas: User: type: object required: - userId - emailAddress - firstName - lastName properties: userId: type: string format: uuid description: Unique user identifier emailAddress: type: string format: email description: User email address firstName: type: string minLength: 2 maxLength: 100 lastName: type: string minLength: 2 maxLength: 100 phoneNumber: type: string pattern: '^\+?[1-9]\d{1,14}$' role: type: string enum: [user, admin, moderator] default: user isActive: type: boolean default: true createdAt: type: string format: date-time updatedAt: type: string format: date-time CreateUserRequest: type: object required: - emailAddress - password - firstName - lastName properties: emailAddress: type: string format: email password: type: string format: password minLength: 8 firstName: type: string minLength: 2 lastName: type: string minLength: 2 phoneNumber: type: string role: type: string enum: [user, admin, moderator] UserListResponse: type: object properties: data: type: array items: $ref: '#/components/schemas/User' pagination: $ref: '#/components/schemas/Pagination' Pagination: type: object properties: page: type: integer minimum: 1 pageSize: type: integer minimum: 1 maximum: 100 total: type: integer totalPages: type: integer ErrorResponse: type: object properties: statusCode: type: integer type: type: string message: type: string details: type: array items: type: object properties: field: type: string message: type: string
Endpoint Definitions
Define endpoints in paths:
paths: /users: get: summary: List users description: Retrieve paginated list of users operationId: listUsers tags: - Users security: - bearerAuth: [] x-apidog-folder: User Management/Users x-apidog-status: released x-apidog-maintainer: backend-team parameters: - name: page in: query schema: type: integer minimum: 1 default: 1 - name: pageSize in: query schema: type: integer minimum: 1 maximum: 100 default: 20 - name: sortBy in: query schema: type: string enum: [createdAt, firstName, emailAddress] - name: orderBy in: query schema: type: string enum: [asc, desc] default: desc responses: '200': description: Successful response content: application/json: schema: $ref: '#/components/schemas/UserListResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' post: summary: Create user description: Create a new user account operationId: createUser tags: - Users x-apidog-folder: User Management/Users x-apidog-status: released x-apidog-maintainer: backend-team requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateUserRequest' responses: '201': description: User created successfully content: application/json: schema: type: object properties: data: $ref: '#/components/schemas/User' '400': description: Invalid request content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '409': description: User already exists content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '422': description: Validation failed content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /users/{userId}: get: summary: Get user description: Retrieve a single user by ID operationId: getUser tags: - Users security: - bearerAuth: [] x-apidog-folder: User Management/Users x-apidog-status: released parameters: - name: userId in: path required: true schema: type: string format: uuid responses: '200': description: Successful response content: application/json: schema: type: object properties: data: $ref: '#/components/schemas/User' '404': description: User not found content: application/json: schema: $ref: '#/components/schemas/ErrorResponse'
Apidog-Specific Extensions
x-apidog-folder
Organize endpoints in folders using / to separate levels:
paths: /users: post: x-apidog-folder: User Management/Users /users/{userId}/profile: get: x-apidog-folder: User Management/Users/Profile /orders: post: x-apidog-folder: Order Management/Orders
Escaping special characters:
- Use
\/for/ - Use
\\for\
x-apidog-status
Endpoint lifecycle status:
| Status | Description |
|---|---|
designing | Being designed |
pending | Pending implementation |
developing | In development |
integrating | Integration phase |
testing | Being tested |
tested | Testing complete |
released | Production release |
deprecated | Marked for deprecation |
exception | Has issues |
obsolete | No longer used |
to be deprecated | Will be deprecated |
paths: /users: post: x-apidog-status: released # Fully implemented /beta/feature: post: x-apidog-status: testing # In testing phase /legacy/api: get: x-apidog-status: deprecated # Being phased out
x-apidog-maintainer
Specify owner/maintainer (use Apidog username or nickname):
paths: /users: post: x-apidog-maintainer: backend-team /admin/settings: put: x-apidog-maintainer: john-doe
Importing to Apidog via REST API
Import Process
Step 1: Prepare OpenAPI Spec
Create a complete OpenAPI 3.0 spec in JSON format:
# Save spec to file cat > /tmp/api-spec.json << 'EOF' { "openapi": "3.0.0", "info": { ... }, "paths": { ... } } EOF
Step 2: Import via REST API
#!/bin/bash # Environment variables (from .env) APIDOG_PROJECT_ID="your-project-id" APIDOG_API_TOKEN="your-api-token" # Read OpenAPI spec OPENAPI_SPEC=$(cat /tmp/api-spec.json | jq -c .) # Import to Apidog curl -X POST "https://api.apidog.com/v1/projects/${APIDOG_PROJECT_ID}/import-openapi" \ -H "Authorization: Bearer ${APIDOG_API_TOKEN}" \ -H "X-Apidog-Api-Version: 2024-03-28" \ -H "Content-Type: application/json" \ -d "{ \"input\": \"${OPENAPI_SPEC}\", \"options\": { \"endpointOverwriteBehavior\": \"AUTO_MERGE\", \"schemaOverwriteBehavior\": \"AUTO_MERGE\", \"updateFolderOfChangedEndpoint\": false, \"prependBasePath\": false } }"
Import Behavior Options
| Option | Description |
|---|---|
AUTO_MERGE | Automatically merge changes (recommended) |
OVERWRITE_EXISTING | Replace existing endpoints/schemas completely |
KEEP_EXISTING | Skip changes, keep existing |
CREATE_NEW | Create new endpoints/schemas (duplicates existing) |
Recommendation: Use AUTO_MERGE for intelligent merging without losing existing data.
API Response Format
{ "data": { "counters": { "endpointCreated": 3, "endpointUpdated": 2, "endpointFailed": 0, "endpointIgnored": 0, "schemaCreated": 5, "schemaUpdated": 1, "schemaFailed": 0, "schemaIgnored": 0, "endpointFolderCreated": 1, "endpointFolderUpdated": 0, "schemaFolderCreated": 0, "schemaFolderUpdated": 0 }, "errors": [] } }
Error Handling
| Status Code | Meaning |
|---|---|
| 401 | Token is invalid or expired |
| 404 | Project ID not found |
| 422 | OpenAPI spec validation failed |
Check data.errors array for detailed error messages.
Workflow
1. Create OpenAPI Spec from Code
Step 1: Analyze existing endpoints
# List all route files find src/routes -name "*.ts" -type f # Read route definitions cat src/routes/user.routes.ts
Step 2: Extract schemas from Zod
// src/schemas/user.schema.ts import { z } from 'zod'; export const createUserSchema = z.object({ emailAddress: z.string().email(), password: z.string().min(8), firstName: z.string(), lastName: z.string() }); // Convert to OpenAPI schema manually or use zod-to-json-schema
Step 3: Build OpenAPI spec
Map routes to OpenAPI paths, schemas to components, camelCase fields.
2. Validate Spec
# Use online validator # https://editor.swagger.io/ # Or use CLI tool npm install -g swagger-cli swagger-cli validate api-spec.yaml
3. Import to Apidog
# Via REST API (automated) ./scripts/import-to-apidog.sh # Or manually in Apidog UI # Import β OpenAPI β Upload file
4. Verify in Apidog
- Open Apidog project:
https://app.apidog.com/project/{APIDOG_PROJECT_ID} - Check imported endpoints appear in correct folders
- Verify schemas are properly structured
- Test endpoints with Apidog's testing tools
- Update descriptions and add examples
5. Set Endpoint Status
Update x-apidog-status based on implementation progress:
designingβdevelopingβtestingβreleased
6. Share with Team
Share Apidog project with team members for:
- Frontend integration (use mock servers)
- API testing
- Documentation review
Automation Script
scripts/import-to-apidog.sh:
#!/bin/bash set -e # Load environment variables source .env # Check required variables if [ -z "$APIDOG_PROJECT_ID" ] || [ -z "$APIDOG_API_TOKEN" ]; then echo "Error: APIDOG_PROJECT_ID and APIDOG_API_TOKEN must be set" exit 1 fi # Generate OpenAPI spec (customize based on your needs) SPEC_FILE="/tmp/api-spec-$(date +%Y%m%d-%H%M%S).json" echo "Generating OpenAPI spec..." # Add your spec generation logic here # For example: ts-node scripts/generate-openapi.ts > $SPEC_FILE # Read spec OPENAPI_SPEC=$(cat $SPEC_FILE | jq -c .) # Import to Apidog echo "Importing to Apidog..." RESPONSE=$(curl -s -X POST \ "https://api.apidog.com/v1/projects/${APIDOG_PROJECT_ID}/import-openapi" \ -H "Authorization: Bearer ${APIDOG_API_TOKEN}" \ -H "X-Apidog-Api-Version: 2024-03-28" \ -H "Content-Type: application/json" \ -d "{ \"input\": ${OPENAPI_SPEC}, \"options\": { \"endpointOverwriteBehavior\": \"AUTO_MERGE\", \"schemaOverwriteBehavior\": \"AUTO_MERGE\" } }") # Parse response ENDPOINT_CREATED=$(echo $RESPONSE | jq -r '.data.counters.endpointCreated') ENDPOINT_UPDATED=$(echo $RESPONSE | jq -r '.data.counters.endpointUpdated') SCHEMA_CREATED=$(echo $RESPONSE | jq -r '.data.counters.schemaCreated') SCHEMA_UPDATED=$(echo $RESPONSE | jq -r '.data.counters.schemaUpdated') ERRORS=$(echo $RESPONSE | jq -r '.data.errors | length') # Display summary echo "" echo "β Import Complete!" echo "" echo "Endpoints:" echo " Created: $ENDPOINT_CREATED" echo " Updated: $ENDPOINT_UPDATED" echo "" echo "Schemas:" echo " Created: $SCHEMA_CREATED" echo " Updated: $SCHEMA_UPDATED" echo "" echo "Errors: $ERRORS" echo "" echo "π View in Apidog: https://app.apidog.com/project/${APIDOG_PROJECT_ID}" # Exit with error if there were errors if [ "$ERRORS" != "0" ]; then echo "" echo "β οΈ Import had errors. Check response:" echo $RESPONSE | jq '.data.errors' exit 1 fi
Error Scenarios & Solutions
Missing Environment Variables
Problem: APIDOG_PROJECT_ID or APIDOG_API_TOKEN not set
Solution:
# Add to .env file APIDOG_PROJECT_ID=your-project-id APIDOG_API_TOKEN=your-api-token # Restart application
Schema Conflicts
Problem: New schema conflicts with existing schema
Solution:
- Use
allOfto extend existing schemas - Or create with different name
- Or use
OVERWRITE_EXISTINGbehavior (carefully)
Import Failures
Problem: Automated import fails
Solution:
- Check API token validity
- Verify project ID
- Validate OpenAPI spec syntax
- Check
data.errorsin response for details
Invalid OpenAPI Spec
Problem: Generated spec has validation errors
Solution:
# Validate before importing swagger-cli validate api-spec.yaml # Fix validation errors # Common issues: # - Missing required fields # - Invalid $ref paths # - Incorrect enum values # - Wrong data types
Best Practices
1. Schema Reuse
# β CORRECT: Reuse schemas with $ref paths: /users/{userId}: get: responses: '200': content: application/json: schema: type: object properties: data: $ref: '#/components/schemas/User' # β WRONG: Duplicate schema definitions paths: /users/{userId}: get: responses: '200': content: application/json: schema: type: object properties: data: type: object properties: userId: { type: string } # ... duplicated fields
2. Comprehensive Descriptions
# β CORRECT: Clear descriptions paths: /users: post: summary: Create user description: | Creates a new user account with email verification. The password must meet the following requirements: - At least 8 characters - Contains uppercase and lowercase letters - Contains at least one number - Contains at least one special character Upon successful creation, a verification email is sent to the provided email address.
3. Response Examples
responses: '200': description: Successful response content: application/json: schema: $ref: '#/components/schemas/User' example: data: userId: "550e8400-e29b-41d4-a716-446655440000" emailAddress: "[email protected]" firstName: "John" lastName: "Doe" isActive: true createdAt: "2025-01-06T12:00:00Z"
4. Security Schemes
components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT description: JWT access token obtained from /auth/login # Apply globally security: - bearerAuth: [] # Or per endpoint paths: /public/health: get: security: [] # No auth required
5. Version Your API
servers: - url: https://api.example.com/v1 description: Version 1 (current) - url: https://api.example.com/v2 description: Version 2 (beta)
OpenAPI and Apidog integration for Bun.js TypeScript backend. For core patterns, see dev:bunjs. For architecture, see dev:bunjs-architecture.