@martinffx/atelier 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/LICENSE +21 -0
- package/README.md +285 -0
- package/agents/architect.md +38 -0
- package/agents/oracle.md +40 -0
- package/agents/recon.md +39 -0
- package/dist/atelier.js +33082 -0
- package/package.json +44 -0
- package/skills/code-conventional-commit/SKILL.md +134 -0
- package/skills/code-conventional-commit/evals/evals.json +23 -0
- package/skills/code-conventional-commit/workspace/iteration-1/eval-0/with_skill/outputs/commit-message.txt +4 -0
- package/skills/code-conventional-commit/workspace/iteration-1/eval-0/with_skill/outputs/eval_metadata.json +6 -0
- package/skills/code-conventional-commit/workspace/iteration-1/eval-1/with_skill/outputs/eval_metadata.json +6 -0
- package/skills/code-conventional-commit/workspace/iteration-1/eval-1/with_skill/outputs/validation-result.txt +16 -0
- package/skills/code-conventional-commit/workspace/iteration-1/eval-2/with_skill/outputs/commit-message.txt +8 -0
- package/skills/code-conventional-commit/workspace/iteration-1/eval-2/with_skill/outputs/eval_metadata.json +6 -0
- package/skills/code-conventional-commit/workspace/iteration-1/report.html +1325 -0
- package/skills/code-debug/SKILL.md +271 -0
- package/skills/code-debug/references/language-tools.md +139 -0
- package/skills/code-debug/references/techniques.md +117 -0
- package/skills/code-docs/SKILL.md +171 -0
- package/skills/code-docs/references/api-docs.md +116 -0
- package/skills/code-docs/references/changelog.md +64 -0
- package/skills/code-docs/references/readme-structure.md +94 -0
- package/skills/code-git-worktrees/SKILL.md +218 -0
- package/skills/code-perf/SKILL.md +163 -0
- package/skills/code-perf/references/common-bottlenecks.md +124 -0
- package/skills/code-perf/references/optimization-patterns.md +167 -0
- package/skills/code-perf/references/profiling-tools.md +126 -0
- package/skills/code-review/SKILL.md +78 -0
- package/skills/code-review/references/output.md +200 -0
- package/skills/code-review/references/reviewers.md +719 -0
- package/skills/code-review/references/rq.md +357 -0
- package/skills/code-review/references/rs.md +30 -0
- package/skills/code-security/SKILL.md +183 -0
- package/skills/code-security/references/owasp-top-10.md +129 -0
- package/skills/code-security/references/security-tools.md +102 -0
- package/skills/code-security/references/vulnerability-patterns.md +129 -0
- package/skills/code-stacked-commit/SKILL.md +155 -0
- package/skills/code-subagents/SKILL.md +184 -0
- package/skills/code-subagents/references/code-quality-reviewer-prompt.md +48 -0
- package/skills/code-subagents/references/implementor-prompt.md +79 -0
- package/skills/code-subagents/references/spec-reviewer-prompt.md +62 -0
- package/skills/oracle-architect/SKILL.md +246 -0
- package/skills/oracle-architect/references/api-design.md +345 -0
- package/skills/oracle-architect/references/data-modeling.md +318 -0
- package/skills/oracle-architect/references/ddd-patterns.md +247 -0
- package/skills/oracle-challenge/SKILL.md +126 -0
- package/skills/oracle-testing/SKILL.md +237 -0
- package/skills/oracle-testing/references/anti-patterns.md +618 -0
- package/skills/oracle-testing/references/boundaries.md +579 -0
- package/skills/oracle-testing/references/core-testing.md +691 -0
- package/skills/oracle-testing/references/edge-testing.md +794 -0
- package/skills/oracle-testing/references/stub-driven-tdd.md +322 -0
- package/skills/oracle-thinkdeep/SKILL.md +164 -0
- package/skills/python-architecture/SKILL.md +148 -0
- package/skills/python-architecture/references/data-modeling.md +558 -0
- package/skills/python-architecture/references/ddd.md +569 -0
- package/skills/python-architecture/references/functional-core.md +337 -0
- package/skills/python-build-tools/SKILL.md +153 -0
- package/skills/python-build-tools/references/basedpyright.md +321 -0
- package/skills/python-build-tools/references/ruff.md +225 -0
- package/skills/python-build-tools/references/uv.md +257 -0
- package/skills/python-fastapi/SKILL.md +184 -0
- package/skills/python-fastapi/references/api-design.md +563 -0
- package/skills/python-fastapi/references/dependencies.md +479 -0
- package/skills/python-fastapi/references/middleware.md +475 -0
- package/skills/python-fastapi/references/validation.md +497 -0
- package/skills/python-modern-python/SKILL.md +201 -0
- package/skills/python-modern-python/references/async.md +76 -0
- package/skills/python-modern-python/references/pattern-matching.md +89 -0
- package/skills/python-modern-python/references/typing.md +96 -0
- package/skills/python-monorepo/SKILL.md +146 -0
- package/skills/python-monorepo/references/docker.md +515 -0
- package/skills/python-monorepo/references/namespace-packages.md +426 -0
- package/skills/python-monorepo/references/workspace-config.md +341 -0
- package/skills/python-sqlalchemy/SKILL.md +119 -0
- package/skills/python-sqlalchemy/references/async.md +51 -0
- package/skills/python-sqlalchemy/references/models.md +67 -0
- package/skills/python-sqlalchemy/references/queries.md +57 -0
- package/skills/python-temporal/SKILL.md +128 -0
- package/skills/python-temporal/references/patterns.md +87 -0
- package/skills/python-temporal/references/testing.md +54 -0
- package/skills/python-testing/SKILL.md +161 -0
- package/skills/python-testing/references/boundaries.md +449 -0
- package/skills/python-testing/references/mocking.md +449 -0
- package/skills/python-testing/references/pytest.md +524 -0
- package/skills/spec-brainstorm/SKILL.md +325 -0
- package/skills/spec-finish/SKILL.md +159 -0
- package/skills/spec-implement/SKILL.md +296 -0
- package/skills/spec-orchestrator/SKILL.md +156 -0
- package/skills/spec-plan/SKILL.md +248 -0
- package/skills/typescript-api-design/SKILL.md +396 -0
- package/skills/typescript-api-design/references/error-responses.md +469 -0
- package/skills/typescript-build-tools/SKILL.md +447 -0
- package/skills/typescript-build-tools/references/biome.md +436 -0
- package/skills/typescript-build-tools/references/bun.md +432 -0
- package/skills/typescript-build-tools/references/turborepo.md +426 -0
- package/skills/typescript-build-tools/references/vitest.md +518 -0
- package/skills/typescript-drizzle-orm/SKILL.md +398 -0
- package/skills/typescript-drizzle-orm/references/cloudflare.md +392 -0
- package/skills/typescript-drizzle-orm/references/entity-pattern.md +412 -0
- package/skills/typescript-drizzle-orm/references/postgresql.md +191 -0
- package/skills/typescript-drizzle-orm/references/repository-pattern.md +479 -0
- package/skills/typescript-drizzle-orm/references/sqlite.md +291 -0
- package/skills/typescript-dynamodb-toolbox/SKILL.md +370 -0
- package/skills/typescript-dynamodb-toolbox/references/entity-layer.md +574 -0
- package/skills/typescript-dynamodb-toolbox/references/error-handling.md +495 -0
- package/skills/typescript-dynamodb-toolbox/references/modeling.md +332 -0
- package/skills/typescript-dynamodb-toolbox/references/repository-layer.md +631 -0
- package/skills/typescript-dynamodb-toolbox/references/testing.md +718 -0
- package/skills/typescript-dynamodb-toolbox/references/transactions.md +423 -0
- package/skills/typescript-effect-ts/SKILL.md +341 -0
- package/skills/typescript-effect-ts/references/core.md +412 -0
- package/skills/typescript-effect-ts/references/error-handling.md +468 -0
- package/skills/typescript-effect-ts/references/resources.md +476 -0
- package/skills/typescript-effect-ts/references/schema.md +536 -0
- package/skills/typescript-effect-ts/references/services.md +461 -0
- package/skills/typescript-fastify/SKILL.md +405 -0
- package/skills/typescript-fastify/references/plugins.md +329 -0
- package/skills/typescript-fastify/references/typeid.md +315 -0
- package/skills/typescript-functional-patterns/SKILL.md +402 -0
- package/skills/typescript-functional-patterns/references/adts.md +497 -0
- package/skills/typescript-functional-patterns/references/branded-types.md +542 -0
- package/skills/typescript-functional-patterns/references/migration-guide.md +695 -0
- package/skills/typescript-functional-patterns/references/option-result.md +585 -0
- package/skills/typescript-testing/SKILL.md +353 -0
- package/skills/typescript-testing/references/mocking.md +675 -0
- package/skills/typescript-testing/references/msw.md +852 -0
- package/skills/typescript-testing/references/snapshot-testing.md +774 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Spec Compliance Reviewer Prompt Template
|
|
2
|
+
|
|
3
|
+
Dispatch after the implementer completes a task. Verifies the implementation matches what
|
|
4
|
+
was specified — nothing more, nothing less.
|
|
5
|
+
|
|
6
|
+
**Dispatch this BEFORE the code quality review.**
|
|
7
|
+
|
|
8
|
+
## Template
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
You are reviewing whether an implementation matches its specification.
|
|
12
|
+
|
|
13
|
+
## What Was Requested
|
|
14
|
+
|
|
15
|
+
{FULL TEXT of task requirements from plan.json}
|
|
16
|
+
|
|
17
|
+
## What Implementer Claims They Built
|
|
18
|
+
|
|
19
|
+
{From implementer's report}
|
|
20
|
+
|
|
21
|
+
## CRITICAL: Do Not Trust the Report
|
|
22
|
+
|
|
23
|
+
The implementer's report may be incomplete, inaccurate, or optimistic. You MUST verify
|
|
24
|
+
everything independently.
|
|
25
|
+
|
|
26
|
+
**DO NOT:**
|
|
27
|
+
- Take their word for what they implemented
|
|
28
|
+
- Trust their claims about completeness
|
|
29
|
+
- Accept their interpretation of requirements
|
|
30
|
+
|
|
31
|
+
**DO:**
|
|
32
|
+
- Read the actual code they wrote
|
|
33
|
+
- Compare actual implementation to requirements line by line
|
|
34
|
+
- Check for missing pieces they claimed to implement
|
|
35
|
+
- Look for extra features they didn't mention
|
|
36
|
+
|
|
37
|
+
## Your Job
|
|
38
|
+
|
|
39
|
+
Read the implementation code and verify:
|
|
40
|
+
|
|
41
|
+
**Missing requirements:**
|
|
42
|
+
- Did they implement everything that was requested?
|
|
43
|
+
- Are there requirements they skipped or missed?
|
|
44
|
+
- Did they claim something works but didn't actually implement it?
|
|
45
|
+
|
|
46
|
+
**Extra/unneeded work:**
|
|
47
|
+
- Did they build things that weren't requested?
|
|
48
|
+
- Did they over-engineer or add unnecessary features?
|
|
49
|
+
- Did they add "nice to haves" that weren't in spec?
|
|
50
|
+
|
|
51
|
+
**Misunderstandings:**
|
|
52
|
+
- Did they interpret requirements differently than intended?
|
|
53
|
+
- Did they solve the wrong problem?
|
|
54
|
+
- Did they implement the right feature but the wrong way?
|
|
55
|
+
|
|
56
|
+
**Verify by reading code, not by trusting the report.**
|
|
57
|
+
|
|
58
|
+
## Report
|
|
59
|
+
|
|
60
|
+
- ✅ Spec compliant (if everything matches after code inspection)
|
|
61
|
+
- ❌ Issues found: [list specifically what's missing or extra, with file:line references]
|
|
62
|
+
```
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: oracle-architect
|
|
3
|
+
description: DDD and hexagonal architecture with functional core pattern. Use when designing features, modeling domains, breaking down tasks, or understanding component responsibilities.
|
|
4
|
+
user-invocable: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Architect Skill
|
|
8
|
+
|
|
9
|
+
Domain-Driven Design and hexagonal architecture with functional core pattern for feature design.
|
|
10
|
+
|
|
11
|
+
## Architecture Model
|
|
12
|
+
|
|
13
|
+
Unified view of functional core and effectful edge:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Effectful Edge (IO) Functional Core (Pure)
|
|
17
|
+
┌─────────────────────────────────┐ ┌──────────────────────────┐
|
|
18
|
+
│ Router → request parsing │ │ Service → orchestration│
|
|
19
|
+
│ Consumer → event handling │───▶│ Entity → domain rules │
|
|
20
|
+
│ Client → external APIs │ │ → validation │
|
|
21
|
+
│ Producer → event publishing │◀───│ → transforms │
|
|
22
|
+
│ Repository→ data persistence │ │ │
|
|
23
|
+
└─────────────────────────────────┘ └──────────────────────────┘
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Key Principle:** Business logic lives in the functional core (Service + Entity). IO operations live in the effectful edge. Core defines interfaces; edge implements them (dependency inversion).
|
|
27
|
+
|
|
28
|
+
## Functional Core
|
|
29
|
+
|
|
30
|
+
Pure, deterministic components containing all business logic.
|
|
31
|
+
|
|
32
|
+
### Service Layer
|
|
33
|
+
|
|
34
|
+
**Responsibility:** Orchestrate business operations, coordinate between entities and repositories.
|
|
35
|
+
|
|
36
|
+
**Characteristics:**
|
|
37
|
+
- Pure functions that take data and return results
|
|
38
|
+
- No IO operations (database, HTTP, file system)
|
|
39
|
+
- Calls repositories through interfaces (dependency injection)
|
|
40
|
+
- Composes entity operations into workflows
|
|
41
|
+
- Returns success/error results
|
|
42
|
+
|
|
43
|
+
**Example:**
|
|
44
|
+
```typescript
|
|
45
|
+
class OrderService {
|
|
46
|
+
async createOrder(request: CreateOrderRequest): Promise<Result<Order>> {
|
|
47
|
+
// Validate with entity
|
|
48
|
+
const order = Order.fromRequest(request);
|
|
49
|
+
const validation = order.validate();
|
|
50
|
+
if (!validation.ok) return validation;
|
|
51
|
+
|
|
52
|
+
// Check business rules
|
|
53
|
+
const inventory = await this.inventoryRepo.checkAvailability(order.items);
|
|
54
|
+
if (!inventory.available) return Err('Items not available');
|
|
55
|
+
|
|
56
|
+
// Coordinate persistence
|
|
57
|
+
await this.inventoryRepo.reserve(order.items);
|
|
58
|
+
const saved = await this.orderRepo.save(order.toRecord());
|
|
59
|
+
|
|
60
|
+
return Ok(Order.fromRecord(saved));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Entity Layer
|
|
66
|
+
|
|
67
|
+
**Responsibility:** Domain models, validation, business rules, data transformations.
|
|
68
|
+
|
|
69
|
+
**Characteristics:**
|
|
70
|
+
- Pure data structures with behavior
|
|
71
|
+
- All validation logic
|
|
72
|
+
- Data transformations (fromRequest, toRecord, toResponse)
|
|
73
|
+
- Business rules and invariants
|
|
74
|
+
- No IO, no framework dependencies
|
|
75
|
+
|
|
76
|
+
**Example:**
|
|
77
|
+
```typescript
|
|
78
|
+
class Order {
|
|
79
|
+
constructor(
|
|
80
|
+
public readonly id: string,
|
|
81
|
+
public readonly customerId: string,
|
|
82
|
+
public readonly items: OrderItem[],
|
|
83
|
+
public readonly status: OrderStatus,
|
|
84
|
+
public readonly total: number
|
|
85
|
+
) {}
|
|
86
|
+
|
|
87
|
+
static fromRequest(req: CreateOrderRequest): Order {
|
|
88
|
+
return new Order(
|
|
89
|
+
generateId(),
|
|
90
|
+
req.customerId,
|
|
91
|
+
req.items.map(i => new OrderItem(i)),
|
|
92
|
+
'pending',
|
|
93
|
+
req.items.reduce((sum, i) => sum + i.price * i.quantity, 0)
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
toRecord(): OrderRecord {
|
|
98
|
+
return {
|
|
99
|
+
id: this.id,
|
|
100
|
+
customer_id: this.customerId,
|
|
101
|
+
items: JSON.stringify(this.items),
|
|
102
|
+
status: this.status,
|
|
103
|
+
total: this.total
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
validate(): Result<Order> {
|
|
108
|
+
if (this.items.length === 0) {
|
|
109
|
+
return Err('Order must have at least one item');
|
|
110
|
+
}
|
|
111
|
+
if (this.total < 0) {
|
|
112
|
+
return Err('Order total cannot be negative');
|
|
113
|
+
}
|
|
114
|
+
return Ok(this);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
canCancel(): boolean {
|
|
118
|
+
return ['pending', 'confirmed'].includes(this.status);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Effectful Edge
|
|
124
|
+
|
|
125
|
+
IO-performing components that interact with the outside world.
|
|
126
|
+
|
|
127
|
+
### Router
|
|
128
|
+
|
|
129
|
+
**Responsibility:** HTTP request handling, parsing, response formatting.
|
|
130
|
+
|
|
131
|
+
**Characteristics:**
|
|
132
|
+
- Parses HTTP requests into domain types
|
|
133
|
+
- Calls service layer with parsed data
|
|
134
|
+
- Formats service results into HTTP responses
|
|
135
|
+
- Handles HTTP-specific concerns (status codes, headers)
|
|
136
|
+
- No business logic
|
|
137
|
+
|
|
138
|
+
**Example:**
|
|
139
|
+
```typescript
|
|
140
|
+
router.post('/orders', async (req, res) => {
|
|
141
|
+
const result = await orderService.createOrder(req.body);
|
|
142
|
+
|
|
143
|
+
if (result.ok) {
|
|
144
|
+
res.status(201).json(result.value.toResponse());
|
|
145
|
+
} else {
|
|
146
|
+
res.status(400).json({ error: result.error });
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Repository
|
|
152
|
+
|
|
153
|
+
**Responsibility:** Data persistence and retrieval.
|
|
154
|
+
|
|
155
|
+
**Characteristics:**
|
|
156
|
+
- Implements data access interface used by services
|
|
157
|
+
- Converts between domain entities and database records
|
|
158
|
+
- Handles database queries and transactions
|
|
159
|
+
- No business logic or validation
|
|
160
|
+
|
|
161
|
+
**Example:**
|
|
162
|
+
```typescript
|
|
163
|
+
class OrderRepository {
|
|
164
|
+
async save(record: OrderRecord): Promise<OrderRecord> {
|
|
165
|
+
return await db.orders.create(record);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async findById(id: string): Promise<OrderRecord | null> {
|
|
169
|
+
return await db.orders.findOne({ id });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Component Matrix
|
|
175
|
+
|
|
176
|
+
Quick reference for where things belong:
|
|
177
|
+
|
|
178
|
+
| Concern | Component | Layer | Testability |
|
|
179
|
+
|---------|-----------|-------|-------------|
|
|
180
|
+
| Domain model | Entity | Core | Unit test (pure) |
|
|
181
|
+
| Validation | Entity | Core | Unit test (pure) |
|
|
182
|
+
| Business rules | Entity | Core | Unit test (pure) |
|
|
183
|
+
| Orchestration | Service | Core | Unit test (stub repos) |
|
|
184
|
+
| Data transforms | Entity | Core | Unit test (pure) |
|
|
185
|
+
| HTTP parsing | Router | Edge | Integration test |
|
|
186
|
+
| Data access | Repository | Edge | Integration test |
|
|
187
|
+
| External APIs | Client | Edge | Integration test |
|
|
188
|
+
| Event handling | Consumer | Edge | Integration test |
|
|
189
|
+
| Event publishing | Producer | Edge | Integration test |
|
|
190
|
+
|
|
191
|
+
## Task Breakdown
|
|
192
|
+
|
|
193
|
+
### Bottom-Up Dependency Ordering
|
|
194
|
+
|
|
195
|
+
Implementation order follows dependency chain:
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
1. Entity → Domain models, validation, transforms
|
|
199
|
+
2. Repository → Data access interfaces and implementations
|
|
200
|
+
3. Service → Business logic orchestration
|
|
201
|
+
4. Router → HTTP endpoints
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Rationale:** Each layer depends on layers below. Can't implement service without entity, can't implement router without service.
|
|
205
|
+
|
|
206
|
+
### Task Granularity
|
|
207
|
+
|
|
208
|
+
**One task per layer:**
|
|
209
|
+
- Implement Order entity with validation
|
|
210
|
+
- Implement OrderRepository with data access
|
|
211
|
+
- Implement OrderService with business logic
|
|
212
|
+
- Implement order API endpoints
|
|
213
|
+
|
|
214
|
+
**For complex features, break down further:**
|
|
215
|
+
- Entity: Order, OrderItem, OrderStatus
|
|
216
|
+
- Repository: OrderRepository, InventoryRepository
|
|
217
|
+
- Service: OrderService, PaymentService
|
|
218
|
+
- Router: Order routes, Payment routes
|
|
219
|
+
|
|
220
|
+
## Architect → Testing Flow
|
|
221
|
+
|
|
222
|
+
Architectural decisions inform testing strategy:
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
Architect Outputs → Testing Inputs
|
|
226
|
+
────────────────────────────────────────────────
|
|
227
|
+
Component responsibilities → What to test
|
|
228
|
+
Layer boundaries → Where to test
|
|
229
|
+
Pure vs effectful → Unit vs integration
|
|
230
|
+
Entity transformations → Property-based tests
|
|
231
|
+
Service orchestration → Stub-driven tests
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
The testing skill uses architectural structure to determine:
|
|
235
|
+
- What gets unit tested (core) vs integration tested (edge)
|
|
236
|
+
- Where to place test boundaries
|
|
237
|
+
- What to stub and what to test for real
|
|
238
|
+
- What test cases validate business rules
|
|
239
|
+
|
|
240
|
+
## Reference Materials
|
|
241
|
+
|
|
242
|
+
For detailed patterns and examples:
|
|
243
|
+
|
|
244
|
+
- **See [references/ddd-patterns.md](references/ddd-patterns.md)** - Aggregates, Value Objects, Domain Events, Bounded Contexts, composition patterns
|
|
245
|
+
- **See [references/data-modeling.md](references/data-modeling.md)** - Entity design principles, schema patterns, access pattern optimization, data transformation
|
|
246
|
+
- **See [references/api-design.md](references/api-design.md)** - REST conventions, request/response contracts, error handling, versioning patterns
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# API Design Reference
|
|
2
|
+
|
|
3
|
+
REST conventions, request/response contracts, and error handling patterns.
|
|
4
|
+
|
|
5
|
+
## REST Conventions
|
|
6
|
+
|
|
7
|
+
### Resource-Oriented Endpoints
|
|
8
|
+
|
|
9
|
+
**Standard CRUD operations:**
|
|
10
|
+
```
|
|
11
|
+
GET /orders # List orders
|
|
12
|
+
POST /orders # Create order
|
|
13
|
+
GET /orders/:id # Get single order
|
|
14
|
+
PATCH /orders/:id # Update order
|
|
15
|
+
DELETE /orders/:id # Delete order
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Nested resources:**
|
|
19
|
+
```
|
|
20
|
+
GET /orders/:orderId/items # List order items
|
|
21
|
+
POST /orders/:orderId/items # Add item to order
|
|
22
|
+
GET /orders/:orderId/items/:itemId # Get order item
|
|
23
|
+
PATCH /orders/:orderId/items/:itemId # Update order item
|
|
24
|
+
DELETE /orders/:orderId/items/:itemId # Delete order item
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Actions (Non-CRUD Operations)
|
|
28
|
+
|
|
29
|
+
**Use POST for state transitions:**
|
|
30
|
+
```
|
|
31
|
+
POST /orders/:id/cancel # Cancel order
|
|
32
|
+
POST /orders/:id/ship # Ship order
|
|
33
|
+
POST /orders/:id/confirm # Confirm order
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Reasoning:** These modify state but aren't CRUD operations. They represent domain actions.
|
|
37
|
+
|
|
38
|
+
**Avoid:**
|
|
39
|
+
```
|
|
40
|
+
PATCH /orders/:id { "action": "cancel" } # Unclear what changed
|
|
41
|
+
GET /orders/:id/cancel # GET shouldn't have side effects
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Request/Response Contracts
|
|
45
|
+
|
|
46
|
+
### Request Types
|
|
47
|
+
|
|
48
|
+
**Create request (POST /resource):**
|
|
49
|
+
```typescript
|
|
50
|
+
interface CreateOrderRequest {
|
|
51
|
+
customerId: string;
|
|
52
|
+
items: Array<{
|
|
53
|
+
productId: string;
|
|
54
|
+
quantity: number;
|
|
55
|
+
}>;
|
|
56
|
+
shippingAddress?: {
|
|
57
|
+
street: string;
|
|
58
|
+
city: string;
|
|
59
|
+
zipCode: string;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Update request (PATCH /resource/:id):**
|
|
65
|
+
```typescript
|
|
66
|
+
interface UpdateOrderRequest {
|
|
67
|
+
status?: 'pending' | 'confirmed' | 'shipped';
|
|
68
|
+
shippingAddress?: Address;
|
|
69
|
+
// Only fields that can be updated
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Query parameters:**
|
|
74
|
+
```
|
|
75
|
+
GET /orders?customerId=cust-123&status=pending&limit=20&offset=0
|
|
76
|
+
|
|
77
|
+
interface ListOrdersQuery {
|
|
78
|
+
customerId?: string;
|
|
79
|
+
status?: OrderStatus;
|
|
80
|
+
limit?: number; // Default 20, max 100
|
|
81
|
+
offset?: number; // Pagination offset
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Response Types
|
|
86
|
+
|
|
87
|
+
**Single resource (GET /resource/:id):**
|
|
88
|
+
```typescript
|
|
89
|
+
interface OrderResponse {
|
|
90
|
+
id: string;
|
|
91
|
+
customerId: string;
|
|
92
|
+
items: OrderItem[];
|
|
93
|
+
status: 'pending' | 'confirmed' | 'shipped' | 'delivered';
|
|
94
|
+
total: number;
|
|
95
|
+
createdAt: string; // ISO 8601
|
|
96
|
+
updatedAt: string;
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**List response (GET /resource):**
|
|
101
|
+
```typescript
|
|
102
|
+
interface ListOrdersResponse {
|
|
103
|
+
data: OrderResponse[];
|
|
104
|
+
pagination: {
|
|
105
|
+
total: number;
|
|
106
|
+
limit: number;
|
|
107
|
+
offset: number;
|
|
108
|
+
hasMore: boolean;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Create response (POST /resource):**
|
|
114
|
+
```typescript
|
|
115
|
+
// Return created resource with 201 status
|
|
116
|
+
interface CreateOrderResponse extends OrderResponse {
|
|
117
|
+
id: string; // Generated by server
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Timestamp Conventions
|
|
122
|
+
|
|
123
|
+
**Use ISO 8601 for all timestamps:**
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"createdAt": "2024-01-21T10:30:45Z",
|
|
127
|
+
"updatedAt": "2024-01-21T10:30:45Z",
|
|
128
|
+
"deletedAt": null
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Never:** Unix timestamps, custom formats, or timezone abbreviations
|
|
133
|
+
|
|
134
|
+
## Error Handling
|
|
135
|
+
|
|
136
|
+
### HTTP Status Codes
|
|
137
|
+
|
|
138
|
+
**Success:**
|
|
139
|
+
- `200 OK` - GET, PATCH succeeded
|
|
140
|
+
- `201 Created` - POST created resource
|
|
141
|
+
- `204 No Content` - DELETE or update with no response body
|
|
142
|
+
|
|
143
|
+
**Client Errors:**
|
|
144
|
+
- `400 Bad Request` - Invalid input validation
|
|
145
|
+
- `401 Unauthorized` - Missing authentication
|
|
146
|
+
- `403 Forbidden` - Authenticated but insufficient permissions
|
|
147
|
+
- `404 Not Found` - Resource not found
|
|
148
|
+
- `409 Conflict` - State violation (can't transition state)
|
|
149
|
+
- `422 Unprocessable Entity` - Validation failed
|
|
150
|
+
|
|
151
|
+
**Server Errors:**
|
|
152
|
+
- `500 Internal Server Error` - Unhandled exception
|
|
153
|
+
- `503 Service Unavailable` - Temporary outage
|
|
154
|
+
|
|
155
|
+
### Error Response Format
|
|
156
|
+
|
|
157
|
+
**Validation error (400):**
|
|
158
|
+
```typescript
|
|
159
|
+
interface ValidationErrorResponse {
|
|
160
|
+
error: 'Validation failed';
|
|
161
|
+
code: 'VALIDATION_ERROR';
|
|
162
|
+
details: {
|
|
163
|
+
field: string;
|
|
164
|
+
message: string;
|
|
165
|
+
}[];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Example
|
|
169
|
+
{
|
|
170
|
+
"error": "Validation failed",
|
|
171
|
+
"code": "VALIDATION_ERROR",
|
|
172
|
+
"details": [
|
|
173
|
+
{ "field": "items", "message": "Must have at least one item" },
|
|
174
|
+
{ "field": "customerId", "message": "Invalid customer ID format" }
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Business rule violation (409):**
|
|
180
|
+
```typescript
|
|
181
|
+
interface ConflictErrorResponse {
|
|
182
|
+
error: string;
|
|
183
|
+
code: string;
|
|
184
|
+
details?: Record<string, unknown>;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Example
|
|
188
|
+
{
|
|
189
|
+
"error": "Cannot cancel shipped order",
|
|
190
|
+
"code": "ORDER_ALREADY_SHIPPED",
|
|
191
|
+
"details": {
|
|
192
|
+
"status": "shipped",
|
|
193
|
+
"orderId": "order-123"
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Not found (404):**
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"error": "Order not found",
|
|
202
|
+
"code": "ORDER_NOT_FOUND",
|
|
203
|
+
"details": { "id": "order-123" }
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Generic error (500):**
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"error": "Internal server error",
|
|
211
|
+
"code": "INTERNAL_ERROR"
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Error Code Convention
|
|
216
|
+
|
|
217
|
+
Use SCREAMING_SNAKE_CASE for error codes:
|
|
218
|
+
- `VALIDATION_ERROR` - Input validation failed
|
|
219
|
+
- `ORDER_NOT_FOUND` - Resource not found
|
|
220
|
+
- `ORDER_ALREADY_SHIPPED` - State violation
|
|
221
|
+
- `INSUFFICIENT_INVENTORY` - Business rule violation
|
|
222
|
+
- `AUTHENTICATION_REQUIRED` - Auth failed
|
|
223
|
+
- `PERMISSION_DENIED` - Authz failed
|
|
224
|
+
|
|
225
|
+
## Versioning Patterns
|
|
226
|
+
|
|
227
|
+
### URL Versioning
|
|
228
|
+
|
|
229
|
+
**Include version in path:**
|
|
230
|
+
```
|
|
231
|
+
GET /v1/orders
|
|
232
|
+
GET /v2/orders
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Pros:** Explicit, clear to consumers
|
|
236
|
+
**Cons:** Requires multiple code paths
|
|
237
|
+
|
|
238
|
+
### Accept Header Versioning
|
|
239
|
+
|
|
240
|
+
**Use media type version:**
|
|
241
|
+
```
|
|
242
|
+
GET /orders
|
|
243
|
+
Accept: application/vnd.myapi.v2+json
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**Pros:** Cleaner URLs, single endpoint
|
|
247
|
+
**Cons:** Less discoverable
|
|
248
|
+
|
|
249
|
+
### Pragmatic Approach
|
|
250
|
+
|
|
251
|
+
1. Start without versioning (all responses same format)
|
|
252
|
+
2. Add versioning when you need breaking changes
|
|
253
|
+
3. Support at least 2 versions during transition
|
|
254
|
+
4. Provide clear deprecation timeline
|
|
255
|
+
|
|
256
|
+
### Backwards Compatibility
|
|
257
|
+
|
|
258
|
+
**Adding fields (safe):**
|
|
259
|
+
```typescript
|
|
260
|
+
// v1
|
|
261
|
+
interface OrderResponse {
|
|
262
|
+
id: string;
|
|
263
|
+
total: number;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// v2 (backwards compatible)
|
|
267
|
+
interface OrderResponse {
|
|
268
|
+
id: string;
|
|
269
|
+
total: number;
|
|
270
|
+
tax?: number; // New optional field
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**Removing fields (breaking change):**
|
|
275
|
+
```typescript
|
|
276
|
+
// Requires new API version
|
|
277
|
+
// v1 has 'total'
|
|
278
|
+
// v2 has 'subtotal' and 'tax'
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Example: Complete Order API
|
|
282
|
+
|
|
283
|
+
### Endpoints
|
|
284
|
+
|
|
285
|
+
```
|
|
286
|
+
POST /orders
|
|
287
|
+
Create order
|
|
288
|
+
Request: CreateOrderRequest
|
|
289
|
+
Response: 201 OrderResponse
|
|
290
|
+
|
|
291
|
+
GET /orders
|
|
292
|
+
List orders
|
|
293
|
+
Query: ListOrdersQuery
|
|
294
|
+
Response: 200 ListOrdersResponse
|
|
295
|
+
|
|
296
|
+
GET /orders/:id
|
|
297
|
+
Get order
|
|
298
|
+
Response: 200 OrderResponse
|
|
299
|
+
|
|
300
|
+
PATCH /orders/:id
|
|
301
|
+
Update order
|
|
302
|
+
Request: UpdateOrderRequest
|
|
303
|
+
Response: 200 OrderResponse
|
|
304
|
+
|
|
305
|
+
POST /orders/:id/cancel
|
|
306
|
+
Cancel order
|
|
307
|
+
Response: 200 OrderResponse
|
|
308
|
+
|
|
309
|
+
DELETE /orders/:id
|
|
310
|
+
Delete order
|
|
311
|
+
Response: 204 (no body)
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Implementation Pattern
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
// Router handles HTTP concerns
|
|
318
|
+
router.post('/orders', async (req, res) => {
|
|
319
|
+
// 1. Parse request
|
|
320
|
+
const request: CreateOrderRequest = req.body;
|
|
321
|
+
|
|
322
|
+
// 2. Validate input (preliminary)
|
|
323
|
+
if (!request.customerId) {
|
|
324
|
+
return res.status(400).json({
|
|
325
|
+
error: 'Validation failed',
|
|
326
|
+
code: 'VALIDATION_ERROR',
|
|
327
|
+
details: [{ field: 'customerId', message: 'Required' }]
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// 3. Call service
|
|
332
|
+
const result = await orderService.createOrder(request);
|
|
333
|
+
|
|
334
|
+
// 4. Handle result
|
|
335
|
+
if (result.ok) {
|
|
336
|
+
return res.status(201).json(result.value.toResponse());
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// 5. Return error
|
|
340
|
+
res.status(409).json({
|
|
341
|
+
error: result.error,
|
|
342
|
+
code: result.code || 'UNKNOWN_ERROR'
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
```
|