# ThrillWiki API Conventions

Standards for designing and implementing APIs between the Django backend and Nuxt frontend.

## API Base Structure

### URL Patterns
```
/api/v1/                     # API root (versioned)
/api/v1/parks/               # List/Create parks
/api/v1/parks/{slug}/        # Retrieve/Update/Delete park
/api/v1/parks/{slug}/rides/  # Nested resource
/api/v1/auth/                # Authentication endpoints
/api/v1/users/me/            # Current user
```

### HTTP Methods
| Method | Usage |
|--------|-------|
| GET | Retrieve resource(s) |
| POST | Create resource |
| PUT | Replace resource entirely |
| PATCH | Update resource partially |
| DELETE | Remove resource |

## Response Formats

### Success Response (Single Resource)
```json
{
  "id": "uuid",
  "name": "Cedar Point",
  "slug": "cedar-point",
  "created_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-16T14:20:00Z",
  ...
}
```

### Success Response (List)
```json
{
  "count": 150,
  "next": "/api/v1/parks/?page=2",
  "previous": null,
  "results": [
    { ... },
    { ... }
  ]
}
```

### Error Response
```json
{
  "error": {
    "code": "validation_error",
    "message": "Invalid input data",
    "details": {
      "name": ["This field is required."],
      "status": ["Invalid choice."]
    }
  }
}
```

### HTTP Status Codes
| Code | Meaning | When to Use |
|------|---------|-------------|
| 200 | OK | Successful GET, PUT, PATCH |
| 201 | Created | Successful POST |
| 204 | No Content | Successful DELETE |
| 400 | Bad Request | Validation errors |
| 401 | Unauthorized | Missing/invalid auth |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource doesn't exist |
| 409 | Conflict | Duplicate resource |
| 500 | Server Error | Unexpected errors |

## Pagination

### Query Parameters
```
?page=2           # Page number (default: 1)
?page_size=20     # Items per page (default: 20, max: 100)
```

### Response
```json
{
  "count": 150,
  "next": "/api/v1/parks/?page=3",
  "previous": "/api/v1/parks/?page=1",
  "results": [ ... ]
}
```

## Filtering & Sorting

### Filtering
```
GET /api/v1/parks/?status=operating
GET /api/v1/parks/?country=USA&status=operating
GET /api/v1/rides/?park=cedar-point&type=coaster
```

### Search
```
GET /api/v1/parks/?search=cedar
```

### Sorting
```
GET /api/v1/parks/?ordering=name          # Ascending
GET /api/v1/parks/?ordering=-created_at   # Descending
GET /api/v1/parks/?ordering=-rating,name  # Multiple fields
```

## Authentication

### Token-Based Auth
```
Authorization: Bearer <jwt_token>
```

### Endpoints
```
POST /api/v1/auth/login/          # Get tokens
POST /api/v1/auth/register/       # Create account
POST /api/v1/auth/refresh/        # Refresh access token
POST /api/v1/auth/logout/         # Invalidate tokens
GET  /api/v1/auth/me/             # Current user info
```

### Social Auth
```
POST /api/v1/auth/google/         # Google OAuth
POST /api/v1/auth/discord/        # Discord OAuth
```

## Content Submission API

### Submit New Content
```
POST /api/v1/submissions/
{
  "content_type": "park",
  "data": {
    "name": "New Park Name",
    "city": "Orlando",
    ...
  }
}
```

### Response
```json
{
  "id": "submission-uuid",
  "status": "pending",
  "content_type": "park",
  "data": { ... },
  "created_at": "2024-01-15T10:30:00Z"
}
```

### Submit Edit to Existing Content
```
POST /api/v1/submissions/
{
  "content_type": "park",
  "object_id": "existing-park-uuid",
  "data": {
    "description": "Updated description..."
  }
}
```

## Moderation API

### Get Moderation Queue
```
GET /api/v1/moderation/
GET /api/v1/moderation/?status=pending&type=park
```

### Review Submission
```
POST /api/v1/moderation/{id}/approve/
POST /api/v1/moderation/{id}/reject/
{
  "notes": "Reason for rejection..."
}
```

## Nested Resources

### Pattern
```
/api/v1/parks/{slug}/rides/              # List rides in park
/api/v1/parks/{slug}/reviews/            # List park reviews
/api/v1/parks/{slug}/photos/             # List park photos
```

### When to Nest vs Flat
**Nest when:**
- Resource only makes sense in context of parent (park photos)
- Need to filter by parent frequently

**Use flat with filter when:**
- Resource can exist independently
- Need to query across parents

```
# Flat with filter
GET /api/v1/rides/?park=cedar-point

# Or nested
GET /api/v1/parks/cedar-point/rides/
```

## Geolocation API

### Parks Nearby
```
GET /api/v1/parks/nearby/?lat=41.4821&lng=-82.6822&radius=50
```

### Response includes distance
```json
{
  "results": [
    {
      "id": "uuid",
      "name": "Cedar Point",
      "distance": 12.5,  // in user's preferred units
      ...
    }
  ]
}
```

## File Uploads

### Photo Upload
```
POST /api/v1/photos/
Content-Type: multipart/form-data

{
  "content_type": "park",
  "object_id": "park-uuid",
  "image": <file>,
  "caption": "Optional caption"
}
```

### Response
```json
{
  "id": "photo-uuid",
  "url": "https://cdn.thrillwiki.com/photos/...",
  "thumbnail_url": "https://cdn.thrillwiki.com/photos/.../thumb",
  "status": "pending",  // Pending moderation
  "created_at": "2024-01-15T10:30:00Z"
}
```

## Rate Limiting

### Headers
```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1642089600
```

### When Limited
```
HTTP/1.1 429 Too Many Requests
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Too many requests. Try again in 60 seconds."
  }
}
```

## Frontend Integration

### API Client Setup (Nuxt)
```typescript
// composables/useApi.ts
export function useApi() {
  const config = useRuntimeConfig()
  const authStore = useAuthStore()
  
  const api = $fetch.create({
    baseURL: config.public.apiBase,
    headers: authStore.token ? {
      Authorization: `Bearer ${authStore.token}`
    } : {},
    onResponseError: ({ response }) => {
      if (response.status === 401) {
        authStore.logout()
        navigateTo('/auth/login')
      }
    }
  })
  
  return api
}
```

### Usage in Components
```typescript
const api = useApi()

// Fetch parks
const { data: parks } = await useAsyncData('parks', () =>
  api('/parks/', { params: { status: 'operating' } })
)

// Create submission
await api('/submissions/', {
  method: 'POST',
  body: { content_type: 'park', data: formData }
})
```
