---
name: add-api-endpoint
description: >
  Guide for adding a new API endpoint to the codeflash-internal system.
  Use when adding a new endpoint, creating a route, or implementing a new API.
  Covers both aiservice (Django-Ninja) and cf-api (Express) endpoints
  including schemas, routers, authentication, URL registration, and tests.
---

# Add API Endpoint

Use this workflow when adding a new endpoint to either the aiservice backend or the cf-api middleware. Follow the section that matches your target service.

## Part A: AIService Endpoint (Django-Ninja)

### Step 1: Define Schemas

Create request and response schemas.

1. Create or update schemas in the appropriate module (e.g., `core/shared/` for shared, `core/languages/python/` for Python-specific)
2. Use `ninja.Schema` for all request/response types:
   ```python
   from ninja import Schema

   class MyRequestSchema(Schema):
       source_code: str
       trace_id: str
       language: str = "python"

   class MyResponseSchema(Schema):
       results: list[str]

   class MyErrorResponseSchema(Schema):
       message: str
   ```
3. Follow existing patterns in `core/shared/optimizer_models.py`

**Checkpoint**: Schemas should use Pydantic validation. Test with `MyRequestSchema.model_validate(data)`.

### Step 2: Create the Router

Create a NinjaAPI router for the endpoint.

1. Create a new module (e.g., `core/shared/my_feature.py` or `core/languages/python/my_feature/my_feature.py`)
2. Define the router and endpoint:
   ```python
   from ninja import NinjaAPI
   from authapp.auth import AuthenticatedRequest

   my_feature_api = NinjaAPI(urls_namespace="my_feature")

   @my_feature_api.post(
       "/", response={200: MyResponseSchema, 400: MyErrorResponseSchema, 500: MyErrorResponseSchema}
   )
   async def my_endpoint(
       request: AuthenticatedRequest, data: MyRequestSchema
   ) -> tuple[int, MyResponseSchema | MyErrorResponseSchema]:
       # Implementation here
       return 200, MyResponseSchema(results=[])
   ```
3. All endpoints must be `async def`
4. Use `AuthenticatedRequest` for authenticated endpoints
5. For multi-language endpoints, add dispatch by `data.language` with lazy imports

**Checkpoint**: The endpoint should handle success and error cases with proper status codes.

### Step 3: Register in urls.py

Add the endpoint to `aiservice/urls.py`.

1. Import the API object:
   ```python
   from core.shared.my_feature import my_feature_api
   ```
2. Add to `urlpatterns`:
   ```python
   path("ai/my-feature", my_feature_api.urls),
   ```
3. Follow the naming convention: `ai/<kebab-case-name>`

**Checkpoint**: The endpoint should be accessible at `/ai/my-feature/`.

### Step 4: Add Tests

Write tests for the endpoint.

1. Create test file in `tests/` matching the source structure
2. Test the handler function directly (not via HTTP):
   ```python
   @pytest.mark.asyncio
   async def test_my_endpoint():
       # Mock request and data
       result = await my_endpoint(mock_request, mock_data)
       assert result[0] == 200
   ```
3. Run: `uv run pytest tests/path/test_file.py -v`

**Checkpoint**: Tests pass. Run `uv run prek run --all-files`.

## Part B: CF-API Endpoint (Express)

### Step 1: Create Endpoint Handler

Create the endpoint handler in `js/cf-api/endpoints/`.

1. Create `js/cf-api/endpoints/my-feature.ts`:
   ```typescript
   import { Request, Response } from "express"

   export async function myFeature(req: Request, res: Response) {
       // Implementation
       res.status(200).json({ results: [] })
   }
   ```
2. Follow existing patterns in `endpoints/`

**Checkpoint**: Handler should return appropriate status codes and JSON responses.

### Step 2: Create Route File or Add to Existing

Add the route to the appropriate route file.

1. If it's a new domain, create `js/cf-api/routes/my-feature.routes.ts`:
   ```typescript
   import { Router } from "express"
   import { addAsync } from "@awaitjs/express"
   import { myFeature } from "../endpoints/my-feature.js"
   import { ROUTES } from "../constants/index.js"

   const router = addAsync(Router()) as any
   router.postAsync(ROUTES.MY_FEATURE, myFeature)
   export default router
   ```
2. If it belongs to an existing domain, add to the corresponding route file
3. Add the route constant to `constants/index.ts`

**Checkpoint**: Route file exports a router with the endpoint registered.

### Step 3: Register in Route Index

Register the route in `js/cf-api/routes/index.ts`.

1. Import the route module
2. Register in the correct section:
   - Before body parser: webhook routes only
   - Public routes: no auth required
   - Protected routes: after `checkForValidAPIKey` middleware
3. Apply middleware as needed (`trackUsage`, `idLimiter`, etc.)

**Checkpoint**: The endpoint should be accessible with proper authentication.

### Step 4: Add Tests

Write tests using the dependency injection pattern.

1. Create test file in `js/cf-api/__tests__/`
2. Use `setXxxDependencies()` / `resetXxxDependencies()` for mocking
3. Follow existing test patterns

**Checkpoint**: Tests pass with `npm test`.

## Key Files Reference

| File | What to modify |
|------|---------------|
| `core/shared/optimizer_models.py` | Schema patterns |
| `aiservice/urls.py` | AIService endpoint registration |
| `js/cf-api/routes/index.ts` | CF-API route registration |
| `js/cf-api/constants/index.ts` | Route constants |
| `authapp/auth.py` | Authentication patterns |
