6  REST API Design Patterns

app-protocols
rest
api
patterns

Start with the story: A REST API is the map a device, app, or service uses to talk about real things: thermostats, readings, commands, firmware, and alerts. Good design makes those things obvious in the URL, uses HTTP verbs consistently, and returns errors that tell the next engineer what actually happened.

In 60 Seconds

IoT REST API design requires choosing between RESTful and message-based patterns, designing consistent URI/topic naming, selecting compact payload formats (JSON vs CBOR vs Protocol Buffers), implementing API versioning, and applying rate limiting and TLS security suited to constrained devices.

Chapter Roadmap

This chapter works best if you read it as a design review checklist:

  • First decide which conversation pattern belongs where: REST or CoAP for commands and queries, MQTT for telemetry and event streams.
  • Then make the contract readable by naming resources, topics, versions, errors, and payload formats consistently.
  • Next use the JSON, CBOR, Protocol Buffers, versioning, and rate-limit calculators to test whether the design fits constrained devices and cloud cost limits.
  • After that connect security, the HVAC case study, and the REST explorer to deployment behavior rather than treating them as separate topics.
  • Finally use the quizzes and method-semantics deep dive to check retry safety, caching, and response contracts.

Checkpoint callouts summarize each major decision point. Existing interactives and quizzes are breathers: pause there, test the current rule, and then continue.

6.1 Learning Objectives

By the end of this chapter, you will be able to:

  • Distinguish RESTful vs Message-Based Patterns: Analyze the trade-offs between request-response (REST/CoAP) and publish-subscribe (MQTT) architectures to select the appropriate pattern for a given IoT use case
  • Design Topic and URI Naming Conventions: Construct consistent MQTT topic hierarchies and CoAP URI structures that scale to thousands of devices in multi-tenant deployments
  • Evaluate and Select Payload Formats: Compare JSON, CBOR, and Protocol Buffers across size, parsing overhead, and tooling constraints; justify format choices for constrained vs. cloud endpoints
  • Implement API Versioning Strategies: Apply URI path, header, and query-parameter versioning techniques; assess the cost of breaking changes against maintaining parallel API versions
  • Configure Rate Limiting and Throttling: Design token-bucket rate-limiting policies to protect infrastructure from device misbehavior and calculate the financial impact of unconstrained traffic
  • Apply IoT API Security Principles: Implement TLS/DTLS, per-request authentication tokens, and credential rotation; diagnose common authentication vulnerabilities in deployed IoT systems
  • Core Concept: Fundamental principle underlying REST API Design Patterns — understanding this enables all downstream design decisions
  • Key Metric: Primary quantitative measure for evaluating REST API Design Patterns performance in real deployments
  • Trade-off: Central tension in REST API Design Patterns design — optimizing one parameter typically degrades another
  • Protocol/Algorithm: Standard approach or algorithm most commonly used in REST API Design Patterns implementations
  • Deployment Consideration: Practical factor that must be addressed when deploying REST API Design Patterns in production
  • Common Pattern: Recurring design pattern in REST API Design Patterns that solves the most frequent implementation challenges
  • Performance Benchmark: Reference values for REST API Design Patterns performance metrics that indicate healthy vs. problematic operation

6.2 For Beginners: REST API Patterns for IoT

REST API design patterns are proven templates for building interfaces that devices and applications use to communicate. Think of patterns as recipes – rather than inventing a new way to handle pagination, authentication, or error responses every time, you follow a well-tested approach that developers already know and expect.

“Every time I build an API, I start from scratch,” sighed Sammy the Sensor. “There must be a better way.”

Max the Microcontroller handed him a pattern book. “There is! Design patterns are like cooking recipes that smart engineers already figured out. For example, the pagination pattern: when you have 10,000 temperature readings, don’t dump them all at once. Send 50 at a time with a ‘next page’ link. It’s like reading a book chapter by chapter instead of swallowing the whole thing.”

“My favorite is the error response pattern,” said Lila the LED. “Instead of just saying ‘error’, you return a structured message with a code, a human-readable description, and a hint about what to fix. Like the difference between a teacher saying ‘wrong’ versus ‘wrong – try converting to Celsius first.’”

Bella the Battery added: “And the rate limiting pattern saves my energy. The API says ‘you can ask me 100 times per minute, but no more.’ This stops badly written apps from hammering a sensor with thousands of requests and draining its battery. Patterns protect both the client and the server!”

6.3 Prerequisites

Before diving into this chapter, you should be familiar with:

6.4 How This Chapter Fits

REST API Design Series Navigation:

  1. Introduction and Why Lightweight Protocols Matter
  2. Protocol Overview and Comparison
  3. REST API Design for IoT (Index)
  4. Real-Time Protocol Workflows
  5. Protocol Selection Worked Examples

This chapter focuses on practical REST API design patterns for IoT systems.


6.5 IoT API Design Best Practices

Understanding protocol theory is essential, but practical API design determines whether your IoT system is maintainable, scalable, and developer-friendly. This section provides actionable guidance for designing IoT APIs using the protocols covered in this chapter.

Understanding REST Constraints

Core Concept: REST (Representational State Transfer) defines six architectural constraints - client-server separation, statelessness, cacheability, uniform interface, layered system, and optional code-on-demand - that enable scalable, reliable web services.

Why It Matters: For IoT APIs, the statelessness constraint is critical: each request must contain all information needed to process it, with no server-side session state. This enables horizontal scaling (any server can handle any request), simplifies load balancing across regions, and allows devices to reconnect to different servers without losing context after network disruptions.

Key Takeaway: Design IoT REST APIs around resources (nouns like /devices/, /sensors/, /readings/) not actions (verbs like /getTemperature), and include authentication tokens in every request rather than relying on server sessions - this matches IoT reality where devices may connect through different gateways over time.

6.5.1 RESTful vs Message-Based Patterns

The choice between REST (HTTP/CoAP) and message-based (MQTT) architectures fundamentally shapes your API design:

Aspect REST (HTTP/CoAP) Message-Based (MQTT)
Pattern Request-Response Publish-Subscribe
State Stateless Connection-based
Discovery URI paths Topic hierarchy
Scalability Horizontal (add servers) Vertical (broker capacity)
Best For CRUD operations, device control Event streams, telemetry
Client Complexity Simple (standard HTTP libs) Moderate (manage subscriptions)

Design principle: Use REST for commands and queries (“What is the temperature?”), use pub-sub for events and updates (“Temperature changed!”).

6.5.2 Topic and URI Naming Conventions

Consistent naming prevents confusion in systems with thousands of devices:

6.5.2.1 MQTT Topic Hierarchy

# Structure:
#   {organization}/{location}/{building}/{floor}
#   /{device_type}/{device_id}/{data_type}

# Examples:
acme/hq/bldg1/floor3/hvac/unit42/temperature
acme/factory/line2/sensor/pressure01/value
acme/warehouse/zone-a/motion/detector03/event

# Wildcards for subscriptions:
acme/hq/+/+/hvac/+/temperature        # All HVAC temps in HQ
acme/+/+/+/motion/+/event             # All motion events company-wide

Best practices:

  • Use lowercase, hyphens for readability
  • Start with organization/tenant for multi-tenant systems
  • Include location hierarchy for geographical filtering
  • End with data type (temperature, status, event, command)
  • Avoid special characters (/, +, #, $ reserved)

6.5.2.2 CoAP URI Pattern

# Structure: coap://{host}/{version}/{resource_type}/{device_id}/{subresource}

# Examples:
coap://sensors.local/v1/devices/temp42/reading
coap://actuators.local/v1/devices/valve12/status
coap://gateway.local/v1/config/network

# Query parameters for filtering:
coap://sensors.local/v1/devices/temp42/history?start=2025-01-01&limit=100

Best practices:

  • Always version your API (/v1/, /v2/) to allow migration
  • Use plural resource names (/devices/, not /device/)
  • Keep URIs short (remember constrained bandwidth)
  • Use query parameters sparingly (adds overhead)

6.6 Payload Format Selection

The right payload format balances human readability, efficiency, and tooling support:

Format Size Human Readable Schema Validation Best For
JSON Large (verbose) Yes JSON Schema Development, debugging, web apps
CBOR Small (binary) No CDDL Constrained devices, low bandwidth
Protocol Buffers Small (binary) No .proto files High volume, multiple languages
MessagePack Medium No None Mixed environments
Plain Text Variable Yes None Simple sensors, legacy systems
Tradeoff: JSON vs Binary Payload Formats (CBOR/Protobuf)

Option A: Use JSON for human-readable, easily debuggable message payloads Option B: Use binary formats (CBOR, Protocol Buffers) for compact, efficient encoding

Decision Factors:

Factor JSON CBOR/Protobuf
Payload size Large (50-100% overhead) Small (10-30% of JSON)
Human readable Yes (text-based) No (requires decoder)
Debugging Easy (curl, browser tools) Requires specialized tools
Schema enforcement Optional (JSON Schema) Built-in (CDDL, .proto)
Parsing complexity Moderate (string parsing) Low (binary scanning)
CPU usage Higher (text parsing) Lower (direct decode)
Tooling ecosystem Excellent (universal) Good (growing)
Bandwidth cost Higher Lower

Choose JSON when:

  • Development and debugging convenience is priority (prototyping phase)
  • Integrating with web services, REST APIs, or JavaScript clients
  • Message frequency is low (hourly reports, configuration)
  • Devices have sufficient processing power and bandwidth (Wi-Fi gateways)
  • Team lacks binary protocol expertise

Choose Binary (CBOR/Protobuf) when:

  • Bandwidth is constrained or metered (cellular, satellite, LPWAN)
  • High message frequency makes overhead significant (10+ messages/second)
  • Battery life depends on minimizing transmission time
  • Strict schema validation is required for data quality
  • Production systems where debugging tools are already in place

Default recommendation: JSON for development, cloud APIs, and low-frequency messages; CBOR for constrained devices and CoAP payloads; Protocol Buffers for high-volume systems with strong typing requirements

Example comparison (temperature reading):

// JSON: 43 bytes
{"device":"temp42","value":23.5,"unit":"C"}

// CBOR: ~30 bytes (binary, shown as hex; string keys retained)
A3 66 64 65 76 69 63 65 66 74 65 6D 70 34 32...

// Plain text: 4 bytes
23.5

Design recommendations:

  • Battery sensors: Use CBOR or plain text (minimize bytes over air)
  • Cloud APIs: Use JSON (debugging, wide tool support)
  • High-frequency telemetry: Protocol Buffers (efficient, versioned)
  • Mixed systems: JSON at gateway, CBOR on constrained networks

How much bandwidth and energy can binary formats save? Let’s quantify the difference between JSON and CBOR for a typical IoT sensor payload.

Example payload: Temperature reading with metadata.

JSON representation (human-readable):

{"device":"sensor-042","temp":23.5,"unit":"C","time":1705392000}

\(\text{Size} = 64\text{ bytes (including whitespace and delimiters)}\)

CBOR representation (binary, using integer map keys 1-4 instead of string key names): \(\text{Size} \approx 28\text{ bytes (compact binary encoding with integer keys)}\)

Bandwidth savings: \(\text{Reduction} = \frac{64 - 28}{64} \times 100\% = 56\%\text{ smaller payload}\)

Energy impact (LoRaWAN SF7/125 kHz, simplified proportional estimate):

Actual LoRaWAN airtime depends on spreading factor, coding rate, and header overhead using the Semtech formula — it is not simply linear in byte count. As a proportional approximation illustrating relative savings:

  • JSON (64 bytes): proportionally longer airtime at SF7 ≈ longer transmission
  • CBOR (28 bytes): \(\approx \frac{28}{64} = 44\%\) of JSON airtime
  • Transmission time savings: \(\frac{64 - 28}{64} \times 100\% \approx 56\%\text{ less airtime}\)

(For accurate airtime budgeting, use the Semtech LoRa Airtime Calculator with your specific SF, BW, and CR parameters.)

At scale (10,000 sensors, 1 message/hour for 1 year): \(\text{Annual messages} = 10{,}000 \times 24 \times 365 = 87{,}600{,}000\) \(\text{JSON bandwidth} = 87{,}600{,}000 \times 64 = 5.6\text{ GB/year}\) \(\text{CBOR bandwidth} = 87{,}600{,}000 \times 28 = 2.45\text{ GB/year}\) \(\text{Savings} = 3.15\text{ GB/year (56\% reduction)}\)

6.6.1 Calculation Audit

The example uses a minified UTF-8 JSON body, integer CBOR keys, and decimal gigabytes. The raw arithmetic is:

  • The JSON string {"device":"sensor-042","temp":23.5,"unit":"C","time":1705392000} is 64 bytes.
  • The CBOR estimate is 28 bytes, so the byte reduction is 64 - 28 = 36 bytes.
  • The percentage reduction is 36 / 64 = 0.5625, or 56.25% smaller.
  • Hourly reporting for 10,000 sensors gives 10,000 x 24 x 365 = 87,600,000 messages per year.
  • JSON traffic is 87,600,000 x 64 = 5,606,400,000 bytes, or 5.6064 GB using 1 GB = 1,000,000,000 bytes.
  • CBOR traffic is 87,600,000 x 28 = 2,452,800,000 bytes, or 2.4528 GB.
  • The annual saving is 5.6064 - 2.4528 = 3.1536 GB, which rounds to the 3.15 GB/year figure above.

For a real battery budget, combine this byte-count reduction with the radio’s airtime formula, retries, receive windows, wake cost, and sleep current. The 56% figure is a payload-size reduction; it only becomes an energy reduction when transmit airtime dominates the device budget.

Key insight: For battery-powered devices, every byte transmitted can shorten battery life. CBOR’s 56% payload reduction can approach a similar airtime saving for transmission-dominated budgets, but the measured power model decides the final battery result. The tradeoff: debugging requires binary decoders instead of simple text tools.

6.6.2 Interactive: Payload Format Size Comparison Calculator

Estimate serialized payload sizes for different formats based on the number and types of fields in your IoT message.


Broker BexCheckpoint: Payload Evidence

The payload section gives you the evidence a design review should keep:

  • You now know that the example JSON body is 64 bytes and the compact CBOR estimate is 28 bytes.
  • You now know that 64 - 28 = 36 bytes, or 56.25% smaller, is a payload-size claim first; it becomes an energy claim only when transmit airtime dominates the device budget.
  • You now know why the chapter recommends JSON for cloud APIs and debugging, CBOR for constrained CoAP payloads, and Protocol Buffers for high-volume systems with strong typing.

6.7 API Versioning Strategies

IoT systems run for years - versioning prevents breaking deployed devices:

Understanding API Versioning

Core Concept: API versioning provides a contract between API providers and consumers that allows the API to evolve without breaking existing clients.

Why It Matters: IoT devices deployed in the field may run for 5-10 years without firmware updates. Without versioning, any API change (adding required fields, changing response formats, deprecating endpoints) will break thousands of devices simultaneously, causing service outages and costly emergency patches.

Key Takeaway: Always version from day one using URI path versioning (/v1/) for IoT APIs - it is the simplest approach that works across all protocols and is immediately visible in logs and debugging tools.

6.7.2 Header Versioning

GET /temperature
Accept: application/vnd.iot.v1+json

Pros: Clean URLs Cons: Embedded devices may not support custom headers

6.7.3 Query Parameter

coap://sensor.local/temperature?version=1

Pros: Flexible, backward compatible Cons: Easy to forget, adds overhead

IoT-specific recommendation: Use URI versioning (/v1/, /v2/) because: - Simplest for embedded clients with limited HTTP stack - Clear in logs and debugging - No header parsing complexity - Works across all protocols (MQTT, CoAP, HTTP)

Broker BexCheckpoint: Naming the Contract

Before using the versioning calculator, verify that the API contract is readable without tribal knowledge:

  • You now know why REST should expose resources such as /devices/, /sensors/, and /readings/ rather than action URLs such as /getTemperature.
  • You now know why MQTT topics need organization, location, device type, device id, and data type fields when a deployment grows to thousands of devices.
  • You now know why URI versioning with /v1/ and /v2/ is the default IoT choice: it is visible in logs, works across protocols, and avoids custom header parsing on constrained clients.

6.7.4 Interactive: API Versioning Migration Cost Calculator

Compare the cost of breaking existing devices (modifying v1) versus maintaining parallel API versions.


6.8 Error Response Format

Consistent error handling reduces debugging time:

Standard error structure (JSON):

{
  "error": {
    "code": "SENSOR_OFFLINE",
    "message": "Device has not reported in 5 minutes",
    "timestamp": "2025-01-15T10:30:00Z",
    "device_id": "sensor-42",
    "retry_after": 300
  }
}

CoAP response codes:

2.01 Created   - Resource created successfully
2.04 Changed   - Resource updated
2.05 Content   - Successful GET with payload
4.00 Bad Request - Invalid syntax
4.04 Not Found - Resource doesn't exist
5.00 Internal Server Error

MQTT error patterns:

# Publish errors to special topics
acme/errors/sensor-42  → {"code": "SENSOR_OFFLINE", ...}

# Or use QoS 0 for best-effort error reporting

6.9 Rate Limiting and Throttling

Protect infrastructure from device misbehavior:

Understanding Rate Limiting

Core Concept: Rate limiting restricts the number of API requests a client can make within a specified time window, protecting servers from overload and ensuring fair resource allocation across clients.

Why It Matters: In IoT systems, a single malfunctioning device or firmware bug can generate thousands of requests per second, overwhelming your cloud infrastructure and causing cascading failures that affect all devices. Rate limiting acts as a circuit breaker that isolates misbehaving devices while keeping the system operational for well-behaved clients.

Key Takeaway: Implement rate limits at multiple levels (per-device, per-tenant, per-endpoint) and always return meaningful error responses (HTTP 429 with Retry-After header) so clients can implement proper backoff strategies rather than hammering your servers.

Patterns:

# Per-device limits
Device temp42: 1 request/second max
Response: 429 Too Many Requests (HTTP)
          4.29 Too Many Requests (CoAP)

# Per-tenant limits
Organization ACME: 10,000 messages/minute
MQTT: Disconnect with reason code (0x97 Quota Exceeded)

Implementation:

  • Use token bucket algorithm (burst allowed, sustained rate limited)
  • Return Retry-After header with backoff time
  • Log violations for debugging misbehaving devices

6.9.1 Interactive: Rate Limiting Cost Impact Calculator

Model the financial impact of missing rate limits when a firmware bug causes devices to over-poll.


6.10 Security Best Practices

Always authenticate and authorize:

# CoAP with DTLS
coaps://sensor.local/v1/temperature  # Note: 's' for secure

# MQTT with TLS + auth
Username: device-42
Password: [device-specific token]
Client Certificate: [for mutual TLS]

# HTTP Bearer tokens
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Design principles:

  • Never accept unauthenticated writes
  • Use TLS/DTLS even on local networks (prevents eavesdropping)
  • Rotate credentials regularly (especially for high-security deployments)
  • Rate-limit authentication attempts (prevent brute force)

Broker BexCheckpoint: Protecting the Fleet

At this point the contract has names, formats, versions, errors, limits, and security boundaries:

  • You now know why a fleet with 50,000 deployed thermostats should add a /api/v2/ response rather than surprising strict v1 parsers with a new field.
  • You now know why 429 Too Many Requests, Retry-After, and a token bucket belong in the design, not only in incident response after a polling bug.
  • You now know why TLS, DTLS, per-request authentication, and credential rotation are part of the REST pattern: stateless APIs cannot rely on server-side sessions to remember trust.

6.11 Case Study: Smart HVAC System API Design

Case Study: Smart Building HVAC System

Requirements:

  • 500 temperature sensors per building
  • Real-time alerts for anomalies
  • Historical data queries
  • Mobile app control

Solution - Hybrid approach:

# 1. MQTT for telemetry (sensors → cloud)
Topic: buildings/bldg1/floor3/zone-a/temp42/reading
Payload (CBOR): {t:23.5, h:45, ts:1642259400}
QoS: 0 (frequent updates, loss acceptable)

# 2. CoAP for control (app → actuators)
PUT coap://hvac.local/v1/zones/zone-a/setpoint
Payload: {"target":22.0}
Type: CON (confirmable - critical command)

# 3. HTTP REST for historical queries (app → cloud)
GET https://api.example.com/v1/sensors/temp42/history?start=2025-01-01
Response (JSON): [{"timestamp":"2025-01-01T00:00:00Z","value":23.5}...]

Why this works:

  • MQTT handles high-volume telemetry efficiently
  • CoAP provides low-latency local control
  • HTTP enables rich queries from mobile apps
  • Each protocol optimized for its use case

6.11.1 Choosing the Right Data Serialization Format: A Decision Framework

Payload format choice has outsized impact on constrained IoT devices. Here is a quantitative comparison for a typical sensor reading {"temperature": 23.5, "humidity": 45, "timestamp": 1706140800}:

Format Encoded Size Human Readable Schema Required Library Size (C) Parse Speed
JSON 62 bytes Yes No 5-20 KB Moderate
CBOR 35 bytes No (binary) No (self-describing) 2-5 KB Fast
Protocol Buffers 18 bytes No (binary) Yes (.proto file) 30-100 KB Very fast
MessagePack 38 bytes No (binary) No (self-describing) 3-8 KB Fast
Custom binary 12 bytes No Yes (manual) 0 KB (hand-coded) Fastest
If your project needs… Choose… Because…
Debugging ease, web dashboard integration JSON Universal tooling, browser-native, readable in logs
Compact payloads on constrained networks (LoRaWAN, 6LoWPAN) CBOR 40-50% smaller than JSON, self-describing, IETF standard (RFC 8949)
Maximum efficiency with versioned schemas Protocol Buffers 70%+ smaller than JSON, strong typing, backward-compatible evolution
Drop-in JSON replacement with size savings MessagePack JSON-compatible data model, ~40% smaller, minimal code changes
Extreme constraints (<1 KB payload budget) Custom binary Hand-pack fields at bit level, zero overhead, but no interoperability

Quick Decision Flowchart:

  1. Is the payload going over LoRaWAN (51-242 byte limit)? Yes –> CBOR or custom binary
  2. Do both ends share a compiled schema (.proto file)? Yes –> Protocol Buffers
  3. Is the API consumed by web browsers or curl? Yes –> JSON (use CBOR for device-to-gateway, JSON for gateway-to-cloud)
  4. Do you need a drop-in replacement for JSON with smaller size? Yes –> MessagePack
  5. Default: CBOR for device-to-gateway communication; JSON for cloud APIs and dashboards

6.11.2 Interactive: Fleet Bandwidth Savings Calculator

Estimate annual bandwidth and cost savings when migrating from JSON to a binary format across your IoT fleet.

Scenario: A smart thermostat manufacturer has 50,000 devices deployed running firmware v1.2 connecting to /api/v1/thermostats. They need to add a new field humidity to the response without breaking existing devices.

Option A: Add field to v1 (breaking change risk):

// Old v1 response (existing devices expect this)
{"temperature": 22.5, "mode": "heat"}

// New v1 response (adding humidity)
{"temperature": 22.5, "mode": "heat", "humidity": 45}

Risk: Devices with strict JSON parsing may reject extra fields
  - Estimated 5% of devices have strict parsers
  - 50,000 × 5% = 2,500 devices broken

Option B: Create /api/v2/ (recommended):

// v1 continues unchanged
GET /api/v1/thermostats/device-042
{"temperature": 22.5, "mode": "heat"}

// v2 includes new field
GET /api/v2/thermostats/device-042
{"temperature": 22.5, "mode": "heat", "humidity": 45}

Migration plan:
  - Year 1: Both v1 and v2 active (0 downtime)
  - Year 2: New firmware uses v2, old devices still on v1
  - Year 3: Deprecation warning on v1
  - Year 4: v1 sunset (98% devices upgraded by this point)

Cost comparison:

  • Option A (modify v1): Emergency firmware push to 2,500 devices, support tickets, reputational damage = $125,000
  • Option B (v2 endpoint): Dev cost for v2 endpoint + maintain both APIs for 2 years = $45,000

Decision: Create v2 endpoint. Savings: $80,000 + zero downtime.

Common Mistake: Ignoring API Rate Limiting on IoT Devices

The Error: Not implementing rate limits on device APIs, assuming “our devices are well-behaved.” A firmware bug causes 1,000 devices to poll every 100ms instead of every 5 minutes.

Real Impact:

Normal traffic: 1,000 devices × 12 requests/hour = 12K req/hour
Bug traffic: 1,000 devices × 36,000 requests/hour = 36M req/hour (3,000× increase)

AWS API Gateway cost:
  12K req/hour: $0.04/hour = $29/month (normal)
  36M req/hour: $120/hour = $86,400/month (bug)

Damage: $86K bill + service outage for ALL devices (API throttled)

The Fix: Implement per-device rate limiting:

# Return 429 Too Many Requests after 20 requests/minute
@app.route('/api/v1/temperature')
@rate_limit(max_requests=20, window=60)  # 20 per minute per device
def get_temperature():
    return jsonify({"temp": 22.5})

Result: Bug triggers rate limit, affects only buggy devices. Bill capped at $150/month. Service continues for well-behaved devices.

Common Pitfalls

Relying on theoretical models without profiling actual behavior leads to designs that miss performance targets by 2-10×. Always measure the dominant bottleneck in your specific deployment environment — hardware variability, interference, and load patterns routinely differ from textbook assumptions.

Optimizing one parameter in isolation (latency, throughput, energy) without considering impact on others creates systems that excel on benchmarks but fail in production. Document the top three trade-offs before finalizing any design decision and verify with realistic workloads.

Most field failures come from edge cases that work in the lab: intermittent connectivity, partial node failure, clock drift, and buffer overflow under peak load. Explicitly design and test failure handling before deployment — retrofitting error recovery after deployment costs 5-10× more than building it in.

Label the Diagram

Order the Steps

Match the Concepts

6.12 Deep Dive: Methods, Idempotency, and Conditional Caching

The patterns above cover URIs, versioning, and payloads. This layered walkthrough fills in the semantics that make a REST API survive a lossy IoT link: what each HTTP method promises, why idempotency decides whether a device can safely retry, and how ETag validators let a constrained device poll without re-downloading unchanged data.

REST models a system as resources named by URIs and acts on them with a small, agreed set of HTTP methods. A threshold should live under a noun such as /devices/42/config; the action belongs in PATCH or PUT, not in a custom endpoint like POST /setThreshold. Stable nouns make permissions, logs, test fixtures, and retry behavior easier to audit because each resource can list its allowed methods and expected outcomes once.

Shape Example Design use
Collection /devices List devices with GET; create with POST when the server assigns ids
Item /devices/42 Read, replace, or delete one known device
Sub-resource /devices/42/readings Scope related data to its owner without inventing verbs
Anti-pattern /setDeviceConfig RPC-style URL hides method semantics from caches, proxies, and reviewers

Two method properties matter most on lossy IoT links. A method is safe when it makes no intended state change, so clients may cache, prefetch, or repeat it. A method is idempotent when doing it many times leaves the same state as doing it once. When a device times out after sending a request, it often cannot tell whether the server received it; idempotency decides whether the retry is harmless.

Method Safe / idempotent Typical IoT use
GET Safe, idempotent Read a sensor value or config; success is usually 200 OK
PUT Not safe, idempotent Replace a complete config resource; success is usually 200 OK or 204 No Content
PATCH Not safe, not guaranteed idempotent Partially update config; make patches idempotent when devices may retry
POST Not safe, not idempotent Create a resource or enqueue a command; use 201 Created or 202 Accepted
DELETE Not safe, idempotent Remove a rule or device; repeated deletes should not recreate state

A device that times out after PUT /devices/42/config can safely resend the same full representation, because two identical PUTs leave the same configuration. A blind retry of POST /commands can enqueue two commands. For retry-safe creation, either PUT to a client-chosen URI or send an idempotency key that the server deduplicates. Document that retry rule beside every method in the API contract.

Conditional requests solve the other expensive retry pattern: polling unchanged configuration. The server tags each representation with an ETag, such as ETag: "cfg-v7". The device stores that value and asks the server to send the body only if the representation changed:

GET /devices/42/config HTTP/1.1
Host: api.example
If-None-Match: "cfg-v7"

HTTP/1.1 304 Not Modified
ETag: "cfg-v7"
Cache-Control: max-age=30

sequenceDiagram
  participant D as Device
  participant S as Server
  Note over D: Has stored ETag cfg-v7
  D->>S: GET /config with If-None-Match cfg-v7
  alt config unchanged
    S-->>D: 304 Not Modified (no body)
  else config changed
    S-->>D: 200 OK, new body, new ETag
  end

If nothing changed, 304 Not Modified confirms the device is current without transferring the body. If the config changed, the server returns 200 OK, the new body, and a new ETag. The same validator protects writes: If-Match: "cfg-v7" on a PUT tells the server to apply the update only if the resource is still at version 7; otherwise it returns 412 Precondition Failed and avoids overwriting another client’s change.

Cache-Control decides how intermediaries may reuse the response. max-age=60 lets a gateway reuse a device list for 60 seconds, no-cache allows storage but requires revalidation before use, and no-store is the safer choice for tokens and secrets. A release test should cover all three validator cases: unchanged reads return 304 without a body, changed reads return 200 with a new ETag, and stale writes fail with 412.

For each REST endpoint, document three retry facts beside the request example: whether the method is idempotent, whether clients need an idempotency key, and which validator or cache header proves the response can be reused.

Broker BexCheckpoint: Retry and Cache Semantics

The deep dive turns REST naming into operational behavior:

  • You now know why repeated PUT can be safe for a full config replacement, while blind POST /commands retries can enqueue duplicate commands.
  • You now know how ETag, If-None-Match, 304 Not Modified, If-Match, and 412 Precondition Failed prevent wasted downloads and stale writes.
  • You now know what to document for every endpoint before release: idempotency, idempotency keys, validators, cache headers, and the status code a device should expect after a retry.

6.13 Summary

This chapter covered practical REST API design patterns for IoT systems:

Key topics:

  • RESTful vs message-based patterns: Request-response for control, pub-sub for events
  • Topic and URI naming: Consistent hierarchies for MQTT topics and CoAP URIs
  • Payload format selection: JSON for debugging, CBOR/Protobuf for efficiency
  • API versioning: URI versioning recommended for simplicity and compatibility
  • Rate limiting and throttling: Token bucket algorithm, Retry-After headers
  • Security: TLS/DTLS, authentication on every request, credential rotation

6.14 Knowledge Check

Quiz: REST API Design Patterns

6.15 What’s Next?

Chapter Focus Why Read It
REST API Practice Hands-on API design scenarios Practice applying the patterns from this chapter to thermostat and fleet management case studies with step-by-step walkthroughs
MQTT Fundamentals Publish-subscribe protocol deep-dive Understand QoS levels, retained messages, Last Will and Testament, and broker architecture that make MQTT the dominant IoT telemetry protocol
CoAP Fundamentals and Architecture Constrained Application Protocol Learn how CoAP implements REST semantics over UDP with confirmable messages, observe mode, and resource discovery for low-power devices
Real-Time Protocol Workflows Streaming and real-time IoT data Explore WebSockets, Server-Sent Events, and RTSP for applications where sub-second latency matters — video surveillance, voice, and live monitoring
Protocol Selection Worked Examples Multi-protocol comparison Walk through decision frameworks for choosing between HTTP, MQTT, CoAP, and AMQP across six real IoT deployment scenarios
Application Protocols Overview Protocol landscape and comparison Review the full technical comparison of IoT application-layer protocols as a reference when making architecture decisions for new projects