---
name: bunjs-apidog
version: 1.0.0
description: Use when creating OpenAPI specs for Bun.js APIs, integrating with Apidog, documenting endpoints with schemas, or automating API specification imports via Apidog REST API. See bunjs for basics.
keywords:
  - OpenAPI
  - Apidog
  - API documentation
  - Swagger
  - API specs
  - integration
  - API design
plugin: dev
updated: 2026-01-20
user-invocable: false
---

# 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:**
```bash
APIDOG_PROJECT_ID=your-project-id     # From Apidog project settings
APIDOG_API_TOKEN=your-api-token       # From Apidog account settings
```

**How to get these:**
1. **APIDOG_PROJECT_ID**: Open your Apidog project → Settings → Project ID
2. **APIDOG_API_TOKEN**: Apidog Account → Settings → API Tokens → Generate Token

## OpenAPI Spec Creation

### Basic Structure

```yaml
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.**

```yaml
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`:**

```yaml
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`:**

```yaml
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:

```yaml
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 |

```yaml
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):

```yaml
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:

```bash
# Save spec to file
cat > /tmp/api-spec.json << 'EOF'
{
  "openapi": "3.0.0",
  "info": { ... },
  "paths": { ... }
}
EOF
```

**Step 2: Import via REST API**

```bash
#!/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

```json
{
  "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**

```bash
# 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**

```typescript
// 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

```bash
# 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

```bash
# Via REST API (automated)
./scripts/import-to-apidog.sh

# Or manually in Apidog UI
# Import → OpenAPI → Upload file
```

### 4. Verify in Apidog

1. Open Apidog project: `https://app.apidog.com/project/{APIDOG_PROJECT_ID}`
2. Check imported endpoints appear in correct folders
3. Verify schemas are properly structured
4. Test endpoints with Apidog's testing tools
5. 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:**

```bash
#!/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:**
```bash
# 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 `allOf` to extend existing schemas
- Or create with different name
- Or use `OVERWRITE_EXISTING` behavior (carefully)

### Import Failures

**Problem:** Automated import fails

**Solution:**
- Check API token validity
- Verify project ID
- Validate OpenAPI spec syntax
- Check `data.errors` in response for details

### Invalid OpenAPI Spec

**Problem:** Generated spec has validation errors

**Solution:**
```bash
# 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

```yaml
# ✅ 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

```yaml
# ✅ 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

```yaml
responses:
  '200':
    description: Successful response
    content:
      application/json:
        schema:
          $ref: '#/components/schemas/User'
        example:
          data:
            userId: "550e8400-e29b-41d4-a716-446655440000"
            emailAddress: "john@example.com"
            firstName: "John"
            lastName: "Doe"
            isActive: true
            createdAt: "2025-01-06T12:00:00Z"
```

### 4. Security Schemes

```yaml
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

```yaml
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.*
