skillby sgcarstrends

api-version

Manage API versioning strategy for Hono routes in apps/api/src/v1. Use when creating new API versions or migrating endpoints between versions.

Installs: 0
Used in: 1 repos
Updated: 8h ago
$npx ai-builder add skill sgcarstrends/api-version

Installs to .claude/skills/api-version/

# API Versioning Skill

This skill helps you manage API versions in `apps/api/src/v1/` and prepare for future versions.

## When to Use This Skill

- Creating a new API version (v2, v3, etc.)
- Deprecating old API endpoints
- Migrating endpoints between versions
- Planning breaking changes
- Maintaining backward compatibility

## Current API Structure

```
apps/api/src/
├── v1/                  # Current API version
│   ├── routes/
│   │   ├── cars.ts      # Car registration endpoints
│   │   ├── coe.ts       # COE bidding endpoints
│   │   ├── pqp.ts       # PQP data endpoints
│   │   └── health.ts    # Health check
│   └── index.ts         # v1 router assembly
└── index.ts             # Main Hono app with versioned routes
```

## Versioning Strategy

### URL-Based Versioning

The project uses URL path versioning:
- `https://api.sgcarstrends.com/v1/cars`
- `https://api.sgcarstrends.com/v1/coe`
- Future: `https://api.sgcarstrends.com/v2/cars`

### Benefits
- Clear, explicit versioning visible in URLs
- Easy to cache and monitor per version
- Clients can migrate at their own pace
- Multiple versions can coexist

## Creating a New API Version

### Step 1: Create Version Directory

```bash
mkdir -p apps/api/src/v2/routes
```

### Step 2: Copy Existing Routes

Start with current v1 routes as a base:

```bash
cp -r apps/api/src/v1/routes/* apps/api/src/v2/routes/
```

### Step 3: Create Version Router

Create `apps/api/src/v2/index.ts`:

```typescript
import { Hono } from "hono";
import { carsRouter } from "./routes/cars";
import { coeRouter } from "./routes/coe";
import { pqpRouter } from "./routes/pqp";

const v2 = new Hono();

// Mount routes
v2.route("/cars", carsRouter);
v2.route("/coe", coeRouter);
v2.route("/pqp", pqpRouter);

export default v2;
```

### Step 4: Mount in Main App

Update `apps/api/src/index.ts`:

```typescript
import { Hono } from "hono";
import v1 from "./v1";
import v2 from "./v2";

const app = new Hono();

// Mount API versions
app.route("/v1", v1);
app.route("/v2", v2);  // Add new version

// Default to latest stable version
app.route("/", v1);  // Keep v1 as default or change to v2 when stable

export default app;
```

### Step 5: Implement Breaking Changes

Make necessary changes in v2 routes:

```typescript
// v1 response format
{
  "success": true,
  "data": [...],
  "count": 10
}

// v2 response format (breaking change)
{
  "data": [...],
  "meta": {
    "total": 10,
    "page": 1,
    "pageSize": 10
  }
}
```

## Migration Patterns

### 1. Gradual Migration

Keep both versions running:

```typescript
// v1/routes/cars.ts - deprecated but maintained
export const carsRouter = new Hono();

carsRouter.get("/", async (c) => {
  // Old logic
  return c.json({
    success: true,
    data: await getCars(),
  });
});

// v2/routes/cars.ts - new implementation
export const carsRouter = new Hono();

carsRouter.get("/", async (c) => {
  // New logic with pagination
  const { page = 1, limit = 10 } = c.req.query();
  const result = await getCars({ page, limit });

  return c.json({
    data: result.items,
    meta: {
      total: result.total,
      page,
      pageSize: limit,
    },
  });
});
```

### 2. Feature Flag Pattern

Use feature flags to test changes:

```typescript
import { Hono } from "hono";

export const carsRouter = new Hono();

carsRouter.get("/", async (c) => {
  const useV2Format = c.req.header("X-API-Version") === "2";

  const data = await getCars();

  if (useV2Format) {
    return c.json({ data, meta: { ... } });
  }

  // v1 format
  return c.json({ success: true, data });
});
```

### 3. Deprecation Warnings

Add deprecation headers to v1:

```typescript
import { Hono } from "hono";

export const carsRouter = new Hono();

// Add deprecation middleware
carsRouter.use("*", async (c, next) => {
  await next();
  c.header("X-API-Deprecation", "true");
  c.header("X-API-Sunset", "2025-12-31");
  c.header("Link", '<https://api.sgcarstrends.com/v2/cars>; rel="successor-version"');
});

carsRouter.get("/", async (c) => {
  // Existing logic
});
```

## Breaking Changes Checklist

When introducing breaking changes, consider:

- [ ] Response structure changes
- [ ] Required parameter additions
- [ ] Authentication method changes
- [ ] URL structure modifications
- [ ] HTTP method changes
- [ ] Header requirement changes
- [ ] Error format modifications
- [ ] Data type changes

## Version Documentation

Document versions in OpenAPI/Swagger:

```typescript
// apps/api/src/v2/openapi.ts
import { OpenAPIHono } from "@hono/zod-openapi";

const app = new OpenAPIHono();

app.openapi(
  {
    method: "get",
    path: "/cars",
    summary: "Get car registrations (v2)",
    deprecated: false,
    tags: ["Cars"],
    responses: {
      200: {
        description: "Success",
        content: {
          "application/json": {
            schema: carResponseSchema,
          },
        },
      },
    },
  },
  async (c) => {
    // Handler
  }
);
```

## Version Sunset Process

### 1. Announce Deprecation

- Update documentation
- Add deprecation headers
- Notify API consumers
- Set sunset date

### 2. Monitor Usage

Track v1 usage metrics:

```typescript
import { middleware } from "hono/middleware";

v1.use("*", async (c, next) => {
  // Log usage for monitoring
  console.log("v1 API usage:", {
    path: c.req.path,
    user: c.get("user")?.id,
    timestamp: new Date(),
  });

  await next();
});
```

### 3. Provide Migration Guide

Create migration documentation:

```markdown
# Migrating from v1 to v2

## Breaking Changes

### Response Format
**v1:**
\`\`\`json
{ "success": true, "data": [...] }
\`\`\`

**v2:**
\`\`\`json
{ "data": [...], "meta": { ... } }
\`\`\`

### Pagination
v2 includes built-in pagination:
- Query params: `?page=1&limit=10`
- Response includes `meta` with pagination info

## Migration Steps
1. Update base URL from `/v1` to `/v2`
2. Update response parsing to handle new format
3. Add pagination parameters if needed
4. Update error handling for new error format
```

### 4. Remove Old Version

After sunset date:

```bash
# Remove v1 directory
rm -rf apps/api/src/v1

# Update main app
# Remove v1 mounting from apps/api/src/index.ts
```

## Testing Multiple Versions

Test all active versions:

```bash
# Test v1
curl https://api.sgcarstrends.com/v1/cars

# Test v2
curl https://api.sgcarstrends.com/v2/cars

# Run version-specific tests
pnpm -F @sgcarstrends/api test -- src/v1
pnpm -F @sgcarstrends/api test -- src/v2
```

## Deployment Considerations

### Zero-Downtime Deployment

1. Deploy v2 alongside v1
2. Test v2 in production
3. Gradually route traffic to v2
4. Monitor error rates
5. Rollback if issues occur

### Environment Variables

Version-specific config:

```env
# v1 settings
V1_RATE_LIMIT=100
V1_CACHE_TTL=300

# v2 settings
V2_RATE_LIMIT=200
V2_CACHE_TTL=600
```

## Common Scenarios

### Scenario 1: Add Required Parameter

**v1:** Optional parameter
```typescript
carsRouter.get("/", async (c) => {
  const make = c.req.query("make"); // optional
  return c.json(await getCars({ make }));
});
```

**v2:** Required parameter (breaking change)
```typescript
carsRouter.get("/", async (c) => {
  const make = c.req.query("make");
  if (!make) {
    return c.json({ error: "make parameter required" }, 400);
  }
  return c.json(await getCars({ make }));
});
```

### Scenario 2: Change Data Format

**v1:** Flat structure
```typescript
{ id: 1, make: "Toyota", model: "Camry" }
```

**v2:** Nested structure (breaking change)
```typescript
{
  id: 1,
  vehicle: {
    make: "Toyota",
    model: "Camry"
  }
}
```

### Scenario 3: Rename Endpoint

**v1:** `/cars/list`
**v2:** `/cars` (breaking change - URL structure)

Solution: Redirect in v1
```typescript
v1.get("/cars/list", async (c) => {
  c.header("X-API-Deprecated", "true");
  c.header("Location", "/v2/cars");
  return c.redirect("/v2/cars", 301);
});
```

## References

- Hono documentation: Use Context7 for latest docs
- Related files:
  - `apps/api/src/v1/` - Current API version
  - `apps/api/src/index.ts` - Main app with version mounting
  - `apps/api/CLAUDE.md` - API service documentation

## Best Practices

1. **Semantic Versioning**: Use v1, v2, v3 (not v1.1, v1.2)
2. **Backward Compatibility**: Maintain old versions during migration period
3. **Documentation**: Document all breaking changes clearly
4. **Communication**: Announce deprecations well in advance
5. **Monitoring**: Track usage of deprecated endpoints
6. **Testing**: Maintain tests for all active versions
7. **Graceful Sunset**: Provide sufficient migration time (6-12 months)
8. **Error Messages**: Help users migrate with clear error messages

Quick Install

$npx ai-builder add skill sgcarstrends/api-version

Details

Type
skill
Slug
sgcarstrends/api-version
Created
3d ago