# Backend API Guidelines โ€” The Nerve Center > *"APIs are contracts. Design them with the same care as legal documents."* > โ€” Unknown --- ## ๐ŸŽฏ Filosofi API Backend API BETE adalah **fasilitator antara data dan tampilan**: 1. **RESTful by design** โ€” Sumber daya, bukan aksi 2. **Type-safe** โ€” Zod schemas di setiap endpoint 3. **Consistent pagination** โ€” Tidak ada kejutan format 4. **Error as structure** โ€” Setiap error punya kode dan resolusi --- ## ๐Ÿ“ API Design Principles ### URL Structure ``` GET /api/v1/messages # List messages GET /api/v1/messages/:id # Single message GET /api/v1/channels # List channels GET /api/v1/analytics/overview # Analytics GET /api/v1/voice/connections # Voice connections POST /api/v1/voice/connect # Connect to voice POST /api/v1/voice/disconnect # Disconnect ``` ### Response Envelope ```typescript // Success { "success": true, "data": T, "meta"?: { "page": 1, "limit": 50, "total": 1234, "hasMore": true } } // Error { "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Invalid channelId format", "details": { "field": "channelId", "constraint": "numeric_string" }, "requestId": "req_abc123" } } ``` ### Pagination ```typescript interface PaginationParams { page?: number; // Default: 1 limit?: number; // Default: 50, Max: 200 cursor?: string; // For cursor-based pagination } interface PaginationMeta { page: number; limit: number; total: number; totalPages: number; hasMore: boolean; } ``` ### Filtering ```typescript interface FilterParams { search?: string; channelId?: string; userId?: string; severity?: 'safe' | 'low' | 'medium' | 'high' | 'critical'; dateFrom?: string; // ISO 8601 dateTo?: string; // ISO 8601 sortBy?: string; // Field name sortOrder?: 'asc' | 'desc'; } ``` --- ## ๐Ÿ—๏ธ Module Structure (Backend) ``` services/backend/src/modules/ โ”œโ”€โ”€ messages/ โ”‚ โ”œโ”€โ”€ messages.schema.ts # Zod schemas โ”‚ โ”œโ”€โ”€ messages.repository.ts # Database queries โ”‚ โ”œโ”€โ”€ messages.service.ts # Business logic โ”‚ โ”œโ”€โ”€ messages.controller.ts # Request handlers โ”‚ โ””โ”€โ”€ routes/ โ”‚ โ””โ”€โ”€ index.ts # Express router โ”œโ”€โ”€ analytics/ โ”œโ”€โ”€ voice/ โ”œโ”€โ”€ media/ โ””โ”€โ”€ health/ ``` ### Layer Rules ``` Controller (parse + validate) โ†’ Service (business logic) โ†’ Repository (DB queries) โ†• Shared Infrastructure (config, logger, errors) ``` --- ## โšก WebSocket Events ### Event Format ```typescript interface WsEvent { type: string; // e.g., "message:created" data: T; timestamp: number; requestId?: string; } // Server โ†’ Client events { "type": "message:created", "data": { "id": "msg_123", "content": "...", "author": { "id": "user_1", "name": "User" } }, "timestamp": 1750000000000 } // Client โ†’ Server events { "type": "voice:connect", "data": { "guildId": "123456789", "channelId": "987654321" } } ``` ### Event Catalog | Type | Direction | Description | |------|-----------|-------------| | `message:created` | Server โ†’ Client | New message captured | | `message:updated` | Server โ†’ Client | Message edited | | `message:deleted` | Server โ†’ Client | Message removed | | `message:analyzed` | Server โ†’ Client | AI analysis complete | | `voice:state` | Server โ†’ Client | Voice connection state | | `voice:speaker` | Server โ†’ Client | Speaker activity | | `attachment:uploaded` | Server โ†’ Client | Attachment uploaded | | `analytics:update` | Server โ†’ Client | Analytics data refresh | --- ## ๐Ÿ”’ Authentication & Authorization ```typescript // Admin auth via header Authorization: Bearer // Rate limiting RateLimit: 100/minute per IP Retry-After: 60 ``` ### Error Codes | Code | HTTP | Description | |------|------|-------------| | `VALIDATION_ERROR` | 400 | Invalid input | | `UNAUTHORIZED` | 401 | Invalid/missing auth | | `FORBIDDEN` | 403 | Insufficient permissions | | `NOT_FOUND` | 404 | Resource not found | | `RATE_LIMITED` | 429 | Too many requests | | `INTERNAL_ERROR` | 500 | Unexpected error | | `SERVICE_UNAVAILABLE` | 503 | Downstream failure | --- ## ๐Ÿงช Testing Strategy ```typescript describe('GET /api/v1/messages', () => { it('returns paginated messages', async () => { const res = await request(app).get('/api/v1/messages?page=1&limit=10'); expect(res.status).toBe(200); expect(res.body.success).toBe(true); expect(res.body.meta.hasMore).toBeDefined(); }); it('rejects invalid severity filter', async () => { const res = await request(app).get('/api/v1/messages?severity=invalid'); expect(res.status).toBe(400); expect(res.body.error.code).toBe('VALIDATION_ERROR'); }); }); ``` --- ## โš ๏ธ API Anti-Patterns ### โŒ Nested resources terlalu dalam ``` // โŒ JANGAN GET /api/v1/guilds/123/channels/456/messages/789 // โœ… Flat dengan query params GET /api/v1/messages?channelId=456 ``` ### โŒ Inconsistent error format ```typescript // โŒ JANGAN โ€” kadang string, kadang object if (err) return res.status(400).send('Bad request'); if (err) return res.status(400).json({ message: 'Bad request' }); // โœ… Consistent envelope if (err) return res.status(400).json({ success: false, error: { code: 'VALIDATION_ERROR', message: 'Bad request' } }); ``` ### โŒ No type safety ```typescript // โŒ JANGAN โ€” any, tidak ada validasi app.get('/api/messages', async (req, res) => { const messages = await db.query('SELECT * FROM messages'); res.json(messages); }); // โœ… Zod schema + typed handler app.get('/api/v1/messages', asyncHandler(async (req, res) => { const query = messageQuerySchema.parse(req.query); const messages = await messagesService.list(query); res.json({ success: true, data: messages }); })); ``` --- ## ๐Ÿ”— Referensi | Sumber | Konsep | |--------|--------| | [JSON:API](https://jsonapi.org/) | Response format spec | | [Express.js](https://expressjs.com/) | Server framework | | [Zod](https://zod.dev/) | Schema validation | --- *"API adalah jembatan ingatan โ€” setiap request adalah percakapan."* โ„๏ธ๐Ÿฉต