3.2 KiB
3.2 KiB
Good Example: API Endpoint
A REST endpoint that follows Atelier's API principles. Each aspect cites the principle it satisfies.
The Endpoint
// POST /v1/orders — create an order
router.post('/v1/orders', auth, validate(CreateOrderSchema), async (req, res) => {
const { userId, items } = req.body;
const order = await orderService.create({ userId, items });
res.status(201).location(`/v1/orders/${order.id}`).json({
data: order,
});
});
What Makes It Good
Resource Naming (API P2 Clarity, P3 Predictability)
/v1/orders— noun, plural, lowercase, hyphenated.- Versioned (
/v1) — P5 Versioning. - No verb in the URL; the HTTP method is the verb.
Method Semantics (API P1 Contract Fidelity, P6 Idempotency)
- POST for creation. 201 on success (not 200).
Locationheader for the new resource. - Idempotency key supported via middleware (omitted for brevity) — P6.
Authentication (API P8 Security, Security P1 Zero Trust)
authmiddleware runs on every endpoint by default. No opt-in auth.- The endpoint does not re-implement auth; it relies on the boundary check.
Input Validation (API P8, Security P4 Input Validation)
validate(CreateOrderSchema)— schema-based validation at the boundary.- The schema (zod, joi, etc.) defines types, ranges, required fields.
- Unknown fields rejected (
additionalProperties: falsein the schema).
Response Shape (API P2 Clarity)
{ data: order }— wrapped, not a bare object. Allows adding pagination/metadata without breaking.- The shape is consistent across all endpoints in the API.
Error Handling (API P9 Error Transparency, Errors P1 Errors are Data)
- Errors thrown in
orderService.createare caught by centralized middleware. - Errors are structured:
{ error: { code, message, request_id } }. - 404 →
ORDER_NOT_FOUND, 409 →DUPLICATE_ORDER, 422 →VALIDATION_FAILED.
Observability (Observability P2 Correlation, P3 Sufficient Context)
request_idpropagated via middleware. Every log in the request includes it.- Significant events logged: "order created", "order creation failed".
Economy (Core C8, Performance P4 Resource Bounds)
- The order creation is bounded in time (the service has a timeout).
- No unbounded query; no loading all products into memory.
The Schema (for completeness)
const CreateOrderSchema = z.object({
userId: z.string().uuid(),
items: z.array(z.object({
productId: z.string().uuid(),
quantity: z.number().int().positive().max(100),
})).min(1).max(50),
}).strict(); // additionalProperties: false
userIdis a UUID (Data P7 Type Fidelity).quantityis bounded (Performance P4).itemsis bounded (1–50) (Performance P4, Security P10 Surface Minimization)..strict()rejects unknown fields (Security P4).
What This Example Does NOT Do (And Why That's Good)
- Does not return 200 on error — the status code is the first signal (API P9).
- Does not log the request body — may contain PII (Observability P6, Security P9).
- Does not construct SQL by string interpolation — uses a service layer (Security P5 Output Safety).
- Does not skip auth for "internal" callers — Zero Trust (Security P1).