496303471d
---ci--- project: atelier phase: 7 milestone: v0.1 status: complete phase_role: final milestone_complete: true requirements: covered: [ATELIER-01, ATELIER-02, ATELIER-03, ATELIER-04, ATELIER-05, ATELIER-06, ATELIER-07, ATELIER-08, ATELIER-09, ATELIER-10, ATELIER-11, ATELIER-12, ATELIER-13, ATELIER-14, ATELIER-15, ATELIER-16, ATELIER-17, ATELIER-18, ATELIER-19, ATELIER-20, ATELIER-21, ATELIER-22, ATELIER-23, ATELIER-24, ATELIER-25, ATELIER-26, ATELIER-27, ATELIER-28, ATELIER-29, ATELIER-30, ATELIER-31, ATELIER-32, ATELIER-33, ATELIER-34, ATELIER-35] partial: [] ship: milestone: v0.1 type: NFR tag: v0.0.7 merge: milestone/v0.1-atelier -> main release: https://git.cloudinit.dev/cloudinit-bot/atelier/releases/tag/v0.0.7 ---/ci--- Milestone v0.1 — Initial Framework (NFR, complete). 8 core principles (C1-C8), 11 domains, 110 domain principles, 27 derived docs, 4 good + 3 bad examples, 4 language docs, full matrix, 3 review docs. All 35 requirements covered. 7 patches (v0.0.0 pre-execution through v0.0.7 final). v0.0.7 IS the v0.1.0 milestone release.
79 lines
3.2 KiB
Markdown
79 lines
3.2 KiB
Markdown
# Good Example: API Endpoint
|
||
|
||
> A REST endpoint that follows Atelier's API principles. Each aspect cites the principle it satisfies.
|
||
|
||
## The Endpoint
|
||
|
||
```typescript
|
||
// 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). `Location` header for the new resource.
|
||
- Idempotency key supported via middleware (omitted for brevity) — P6.
|
||
|
||
### Authentication (API P8 Security, Security P1 Zero Trust)
|
||
- `auth` middleware 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: false` in 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.create` are 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_id` propagated 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)
|
||
|
||
```typescript
|
||
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
|
||
```
|
||
|
||
- `userId` is a UUID (Data P7 Type Fidelity).
|
||
- `quantity` is bounded (Performance P4).
|
||
- `items` is 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). |