SPB Git

spb/ultra-sharp-agent-skills Public

Ultra-Sharp Agent Skills — a research-first skill-authoring system + 72 production-ready skills for AI agents.

Python 100%
4.6 KB · 148 lines markdown
Rendered Raw Blame History
1<!--2Author: Simon-Pierre Boucher3Contact: contact@spboucher.ai4-->56# Patterns — Designing REST APIs78## Contents9- URL and method matrix10- Pagination envelope (cursor)11- problem+json error schemas12- Idempotency-Key handling13- OpenAPI 3.1 skeleton14- Gotchas1516## URL and method matrix1718```19GET    /v1/orders               list (paginated)          20020POST   /v1/orders               create                    201 + Location21GET    /v1/orders/{id}          read                      200 / 40422PUT    /v1/orders/{id}          full replace (idempotent) 200 / 40423PATCH  /v1/orders/{id}          partial update            200 / 404 / 40924DELETE /v1/orders/{id}          delete (idempotent)       204 / 40425POST   /v1/orders/{id}/refund   non-CRUD action           201 / 40926GET    /v1/customers/{id}/orders  nested read-only view   20027```28Nest at most one level; deeper relations get top-level resources with filters29(`/v1/orders?customer_id=7`), not `/customers/7/orders/3/items/9`.3031## Pagination envelope (cursor)3233```json34{35  "data": [{ "id": "ord_01J8", "status": "active" }],36  "pagination": {37    "next_cursor": "eyJpZCI6Im9yZF8wMUo4In0",38    "has_more": true,39    "limit": 2040  }41}42```43Request: `GET /v1/orders?limit=20&cursor=eyJpZCI6...`. The cursor is opaque44(base64 of the last sort key) — clients must not parse it. `limit` above the45max of 100 → clamp to 100, do not error.4647## problem+json error schemas4849Header: `Content-Type: application/problem+json`5051```json52{53  "type": "https://api.example.com/errors/validation",54  "title": "Validation failed",55  "status": 422,56  "detail": "2 fields are invalid.",57  "instance": "/v1/orders",58  "errors": [59    { "field": "email", "message": "must be a valid email address" },60    { "field": "quantity", "message": "must be between 1 and 99" }61  ]62}63```6465Conflict example:6667```json68{69  "type": "https://api.example.com/errors/state-conflict",70  "title": "Order already shipped",71  "status": 409,72  "detail": "Order ord_01J8 cannot be cancelled after shipment.",73  "instance": "/v1/orders/ord_01J8/cancellation"74}75```7677Define the schema once under `components/schemas/Problem` and `$ref` it from78every 4xx/5xx response.7980## Idempotency-Key handling8182```python83# Pseudocode for POST endpoints that create resources.84# Keys expire after 24h — long enough for client retry storms, short enough85# to bound storage.86def create_order(request):87    key = request.headers.get("Idempotency-Key")88    if key:89        cached = idempotency_store.get(key)90        if cached:91            if cached.request_hash != hash(request.body):92                return problem(422, "Idempotency-Key reused with different body")93            return cached.response          # replay, no side effect94    response = do_create(request.body)      # single side effect95    if key:96        idempotency_store.put(key, hash(request.body), response, ttl_hours=24)97    return response98```99100## OpenAPI 3.1 skeleton101102```yaml103openapi: 3.1.0104info: { title: Orders API, version: 1.0.0 }105servers: [{ url: https://api.example.com/v1 }]106paths:107  /orders:108    get:109      parameters:110        - { name: limit, in: query, schema: { type: integer, maximum: 100, default: 20 } }111        - { name: cursor, in: query, schema: { type: string } }112      responses:113        "200": { $ref: "#/components/responses/OrderList" }114        "429": { $ref: "#/components/responses/Problem" }115    post:116      parameters:117        - { name: Idempotency-Key, in: header, schema: { type: string } }118      responses:119        "201": { $ref: "#/components/responses/Order" }120        "422": { $ref: "#/components/responses/Problem" }121components:122  schemas:123    Problem:124      type: object125      properties:126        type: { type: string }127        title: { type: string }128        status: { type: integer }129        detail: { type: string }130        instance: { type: string }131```132133## Gotchas134135- **404 vs 403 leaks existence.** If callers must not learn a resource exists,136  return 404 for both missing and forbidden.137- **PUT with partial body silently nulls fields** — that is correct replace138  semantics; if clients expect merge, they need PATCH. Document which you offer.139- **Offset pagination drifts** when rows are inserted mid-scan; deep offsets140  also get slow. Cursors fix both — default to them.141- **Trailing slashes**: `/orders` and `/orders/` must not be two resources.142  Redirect or normalize one to the other.143- **Enum widening is breaking for clients that switch exhaustively.** Adding144  an enum value is only additive if the spec documents "unknown values may145  appear" from v1.146- **Date-times**: always RFC 3339 UTC (`2026-08-05T14:30:00Z`); epoch147  integers and local times cause silent client bugs.148