plugins/dev/skills/backend/bunjs-apidog/SKILL.md
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.
npx skillsauth add madappgang/claude-code bunjs-apidogInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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:
See also:
Apidog is a comprehensive API development platform that combines:
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:
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
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:
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
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'
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:
\/ for /\\ for \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
Specify owner/maintainer (use Apidog username or nickname):
paths:
/users:
post:
x-apidog-maintainer: backend-team
/admin/settings:
put:
x-apidog-maintainer: john-doe
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
}
}"
| 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.
{
"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": []
}
}
| 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.
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.
# Use online validator
# https://editor.swagger.io/
# Or use CLI tool
npm install -g swagger-cli
swagger-cli validate api-spec.yaml
# Via REST API (automated)
./scripts/import-to-apidog.sh
# Or manually in Apidog UI
# Import → OpenAPI → Upload file
https://app.apidog.com/project/{APIDOG_PROJECT_ID}Update x-apidog-status based on implementation progress:
designing → developing → testing → releasedShare Apidog project with team members for:
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
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
Problem: New schema conflicts with existing schema
Solution:
allOf to extend existing schemasOVERWRITE_EXISTING behavior (carefully)Problem: Automated import fails
Solution:
data.errors in response for detailsProblem: 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
# ✅ 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
# ✅ 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.
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"
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
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.
testing
A test skill for validation testing. Use when testing skill parsing and validation logic.
tools
--- name: bad-skill description: This skill has invalid YAML in frontmatter allowed-tools: [invalid, array, syntax prerequisites: not-an-array --- # Bad Skill This skill has malformed frontmatter that should fail parsing. The YAML has: - Unclosed array bracket - Wrong type for prerequisites (should be array, not string)
tools
Plugin release process for MAG Claude Plugins marketplace. Covers version bumping, marketplace.json updates, git tagging, and common mistakes. Use when releasing new plugin versions or troubleshooting update issues.
testing
Fetch trending programming models from OpenRouter rankings. Use when selecting models for multi-model review, updating model recommendations, or researching current AI coding trends. Provides model IDs, context windows, pricing, and usage statistics from the most recent week.