Skip to content

REST and GraphQL APIs via Claude Code

Your product team just scoped a new integration. The partner needs a REST API with twelve endpoints, OAuth2 authentication, rate limiting, and webhook delivery. You need it production-ready in two weeks. The traditional approach means three days of boilerplate before you write a single line of business logic — route scaffolding, validation schemas, error handlers, auth middleware, and the OpenAPI spec that the partner will use to integrate.

Claude Code compresses that scaffolding to a single session. You describe the API design, Claude generates the routes with validation and error handling that match your existing patterns, and you test endpoints with curl right from the same terminal. The key is knowing how to guide the generation so you get production-quality code instead of a demo.

  • A workflow for designing and implementing APIs entirely from the terminal
  • Prompts that generate endpoints with proper validation, auth, and error handling
  • The inline curl testing technique for rapid API development
  • Patterns for generating OpenAPI specs from your route handlers
  1. Design the API contract first

    Before writing any code, have Claude produce an API design document. This catches design issues before you commit to an implementation.

  2. Review the design before implementing

    Read the API design. Check that the resources are correct, the naming is consistent, and the error codes make sense. It is much cheaper to change a design document than to refactor implemented code.

  3. Implement the routes matching your existing patterns

    Read the existing route handlers in src/routes/ to learn our
    patterns. Then implement the partner API routes following the
    same structure:
    - Same middleware chain (auth, rate limit, validation)
    - Same error response format
    - Same logging approach
    - Same test patterns
    Start with the products endpoints. I'll review before you
    move to orders.
  4. Test inline with curl

    This is where the terminal-native workflow shines. You do not need Postman or a separate tool — test right from the Claude session.

    Start the dev server, then test the products endpoint:
    curl -X GET http://localhost:3000/api/v1/products \
    -H "Authorization: Bearer test-token" \
    -H "Content-Type: application/json"
    Show me the response. Then test with invalid auth,
    missing parameters, and an empty result set.
  5. Generate the OpenAPI spec

    Now that the routes are implemented and tested, generate an
    OpenAPI 3.0 spec from the actual route handlers and Zod schemas.
    Save it to docs/partner-api.yaml.

Good APIs validate input before anything else. Claude can generate Zod (or equivalent) schemas directly from your API design.

Create Zod schemas for the partner API:
1. ProductQuerySchema - validates query parameters for
GET /products (category: string, minPrice: number,
maxPrice: number, page: number, limit: number)
2. CreateOrderSchema - validates the POST /orders body
(customerId: string, items: array of {productId, quantity},
shippingAddress: object, notes: optional string)
3. WebhookConfigSchema - validates POST /webhooks body
(url: valid URL, events: array of event types, secret: string)
Put them in src/schemas/partner.schema.ts following the patterns
in our existing schema files.
Implement OAuth2 client credentials authentication for the
partner API. Read our existing auth middleware in
src/middleware/auth.ts for patterns.
The partner auth flow:
1. Partner requests token: POST /oauth/token with client_id
and client_secret
2. We return a JWT with scopes: products:read, orders:write,
webhooks:manage
3. Each endpoint checks the required scope
4. Tokens expire after 1 hour
Create the middleware and apply it to the partner routes.
Include tests that verify:
- Valid token with correct scope passes
- Valid token with wrong scope gets 403
- Expired token gets 401
- Missing token gets 401

For projects using GraphQL, Claude generates schemas, resolvers, and the DataLoader patterns needed to avoid N+1 queries.

We're adding a GraphQL API alongside our REST endpoints.
Read the existing REST route handlers and database models.
Generate:
1. GraphQL schema (SDL) with types matching our models
2. Resolvers that call our existing service layer
3. DataLoaders for User, Product, and Order to prevent N+1
4. Authentication context that integrates with our JWT auth
Use Apollo Server and follow the patterns from the GraphQL
example in our docs. Include connection types for paginated
lists (Relay cursor-based pagination).
Our GraphQL API has N+1 problems. When querying orders,
each order loads its user individually. Read the resolver
code in src/graphql/resolvers/.
Create DataLoaders for:
1. UserLoader - batch load users by ID
2. ProductLoader - batch load products by ID
3. OrderItemsLoader - batch load order items by order ID
Wire them into the Apollo context. Show me the before/after
query count for a query that fetches 20 orders with their
users and items.

The fastest way to test an API during development is curl from the same terminal session where Claude is running.

Test the complete order creation flow:
1. Create a product:
curl -X POST http://localhost:3000/api/v1/products \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Widget", "price": 29.99, "category": "tools"}'
2. Create an order using the product ID from step 1:
curl -X POST http://localhost:3000/api/v1/orders \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"customerId": "cust-123", "items": [{"productId": "PRODUCT_ID", "quantity": 2}]}'
3. Verify the order was created:
curl -X GET http://localhost:3000/api/v1/orders/ORDER_ID \
-H "Authorization: Bearer $TOKEN"
Show me each response.
Generate API integration tests for the partner endpoints.
Use supertest (or the test client for our framework).
Follow the test patterns in tests/routes/.
For each endpoint, test:
- Successful request with valid input
- Validation error with invalid input
- Authentication required (no token)
- Authorization denied (wrong scope)
- Not found (invalid ID)
- Conflict (duplicate creation)
- Rate limit exceeded
Include a test helper that creates an authenticated test client
with partner credentials.
Add rate limiting to the partner API. Read our existing rate
limiter in src/middleware/rateLimit.ts.
Configuration:
- Standard tier: 100 requests per minute per API key
- Elevated tier: 1000 requests per minute per API key
- Include X-RateLimit-Limit, X-RateLimit-Remaining,
and X-RateLimit-Reset headers on every response
- Return 429 with Retry-After header when exceeded
The tier is determined by the partner's plan stored in the
database. Cache the plan lookup in Redis with a 5-minute TTL.
Implement cursor-based pagination for all list endpoints.
Read our existing pagination helper in src/lib/pagination.ts.
The response format should be:
{
"data": [...],
"pagination": {
"cursor": "encoded-cursor",
"hasMore": true,
"totalCount": 1234
}
}
The cursor encodes the sort field and ID for consistent
ordering. Support both forward and backward pagination.
Add tests that verify pagination works correctly with
filters applied.
Implement webhook delivery for order status changes.
1. Partners register webhooks: POST /webhooks
- URL, events to subscribe, signing secret
2. When an order status changes, queue webhook deliveries
to all subscribers of that event
3. Webhook payload format:
{ "event": "order.status_changed", "data": { order }, "timestamp": "ISO" }
4. Sign each payload with HMAC-SHA256 using the partner's secret
Include the signature in X-Webhook-Signature header
5. Retry failed deliveries: 3 attempts with exponential backoff
(30s, 5min, 30min)
6. Log all delivery attempts for debugging
Follow our existing job queue patterns in src/jobs/.
Include tests that verify signing, retry behavior, and
the delivery endpoint receiving correct payloads.

Generated endpoints have inconsistent response formats. Claude sometimes uses different error shapes across endpoints. Define the format once and enforce it: “All error responses must use the ErrorResponse type defined in src/types/api.ts. Do not create ad-hoc error objects.”

Validation is too loose or too strict. Review the Zod schemas Claude generates against your actual data. Common issues: string fields missing max length, number fields without reasonable bounds, optional fields that should be required. Tell Claude: “For every field in the schema, add reasonable constraints. Strings need maxLength, numbers need min/max, arrays need maxItems.”

Auth middleware is applied inconsistently. Some endpoints require auth, others do not. Create a convention: “All routes under /api/v1/partner/ require the partnerAuth middleware. Health checks and the OAuth token endpoint are the only exceptions. Verify this by listing all routes and their middleware.”

API performs well in tests but slowly in production. The test database is small. Add a performance test: “Generate a test that creates 10,000 products, then benchmarks the list endpoint with various filter combinations. Show me response times and query counts.”

The OpenAPI spec drifts from the implementation. Generate the spec from code, not the other way around. Add a CI check: “Compare the OpenAPI spec with the actual route handlers. Fail the build if they disagree.”

Your API is implemented with proper validation, auth, and error handling. Now make sure the database layer underneath it is designed for the queries your API actually runs.