PrepZone Logo
PrepZone

REST API Design for Systems

Resource naming, pagination, versioning and error contracts that survive millions of calls.

REST API on AWS

CLIENT
Clients
NETWORK
API GatewayREST · JWT · usag…
COMPUTE
EKS servicesmicroservices
COMPUTE
Lambdawebhooks · cron
API Gateway handles auth, throttling, routing to EKS and Lambda.

StreamHub's public API serves stream metadata, chat, subscriptions, and clips to web and mobile clients. Consistent resource naming, cursor pagination, and structured errors let third-party integrations ship without breaking every release.

Resource design principles

REST conventions for StreamHub

  • Nouns, not verbs: /streams/{id} not /getStream?id=123.
  • Plural collections: /streams, /users/{id}/followers.
  • Nested resources sparingly: /streams/{id}/clips — max two levels deep.
  • Actions as sub-resources: POST /streams/{id}/follow for non-CRUD operations.
  • HTTP methods map to semantics: GET read, POST create, PUT replace, PATCH update, DELETE remove.
Java
# StreamHub resource map (subset)
/streams:
  GET:    list streams (paginated)
  POST:   create stream (authenticated streamer)
/streams/{stream_id}:
  GET:    stream metadata
  PATCH:  update title, category
/streams/{stream_id}/clips:
  GET:    list clips
  POST:   create clip from timestamp
/users/{user_id}/subscriptions:
  GET:    list followed streamers
  POST:   follow streamer

Pagination

AspectOffset paginationCursor pagination
Query?page=3&limit=20?cursor=eyJpZCI6MTIzfQ&limit=20
PerformanceO(offset) — slow at high pagesO(limit) — constant time with index
ConsistencyDuplicates/skips if data shifts during pagingStable if sorted by indexed cursor field
StreamHub useAdmin dashboards (small datasets)Public stream feed, chat history
  • Query

    Offset pagination?page=3&limit=20
    Cursor pagination?cursor=eyJpZCI6MTIzfQ&limit=20
  • Performance

    Offset paginationO(offset) — slow at high pages
    Cursor paginationO(limit) — constant time with index
  • Consistency

    Offset paginationDuplicates/skips if data shifts during paging
    Cursor paginationStable if sorted by indexed cursor field
  • StreamHub use

    Offset paginationAdmin dashboards (small datasets)
    Cursor paginationPublic stream feed, chat history
Java
{
  "data": [
    { "stream_id": "live_9912", "title": "Friday Night Ranked" }
  ],
  "pagination": {
    "next_cursor": "eyJzdHJlYW1faWQiOiJsaXZlXzk5MTIifQ",
    "has_more": true,
    "limit": 20
  }
}

Versioning

StrategyExampleTrade-off
URL path/v1/streamsExplicit, easy to route; clutters URLs
HeaderAccept: application/vnd.streamhub.v2+jsonClean URLs; harder to test in browser
Query param/streams?version=2Simple but easy to forget
  • URL path

    Example/v1/streams
    Trade-offExplicit, easy to route; clutters URLs
  • Header

    ExampleAccept: application/vnd.streamhub.v2+json
    Trade-offClean URLs; harder to test in browser
  • Query param

    Example/streams?version=2
    Trade-offSimple but easy to forget

StreamHub uses URL path versioning (/v1/) for public API; internal gRPC uses protobuf package versioning.

Never break existing clients — add fields, don't rename or remove without a new version.

Error contract

Java
{
  "error": {
    "code": "STREAM_NOT_FOUND",
    "message": "Stream live_9912 does not exist or has ended.",
    "details": { "stream_id": "live_9912" },
    "request_id": "req_8a3f2b1c"
  }
}

HTTP status conventions

  • 200/201/204: Success (200 read, 201 create, 204 delete).
  • 400: Client error — bad input, validation failure.
  • 401/403: Unauthenticated vs unauthorized.
  • 404: Resource not found (don't leak existence of private resources — use 403).
  • 409: Conflict — duplicate follow, idempotency key mismatch.
  • 429: Rate limited — include Retry-After.
  • 500/503: Server error — include request_id for support lookup.

OpenAPI specification

Java
openapi: 3.1.0
info:
  title: StreamHub Public API
  version: 1.0.0
paths:
  /v1/streams/{stream_id}:
    get:
      operationId: getStream
      parameters:
        - name: stream_id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Stream metadata
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Stream"
        "404":
          description: Stream not found

Quick recall

Everything you need if you only revisit this box.

  • Resources are nouns; HTTP methods carry semantics; keep nesting shallow.
  • Cursor pagination scales; offset pagination breaks at high page numbers.
  • Version via URL path (/v1/) for public APIs; never break clients silently.
  • Structured errors: code, message, details, request_id — not bare strings.
  • OpenAPI documents the contract; generate client SDKs and validation from it.
  • Idempotency-Key header on all mutating POST endpoints.

Test yourself

Answer these before moving on — recall is what makes it stick.