@precepts/standards 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/LICENSE +30 -0
- package/README.md +115 -0
- package/package.json +40 -0
- package/schema/document-standard-template.md +139 -0
- package/schema/standards.schema.json +154 -0
- package/standards/integration/governance/_category_.json +1 -0
- package/standards/integration/governance/integration-styles.md +56 -0
- package/standards/integration/index.md +9 -0
- package/standards/integration/standards/_category_.json +1 -0
- package/standards/integration/standards/api/_category_.json +1 -0
- package/standards/integration/standards/api/error-handling.md +250 -0
- package/standards/integration/standards/api/resource-design.md +286 -0
- package/standards/integration/standards/data-formats/_category_.json +1 -0
- package/standards/integration/standards/data-formats/character-encoding.md +206 -0
- package/standards/integration/standards/data-formats/date-format.md +102 -0
- package/standards/integration/standards/data-formats/datetime-formats.md +265 -0
- package/standards/integration/standards/data-formats/monetary-format.md +61 -0
- package/standards/integration/standards/events/_category_.json +1 -0
- package/standards/integration/standards/events/event-envelope.md +270 -0
- package/standards/integration/standards/foundational/_category_.json +1 -0
- package/standards/integration/standards/foundational/naming-conventions.md +334 -0
- package/standards/integration/standards/observability/_category_.json +1 -0
- package/standards/integration/standards/observability/integration-observability.md +226 -0
- package/standards/integration/standards/resilience/_category_.json +1 -0
- package/standards/integration/standards/resilience/integration-resilience-patterns.md +291 -0
- package/standards/integration/standards/resilience/retry-policy.md +268 -0
- package/standards/integration/standards/resilience/timeout.md +269 -0
- package/standards/integration/standards/versioning/_category_.json +1 -0
- package/standards/integration/standards/versioning/backward-forward-compatibility.md +230 -0
- package/standards/product/Guidelines/_category_.json +1 -0
- package/standards/product/Guidelines/requirement-document.md +54 -0
- package/standards/product/index.md +9 -0
- package/standards/project-management/index.md +9 -0
- package/standards/ux/index.md +9 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
---
|
|
2
|
+
identifier: "INTG-STD-009"
|
|
3
|
+
name: "API Error Handling"
|
|
4
|
+
version: "1.0.0"
|
|
5
|
+
status: "MANDATORY"
|
|
6
|
+
|
|
7
|
+
domain: "INTEGRATION"
|
|
8
|
+
documentType: "standard"
|
|
9
|
+
category: "error-handling"
|
|
10
|
+
appliesTo: ["api", "webhooks"]
|
|
11
|
+
|
|
12
|
+
lastUpdated: "2026-03-28"
|
|
13
|
+
owner: "Integration Architecture Board"
|
|
14
|
+
|
|
15
|
+
standardsCompliance:
|
|
16
|
+
iso: []
|
|
17
|
+
rfc: ["RFC-9457", "RFC-9110", "RFC-2119"]
|
|
18
|
+
w3c: []
|
|
19
|
+
other: ["Zalando-RESTful-API-Guidelines", "OWASP-API-Security-Top-10"]
|
|
20
|
+
|
|
21
|
+
taxonomy:
|
|
22
|
+
capability: "api-design"
|
|
23
|
+
subCapability: "error-handling"
|
|
24
|
+
layer: "contract"
|
|
25
|
+
|
|
26
|
+
enforcement:
|
|
27
|
+
method: "automated"
|
|
28
|
+
validationRules:
|
|
29
|
+
contentType: "application/problem+json"
|
|
30
|
+
requiredFields: ["type", "title", "status"]
|
|
31
|
+
rejectionCriteria:
|
|
32
|
+
- "Stack traces in error responses"
|
|
33
|
+
- "Internal implementation details in error messages"
|
|
34
|
+
- "Non-standard error response format"
|
|
35
|
+
- "Missing correlation ID in error responses"
|
|
36
|
+
|
|
37
|
+
dependsOn: ["INTG-STD-004", "INTG-STD-008"]
|
|
38
|
+
supersedes: ""
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
# Error Handling
|
|
42
|
+
|
|
43
|
+
## Purpose
|
|
44
|
+
|
|
45
|
+
All HTTP API error responses **MUST** use the RFC 9457 Problem Details format (`application/problem+json`). This eliminates bespoke error formats, enables machine-readable error processing by clients and AI agents, and prevents information leakage. RFC 9457 obsoletes RFC 7807 and aligns with industry guidelines from Zalando, Microsoft, and Google.
|
|
46
|
+
|
|
47
|
+
## Rules
|
|
48
|
+
|
|
49
|
+
### R-1: Content-Type
|
|
50
|
+
|
|
51
|
+
- All error responses **MUST** use media type `application/problem+json`.
|
|
52
|
+
- Servers **MUST** set `Content-Type: application/problem+json` for all 4xx and 5xx responses carrying a body.
|
|
53
|
+
- Servers **MUST NOT** return errors using `application/json` or any other media type.
|
|
54
|
+
|
|
55
|
+
### R-2: Required Fields
|
|
56
|
+
|
|
57
|
+
Every Problem Details response **MUST** include:
|
|
58
|
+
|
|
59
|
+
| Field | Type | Description |
|
|
60
|
+
| --------------- | ------- | -------------------------------------------------------- |
|
|
61
|
+
| `type` | string | URI reference identifying the problem type |
|
|
62
|
+
| `title` | string | Short, human-readable summary of the problem type |
|
|
63
|
+
| `status` | integer | HTTP status code (**MUST** match actual response status) |
|
|
64
|
+
| `detail` | string | Human-readable explanation specific to this occurrence |
|
|
65
|
+
| `instance` | string | URI reference identifying this specific occurrence |
|
|
66
|
+
| `correlationId` | string | UUID v4 for end-to-end correlation |
|
|
67
|
+
|
|
68
|
+
### R-3: Recommended Extension Fields
|
|
69
|
+
|
|
70
|
+
The following fields **SHOULD** be included where applicable:
|
|
71
|
+
|
|
72
|
+
| Field | Type | Description |
|
|
73
|
+
| ----------- | ------ | ---------------------------------------------- |
|
|
74
|
+
| `errorCode` | string | Machine-readable error code (see R-6) |
|
|
75
|
+
| `timestamp` | string | ISO-8601 UTC timestamp of the error occurrence |
|
|
76
|
+
| `errors` | array | Per-field validation errors (see R-5) |
|
|
77
|
+
|
|
78
|
+
### R-4: Type URI Requirements
|
|
79
|
+
|
|
80
|
+
- The `type` field **MUST** be an absolute URI following the pattern `https://{domain}/problems/{error-category}`.
|
|
81
|
+
- Organizations **SHOULD** host human-readable documentation at the `type` URI.
|
|
82
|
+
- When no specific problem type applies, `"about:blank"` **MUST** be used and `title` **SHOULD** match the standard HTTP status phrase from RFC 9110.
|
|
83
|
+
|
|
84
|
+
### R-5: Validation Error Array
|
|
85
|
+
|
|
86
|
+
For 400 and 422 responses involving input validation, the response **MUST** include an `errors` array. Each entry **MUST** contain:
|
|
87
|
+
|
|
88
|
+
| Field | Type | Req. | Description |
|
|
89
|
+
| --------- | ------ | ------------ | --------------------------------------------------------- |
|
|
90
|
+
| `field` | string | **MUST** | JSON Pointer (RFC 6901) or dot-notation path to the field |
|
|
91
|
+
| `message` | string | **MUST** | Human-readable description of the validation failure |
|
|
92
|
+
| `code` | string | **SHOULD** | Machine-readable validation error code |
|
|
93
|
+
| `value` | any | **MAY** | The rejected value (**MUST NOT** include sensitive data) |
|
|
94
|
+
|
|
95
|
+
### R-6: Machine-Readable Error Codes
|
|
96
|
+
|
|
97
|
+
- Each problem type **SHOULD** define a unique `errorCode` in `UPPER_SNAKE_CASE`.
|
|
98
|
+
- Error codes **MUST** be stable across releases and **MUST NOT** be removed without a major version increment.
|
|
99
|
+
- Error codes **SHOULD** follow the pattern `{DOMAIN}_{CATEGORY}_{SPECIFIC}` (e.g., `ORDER_VALIDATION_INVALID_QUANTITY`).
|
|
100
|
+
|
|
101
|
+
### R-7: Correlation ID
|
|
102
|
+
|
|
103
|
+
- Every error response **MUST** include `correlationId` in the body and `X-Correlation-ID` in the response header; values **MUST** match.
|
|
104
|
+
- If the inbound request includes `X-Correlation-ID`, the server **MUST** propagate that value.
|
|
105
|
+
- If absent, the server **MUST** generate a new UUID v4.
|
|
106
|
+
- The `correlationId` **MUST** appear in all log entries related to the failed request.
|
|
107
|
+
|
|
108
|
+
### R-8: Status Code Consistency
|
|
109
|
+
|
|
110
|
+
- The `status` field **MUST** match the HTTP response status code.
|
|
111
|
+
- Servers **MUST** use the most specific status code applicable and **MUST NOT** use generic codes (e.g., 400) when a more specific code (e.g., 422) applies.
|
|
112
|
+
|
|
113
|
+
### R-9: HTTP Status Code Usage
|
|
114
|
+
|
|
115
|
+
| Status | Title | When to Use |
|
|
116
|
+
| ------ | --------------------- | ------------------------------------------------------------------------------------ |
|
|
117
|
+
| 400 | Bad Request | Malformed syntax, invalid JSON, missing required headers |
|
|
118
|
+
| 401 | Unauthorized | Missing, expired, or malformed authentication credentials |
|
|
119
|
+
| 403 | Forbidden | Valid credentials but insufficient permissions |
|
|
120
|
+
| 404 | Not Found | Requested resource does not exist |
|
|
121
|
+
| 409 | Conflict | State conflict (optimistic locking, duplicate creation) |
|
|
122
|
+
| 422 | Unprocessable Content | Well-formed but semantically invalid (business rules, field validation) |
|
|
123
|
+
| 429 | Too Many Requests | Rate limit exceeded; **MUST** include `Retry-After` header |
|
|
124
|
+
| 500 | Internal Server Error | Unexpected server failure; **MUST NOT** expose internals |
|
|
125
|
+
| 502 | Bad Gateway | Upstream dependency returned an invalid response |
|
|
126
|
+
| 503 | Service Unavailable | Temporary outage or maintenance; **SHOULD** include `Retry-After` |
|
|
127
|
+
|
|
128
|
+
Use **400** for structurally malformed requests (bad JSON, wrong content type). Use **422** for well-formed requests that violate business rules or field validation.
|
|
129
|
+
|
|
130
|
+
### R-10: Title Stability
|
|
131
|
+
|
|
132
|
+
- The `title` field **SHOULD NOT** change between occurrences of the same problem type.
|
|
133
|
+
- Clients **MUST NOT** parse `title` or `detail` for programmatic decisions; use `type`, `status`, and `errorCode` instead.
|
|
134
|
+
|
|
135
|
+
### R-11: Security Requirements
|
|
136
|
+
|
|
137
|
+
- Error responses **MUST NOT** include stack traces, exception class names, internal method names, SQL queries, database details, internal hostnames, IP addresses, file paths, or software version numbers.
|
|
138
|
+
- The `detail` field **MUST** describe the problem from the client's perspective, not internal state.
|
|
139
|
+
- For 5xx errors, `detail` **SHOULD** use a generic message and log full diagnostics server-side via `correlationId`.
|
|
140
|
+
- Validation error `value` fields **MUST NOT** echo sensitive data (passwords, tokens, personal identifiers).
|
|
141
|
+
|
|
142
|
+
### R-12: Retry Guidance
|
|
143
|
+
|
|
144
|
+
- 429 responses **MUST** include a `Retry-After` header.
|
|
145
|
+
- 503 responses **SHOULD** include a `Retry-After` header when the outage duration is known.
|
|
146
|
+
- Retryable errors **MAY** include a `retryAfterSeconds` integer extension field.
|
|
147
|
+
|
|
148
|
+
### R-13: Content Negotiation
|
|
149
|
+
|
|
150
|
+
- If a client sends `Accept: application/problem+json`, the server **MUST** respond with Problem Details JSON.
|
|
151
|
+
- If a client sends `Accept: application/json`, the server **SHOULD** still respond with Problem Details JSON using `Content-Type: application/problem+json`.
|
|
152
|
+
- XML representation (`application/problem+xml`) **MAY** be supported but is not required.
|
|
153
|
+
|
|
154
|
+
## Examples
|
|
155
|
+
|
|
156
|
+
### Standard Error Response
|
|
157
|
+
|
|
158
|
+
```json
|
|
159
|
+
{
|
|
160
|
+
"type": "https://api.example.com/problems/malformed-request",
|
|
161
|
+
"title": "Malformed Request",
|
|
162
|
+
"status": 400,
|
|
163
|
+
"detail": "The request body contains invalid JSON. Expected a comma at position 42.",
|
|
164
|
+
"instance": "/logs/errors/a937b-41f2",
|
|
165
|
+
"correlationId": "550e8400-e29b-41d4-a716-446655440000",
|
|
166
|
+
"errorCode": "REQUEST_PARSE_INVALID_JSON",
|
|
167
|
+
"timestamp": "2026-03-28T14:30:00.000Z"
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Validation Error Response
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"type": "https://api.example.com/problems/validation-error",
|
|
176
|
+
"title": "Validation Error",
|
|
177
|
+
"status": 422,
|
|
178
|
+
"detail": "The request contains 2 validation errors that must be corrected.",
|
|
179
|
+
"instance": "/logs/errors/f682g-d6h5",
|
|
180
|
+
"correlationId": "3c6e0b8a-9c0a-45af-9db8-0b2e1f4b5c7d",
|
|
181
|
+
"errorCode": "REQUEST_VALIDATION_FAILED",
|
|
182
|
+
"timestamp": "2026-03-28T14:35:00.000Z",
|
|
183
|
+
"errors": [
|
|
184
|
+
{
|
|
185
|
+
"field": "/email",
|
|
186
|
+
"message": "Must be a valid email address.",
|
|
187
|
+
"code": "FIELD_FORMAT_INVALID",
|
|
188
|
+
"value": "not-an-email"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"field": "/quantity",
|
|
192
|
+
"message": "Must be greater than zero.",
|
|
193
|
+
"code": "FIELD_RANGE_BELOW_MINIMUM",
|
|
194
|
+
"value": -5
|
|
195
|
+
}
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Enforcement Rules
|
|
201
|
+
|
|
202
|
+
### Gateway-Level
|
|
203
|
+
|
|
204
|
+
- API gateways **MUST** intercept non-compliant error responses and transform them into valid Problem Details format.
|
|
205
|
+
- API gateways **MUST** strip fields matching sensitive patterns (stack traces, SQL, internal paths).
|
|
206
|
+
- API gateways **MUST** inject `correlationId` and `X-Correlation-ID` if the backend has not provided them.
|
|
207
|
+
|
|
208
|
+
### Build-Time
|
|
209
|
+
|
|
210
|
+
- OpenAPI specs **MUST** define `application/problem+json` for all 4xx and 5xx status codes.
|
|
211
|
+
- Contract tests **MUST** validate error responses conform to the Problem Details schema.
|
|
212
|
+
- Static analysis **SHOULD** flag error response definitions that do not reference the Problem Details schema.
|
|
213
|
+
|
|
214
|
+
### Automated Checks
|
|
215
|
+
|
|
216
|
+
The following **MUST** be enforced at the API gateway or integration test layer:
|
|
217
|
+
|
|
218
|
+
1. All 4xx/5xx responses have `Content-Type: application/problem+json`.
|
|
219
|
+
2. Required fields (`type`, `title`, `status`, `detail`, `instance`, `correlationId`) are present.
|
|
220
|
+
3. The `status` field matches the HTTP response status code.
|
|
221
|
+
4. `X-Correlation-ID` header is present and matches body `correlationId`.
|
|
222
|
+
5. Response bodies do not match patterns for stack traces, SQL keywords, or internal hostnames.
|
|
223
|
+
6. Responses with status 400 or 422 reporting field issues include the `errors` array.
|
|
224
|
+
|
|
225
|
+
### Rejection Criteria
|
|
226
|
+
|
|
227
|
+
CI/CD **MUST** reject: non-`application/problem+json` errors, missing required fields, sensitive data in response bodies (stack traces, SQL, internal paths, hostnames), and missing or mismatched `X-Correlation-ID` headers.
|
|
228
|
+
|
|
229
|
+
## References
|
|
230
|
+
|
|
231
|
+
- [RFC 9457 - Problem Details for HTTP APIs](https://www.rfc-editor.org/rfc/rfc9457.html)
|
|
232
|
+
- [RFC 9110 - HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110.html)
|
|
233
|
+
- [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119.html) / [RFC 6901](https://www.rfc-editor.org/rfc/rfc6901.html)
|
|
234
|
+
- [Zalando RESTful API Guidelines](https://opensource.zalando.com/restful-api-guidelines/) / [OWASP Error Handling](https://cheatsheetseries.owasp.org/cheatsheets/Error_Handling_Cheat_Sheet.html)
|
|
235
|
+
|
|
236
|
+
## Rationale
|
|
237
|
+
|
|
238
|
+
**Why RFC 9457?** Current IETF standard for HTTP error responses, widely supported by frameworks and gateways, eliminating the cost of bespoke formats.
|
|
239
|
+
|
|
240
|
+
**Why require all core fields plus correlationId?** `type` enables deterministic branching, `status` survives proxy transformations, `instance` links alerts to occurrences, and `correlationId` is essential for distributed tracing.
|
|
241
|
+
|
|
242
|
+
**Why strict security rules?** OWASP identifies error information leakage as a common API vulnerability - stack traces, SQL, and hostnames aid targeted exploits.
|
|
243
|
+
|
|
244
|
+
**Why the errors[] array?** Returning all validation failures at once avoids trial-and-error loops and improves developer experience.
|
|
245
|
+
|
|
246
|
+
## Version History
|
|
247
|
+
|
|
248
|
+
| Version | Date | Change |
|
|
249
|
+
| ------- | ---------- | ------------------ |
|
|
250
|
+
| 1.0.0 | 2026-03-28 | Initial definition |
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
---
|
|
2
|
+
identifier: "INTG-STD-008"
|
|
3
|
+
name: "API Resource Design and Naming"
|
|
4
|
+
version: "1.0.0"
|
|
5
|
+
status: "MANDATORY"
|
|
6
|
+
|
|
7
|
+
domain: "INTEGRATION"
|
|
8
|
+
documentType: "standard"
|
|
9
|
+
category: "protocol"
|
|
10
|
+
appliesTo: ["api"]
|
|
11
|
+
|
|
12
|
+
lastUpdated: "2026-03-28"
|
|
13
|
+
owner: "Integration Architecture Board"
|
|
14
|
+
|
|
15
|
+
standardsCompliance:
|
|
16
|
+
iso: []
|
|
17
|
+
rfc: ["RFC-9110", "RFC-9205"]
|
|
18
|
+
w3c: []
|
|
19
|
+
other: ["Google-Cloud-API-Design-Guide", "Zalando-RESTful-API-Guidelines", "Microsoft-REST-API-Guidelines"]
|
|
20
|
+
|
|
21
|
+
taxonomy:
|
|
22
|
+
capability: "api-design"
|
|
23
|
+
subCapability: "resource-modeling"
|
|
24
|
+
layer: "contract"
|
|
25
|
+
|
|
26
|
+
enforcement:
|
|
27
|
+
method: "hybrid"
|
|
28
|
+
validationRules:
|
|
29
|
+
urlPattern: "^/v[0-9]+/[a-z][a-z0-9-]*(/{[a-z_]+}(/[a-z][a-z0-9-]*)*)*$"
|
|
30
|
+
rejectionCriteria:
|
|
31
|
+
- "Verbs in URL path segments"
|
|
32
|
+
- "Singular nouns for collection endpoints"
|
|
33
|
+
- "Missing pagination on collection endpoints"
|
|
34
|
+
|
|
35
|
+
dependsOn: ["INTG-STD-004"]
|
|
36
|
+
supersedes: ""
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
# Resource Design
|
|
40
|
+
|
|
41
|
+
## Purpose
|
|
42
|
+
|
|
43
|
+
This standard defines the required structure for RESTful API resources, URL paths, HTTP method usage, collection patterns, and operation semantics across all integration boundaries. Consistent resource design makes APIs predictable, securable, and machine-governable. Field naming within payloads is governed by INTG-STD-004; this standard covers URL structure, HTTP semantics, and collection-level patterns.
|
|
44
|
+
|
|
45
|
+
> *Normative language (**MUST**, **MUST NOT**, **SHOULD**, **MAY**) follows RFC 2119 semantics.*
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Rules
|
|
50
|
+
|
|
51
|
+
### R-1: URL Structure
|
|
52
|
+
|
|
53
|
+
Every API URL **MUST** follow this pattern:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
/v{major}/{collection}/{resource_id}/{sub-collection}/{sub_resource_id}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- The version prefix **MUST** be `v` + major version number as the first path segment.
|
|
60
|
+
- Path segments after the version **MUST** alternate between collection names and resource identifiers.
|
|
61
|
+
- Collection path segments **MUST** use plural nouns in kebab-case. They **MUST NOT** contain verbs.
|
|
62
|
+
- Resource identifier path parameter names **MUST** use snake_case (e.g., `{order_id}`).
|
|
63
|
+
- Identifier values **SHOULD** use a type prefix with an opaque string (e.g., `ord_82f3k`).
|
|
64
|
+
- Identifier values **MUST** be URL-safe and **MUST NOT** expose sequential integers as the sole identifier.
|
|
65
|
+
- URL paths **SHOULD NOT** exceed three levels of resource nesting.
|
|
66
|
+
- URLs **MUST NOT** contain trailing slashes or empty path segments.
|
|
67
|
+
- All API paths **MUST** match: `^/v[0-9]+/[a-z][a-z0-9-]*(/{[a-z_]+}(/[a-z][a-z0-9-]*)*)*$`
|
|
68
|
+
|
|
69
|
+
**Valid URLs:**
|
|
70
|
+
```
|
|
71
|
+
/v1/orders
|
|
72
|
+
/v1/line-items
|
|
73
|
+
/v1/orders/ord_82f3k
|
|
74
|
+
/v1/users/usr_19dk2/addresses/addr_7fj29
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Invalid URLs:**
|
|
78
|
+
```
|
|
79
|
+
/v1/order # singular
|
|
80
|
+
/v1/getOrders # verb, camelCase
|
|
81
|
+
/v1/order_items # snake_case path segment
|
|
82
|
+
/v1/orders/12345 # sequential integer
|
|
83
|
+
/v1/orders/ord_82f3k/ # trailing slash
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### R-2: HTTP Method Semantics
|
|
87
|
+
|
|
88
|
+
APIs **MUST** use HTTP methods per RFC 9110.
|
|
89
|
+
|
|
90
|
+
| Operation | Method | URL Target | Body | Idempotent | Safe |
|
|
91
|
+
|-----------|--------|------------|------|------------|------|
|
|
92
|
+
| List collection | `GET` | `/v1/resources` | None | Yes | Yes |
|
|
93
|
+
| Get resource | `GET` | `/v1/resources/{id}` | None | Yes | Yes |
|
|
94
|
+
| Create resource | `POST` | `/v1/resources` | Resource | No | No |
|
|
95
|
+
| Full replace | `PUT` | `/v1/resources/{id}` | Complete | Yes | No |
|
|
96
|
+
| Partial update | `PATCH` | `/v1/resources/{id}` | Partial | No* | No |
|
|
97
|
+
| Delete resource | `DELETE` | `/v1/resources/{id}` | None | Yes | No |
|
|
98
|
+
|
|
99
|
+
*PATCH **SHOULD** be designed to be idempotent where possible.
|
|
100
|
+
|
|
101
|
+
- `GET` **MUST** be safe and **MUST NOT** accept a request body. Responses **MUST** be cacheable unless marked otherwise.
|
|
102
|
+
- `POST` to a collection **MUST** return `201 Created` with a `Location` header. POST **SHOULD** accept an `Idempotency-Key` header for safe retries.
|
|
103
|
+
- `PUT` **MUST** replace the entire resource and **MUST** be idempotent. Omitted fields **MUST** reset to defaults.
|
|
104
|
+
- `PATCH` **MUST** use JSON Merge Patch (RFC 7396). Only fields present **MUST** be updated; absent fields **MUST** remain unchanged. To null a field, send JSON `null`.
|
|
105
|
+
- `DELETE` **MUST** be idempotent and **MUST NOT** accept a request body. Return `204 No Content` or `200 OK`.
|
|
106
|
+
- `GET` and `DELETE` **MUST NOT** accept a request body. `POST` **MUST NOT** be used for retrieval. `PUT` **MUST NOT** be used for partial updates.
|
|
107
|
+
|
|
108
|
+
### R-3: Collection Response Envelope
|
|
109
|
+
|
|
110
|
+
All collection responses **MUST** return a JSON object (never a bare array) with these fields:
|
|
111
|
+
|
|
112
|
+
| Field | Type | Required | Description |
|
|
113
|
+
|-------|------|----------|-------------|
|
|
114
|
+
| `data` | array | **MUST** | The array of resource objects |
|
|
115
|
+
| `pagination` | object | **MUST** | Pagination metadata |
|
|
116
|
+
|
|
117
|
+
**Valid collection response:**
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"data": [
|
|
121
|
+
{ "id": "ord_82f3k", "status": "confirmed" },
|
|
122
|
+
{ "id": "ord_93g4l", "status": "draft" }
|
|
123
|
+
],
|
|
124
|
+
"pagination": {
|
|
125
|
+
"has_more": true,
|
|
126
|
+
"next_cursor": "eyJpZCI6Im9yZF85M2c0bCJ9"
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### R-4: Pagination
|
|
132
|
+
|
|
133
|
+
All collection endpoints **MUST** support pagination. Cursor-based pagination **MUST** be the default. Offset-based pagination (`?page=`, `?offset=`) **MUST NOT** be used.
|
|
134
|
+
|
|
135
|
+
**Cursor request parameters:**
|
|
136
|
+
|
|
137
|
+
| Parameter | Type | Description |
|
|
138
|
+
|-----------|------|-------------|
|
|
139
|
+
| `limit` | integer | Max items to return. **MUST** default to a service-defined value (recommended: 20). **MUST NOT** exceed 100. |
|
|
140
|
+
| `starting_after` | string | Return items after this resource ID |
|
|
141
|
+
| `ending_before` | string | Return items before this resource ID |
|
|
142
|
+
|
|
143
|
+
`starting_after` and `ending_before` are mutually exclusive; sending both **MUST** return `400 Bad Request`.
|
|
144
|
+
|
|
145
|
+
**Cursor response fields:**
|
|
146
|
+
|
|
147
|
+
| Field | Type | Description |
|
|
148
|
+
|-------|------|-------------|
|
|
149
|
+
| `has_more` | boolean | `true` if additional items exist beyond this page |
|
|
150
|
+
| `next_cursor` | string or null | Opaque cursor for next page. `null` when `has_more` is `false`. |
|
|
151
|
+
|
|
152
|
+
Collection endpoints **SHOULD NOT** return `total_count` by default. If required, it **MAY** be opt-in via `?include_total=true`.
|
|
153
|
+
|
|
154
|
+
### R-5: Filtering and Sorting
|
|
155
|
+
|
|
156
|
+
Filtering **MUST** use query parameters with snake_case names. Range operators **SHOULD** use suffixed names:
|
|
157
|
+
|
|
158
|
+
| Suffix | Meaning | Example |
|
|
159
|
+
|--------|---------|---------|
|
|
160
|
+
| (none) | Exact match | `?status=confirmed` |
|
|
161
|
+
| `_gt`, `_gte` | Greater than (or equal) | `?created_at_gt=2026-03-01T00:00:00Z` |
|
|
162
|
+
| `_lt`, `_lte` | Less than (or equal) | `?amount_lte=500` |
|
|
163
|
+
| `_ne` | Not equal | `?status_ne=cancelled` |
|
|
164
|
+
| `_in` | In set (comma-separated) | `?status_in=draft,confirmed` |
|
|
165
|
+
|
|
166
|
+
Sorting **MUST** use the `sort` query parameter with field names optionally prefixed by `-` for descending. Multiple fields **MUST** be comma-separated. Default sort **MUST** be stable.
|
|
167
|
+
|
|
168
|
+
### R-6: Field Selection and Expansion
|
|
169
|
+
|
|
170
|
+
APIs **MAY** support `fields` (comma-separated field names) for sparse fieldsets. The `id` field **MUST** always be included regardless.
|
|
171
|
+
|
|
172
|
+
APIs **MAY** support `expand` (comma-separated relationship names) to inline related resources. Expansion depth **MUST** be limited to one level - nested expansion **MUST NOT** be supported.
|
|
173
|
+
|
|
174
|
+
### R-7: Sub-resources
|
|
175
|
+
|
|
176
|
+
A resource **MUST** be modeled as a sub-resource only when it cannot exist without its parent, is never accessed independently, and shares authorization context with its parent. Otherwise it **SHOULD** be a top-level resource with a foreign key reference.
|
|
177
|
+
|
|
178
|
+
Singleton sub-resources (one instance per parent) **MUST** use a singular noun, **MUST** support `GET` and `PUT`/`PATCH`, and **MUST NOT** support `POST`, `DELETE`, or list operations independently.
|
|
179
|
+
|
|
180
|
+
### R-8: Bulk Operations
|
|
181
|
+
|
|
182
|
+
Bulk operations **MUST** use `POST /v1/{collection}:bulk-{verb}`. The request **MUST** contain an array of identifiers or representations. The response **MUST** return per-item results including individual success or failure. Maximum item count **MUST** be enforced (recommended: 100).
|
|
183
|
+
|
|
184
|
+
Batch operations (heterogeneous) **SHOULD** be avoided. If required, the endpoint **MUST** be `/v1/batch` and the response **MUST** use `207 Multi-Status`.
|
|
185
|
+
|
|
186
|
+
### R-9: Long-running Operations
|
|
187
|
+
|
|
188
|
+
Operations that cannot complete synchronously **MUST** return `202 Accepted` with an `Operation-Location` header pointing to a status monitor resource. The status field **MUST** use one of: `pending`, `running`, `succeeded`, `failed`, `cancelled`. The server **SHOULD** return a `Retry-After` header. On completion, the response **MUST** include `result` (success) or `error` (failure). Cancellation **SHOULD** be supported via `POST /v1/operations/{op_id}:cancel`.
|
|
189
|
+
|
|
190
|
+
### R-10: Security
|
|
191
|
+
|
|
192
|
+
- Resource identifiers **MUST NOT** be sequential integers. Use opaque, high-entropy identifiers.
|
|
193
|
+
- Authorization checks **MUST** occur on every request. Sub-resource access **MUST** validate the parent chain.
|
|
194
|
+
- Error responses **MUST NOT** distinguish between "does not exist" and "no access" - return `404` (or `403` consistently) for both.
|
|
195
|
+
- Collection endpoints **MUST** apply authorization filters before pagination.
|
|
196
|
+
- URLs **MUST NOT** contain PII or sensitive data.
|
|
197
|
+
|
|
198
|
+
### R-11: Content Types
|
|
199
|
+
|
|
200
|
+
| Operation | Content-Type | Notes |
|
|
201
|
+
|-----------|-------------|-------|
|
|
202
|
+
| Request body (create/update) | `application/json` | **MUST** be the default |
|
|
203
|
+
| PATCH request body | `application/merge-patch+json` | **MUST** for JSON Merge Patch |
|
|
204
|
+
| Response body | `application/json` | **MUST** be the default |
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Examples
|
|
209
|
+
|
|
210
|
+
### Valid API resource structure
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
GET /v1/orders # List orders (paginated)
|
|
214
|
+
GET /v1/orders/{order_id} # Get single order
|
|
215
|
+
POST /v1/orders # Create order
|
|
216
|
+
GET /v1/orders/{order_id}/items # List items in order
|
|
217
|
+
POST /v1/orders:bulk-cancel # Bulk action on orders
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Invalid API resource structure
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
GET /v1/getOrders # Verb in path
|
|
224
|
+
GET /v1/order/123 # Singular collection name
|
|
225
|
+
POST /v1/orders/123/cancel # Action as sub-resource
|
|
226
|
+
GET /api/v1/orders # Redundant /api prefix
|
|
227
|
+
GET /v1/orders/ # Trailing slash
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Enforcement Rules
|
|
231
|
+
|
|
232
|
+
The following **MUST** be rejected at API gateway or design review:
|
|
233
|
+
|
|
234
|
+
1. **Verbs in URL paths** - path segments containing action words (e.g., `get`, `create`, `delete`).
|
|
235
|
+
2. **Singular collection names** - collection path segments that are not plural.
|
|
236
|
+
3. **Missing pagination** - collection endpoints returning unbounded arrays.
|
|
237
|
+
4. **Offset-based pagination** - use of `page`, `offset`, or `skip` query parameters.
|
|
238
|
+
5. **Sequential integer identifiers** - predictable sequential resource IDs.
|
|
239
|
+
6. **Request body on GET or DELETE** - any GET or DELETE accepting a body.
|
|
240
|
+
7. **PII in URLs** - email addresses, names, or personal data in path segments.
|
|
241
|
+
8. **Bare array responses** - collections returning a JSON array instead of `{"data": [], "pagination": {}}`.
|
|
242
|
+
9. **URL path validation** - all paths **MUST** match: `^/v[0-9]+/[a-z][a-z0-9-]*(/{[a-z_]+}(/[a-z][a-z0-9-]*)*)*$`
|
|
243
|
+
10. **Collection response validation** - `data` **MUST** be an array, `pagination` **MUST** be an object, `pagination.has_more` **MUST** be a boolean.
|
|
244
|
+
11. **POST to collection** - **MUST** return `201`.
|
|
245
|
+
12. **PUT idempotency** - repeated identical requests **MUST** produce the same result.
|
|
246
|
+
|
|
247
|
+
Enforcement **MUST** occur at two stages: design time (OpenAPI linting) and runtime (API gateway validation).
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## References
|
|
252
|
+
|
|
253
|
+
- [RFC 9110 - HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110)
|
|
254
|
+
- [RFC 9205 - Building Protocols with HTTP](https://www.rfc-editor.org/rfc/rfc9205)
|
|
255
|
+
- [RFC 7396 - JSON Merge Patch](https://www.rfc-editor.org/rfc/rfc7396)
|
|
256
|
+
- [Google AIP-121 - Resource-oriented Design](https://google.aip.dev/121)
|
|
257
|
+
- [Zalando RESTful API Guidelines](https://opensource.zalando.com/restful-api-guidelines/)
|
|
258
|
+
- [Microsoft REST API Guidelines](https://github.com/microsoft/api-guidelines)
|
|
259
|
+
- [OWASP API Security - BOLA](https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/)
|
|
260
|
+
- INTG-STD-004 - Naming Conventions
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Rationale
|
|
265
|
+
|
|
266
|
+
**Resource-oriented design over RPC** - Modeling APIs as resources with standard methods reduces cognitive load; learn one resource, understand them all.
|
|
267
|
+
|
|
268
|
+
**Cursor-based pagination** - Offset pagination breaks under concurrent writes (items shift between pages). Cursor pagination provides stable traversal at any scale.
|
|
269
|
+
|
|
270
|
+
**Opaque identifiers** - Sequential integers are trivially enumerable and enable BOLA attacks. Prefixed opaque IDs provide type safety and security.
|
|
271
|
+
|
|
272
|
+
**Kebab-case URLs, snake_case parameters** - Kebab-case is readable in logs and browsers. Snake_case in parameters aligns with JSON field naming per INTG-STD-004.
|
|
273
|
+
|
|
274
|
+
**Collection envelope** - `{"data": [], "pagination": {}}` ensures pagination metadata can be added without breaking changes.
|
|
275
|
+
|
|
276
|
+
**JSON Merge Patch** - Simpler than JSON Patch (RFC 6902) for common partial updates, using natural JSON structure rather than operation arrays.
|
|
277
|
+
|
|
278
|
+
**Long-running operations as resources** - Treating operations as pollable resources follows the same pattern used throughout the API, avoiding WebSocket complexity.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Version History
|
|
283
|
+
|
|
284
|
+
| Version | Date | Change |
|
|
285
|
+
| ------- | ---------- | ------------------ |
|
|
286
|
+
| 1.0.0 | 2026-03-28 | Initial definition |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"label": "Data Formats", "position": 2}
|