Home / Blog / Serverless device API
IoT Backend and Serverless Systems

ESP32 and Cloudflare Workers: A Serverless API for Physical Devices

A small sensor fleet does not need a server that idles all day just to accept a few measurements. An ESP32 can send bounded HTTPS batches to a Cloudflare Worker, where identity, schema and authorization are checked before a binding writes to storage or queues asynchronous work. Serverless changes where operations happen; it does not remove the need for device identity, retries, retention or failure handling.

Follow one reading across the boundary

Keep device credentials on the device and data-store access behind Worker bindings
1 / ESP32Samples, sequences and signs/authenticates a bounded batch.
2 / HTTPSTLS protects transport; endpoint scopes a device identity.
3 / WorkerVerifies auth, body size, schema, replay and rate limits.
4 / BindingPrepared D1 write or Queue message; no DB secret in firmware.
5 / DashboardSeparate user auth and tenant-scoped read API.

Choose HTTP when devices report periodically and can retry a request. MQTT may be better for persistent pub/sub or downlink needs, but the Worker should not be assumed to be a generic MQTT broker. An HTTPS Worker endpoint is a straightforward ingestion boundary; compare protocol support and service limits for your actual deployment before committing.

Give each unit a narrow identity

Do not embed a Supabase service-role key, database password or shared fleet secret in firmware. A device credential should identify one unit and authorize only its expected operations. Depending on your threat model and provisioning capacity, use per-device credentials with rotation/revocation or a signed request scheme with securely provisioned keys. TLS server validation is mandatory; client authentication can be layered through a token/signature or a supported mutual-TLS architecture.

For request signing, include method, path, device ID, timestamp, nonce and body digest in the signature. The Worker validates the signature in constant time, checks an allowed clock window, and records nonce/event IDs to reject replay. A device with a bad clock needs a carefully designed bootstrap or server challenge; do not silently disable freshness checks. Rotate credentials through staged overlap and provide a lost-device revocation path.

Validate before touching storage

Boundary checkReasonFailure behavior
Method, route and content typeKeep the public surface small.Reject unsupported requests early.
Body length and batch countBound parsing and storage work.Return a clear 413/400; device splits batch.
Device identity and tenant mappingPrevent cross-device writes.Reject unauthorized identity; do not trust body-supplied tenant.
Schema, units, ranges and timestampKeep corrupt or ambiguous telemetry out.Reject or quarantine with a reason code.
Event ID, sequence and rateHandle duplicate retries and floods.Return prior accepted result or bounded retry guidance.

Apply payload limits that fit device memory and the Worker account's request constraints; large account-level uploads do not make oversized telemetry batches a good design. Parse bounded JSON, cap array depth/count and validate fields explicitly. Never construct SQL by concatenating device input. D1 bindings support prepared statements; bind values and derive the authorized device ID from verified credentials, not an untrusted JSON field.

Choose synchronous storage or a queue deliberately

For modest telemetry, the Worker can write directly through a D1 binding using prepared statements and an idempotency key. This keeps the flow simple, but request success should mean the durable write completed. For bursts or slower downstream work, a Queue can decouple acceptance from processing: acknowledge only after enqueue succeeds, make the consumer idempotent, configure bounded retries and a dead-letter strategy, and expose delayed/failed work operationally. A queue is not a substitute for a retention policy or end-to-end acknowledgment.

If you already operate Postgres elsewhere, a Worker can reach it through an appropriate supported connection path such as Hyperdrive or a service API, but connection management and database capacity still matter. Avoid choosing storage only because it shares a vendor dashboard.

Make retries safe on flaky links

Devices should persist event IDs or monotonic sequence numbers until the API confirms acceptance. Retry timeouts with exponential backoff and jitter; a timeout can occur after the server committed, so the same ID must not create a second row. Return explicit accepted, duplicate, invalid and retryable outcomes. Keep batches bounded and provide a local queue limit, disk-full policy and operator alert for long outages.

Secure the operator path separately

Device authentication and human dashboard authentication are separate trust domains. The dashboard calls a user-authenticated API that scopes every read to an authorized tenant/site. Never expose device bearer tokens to browser JavaScript. Keep Worker credentials in encrypted secrets or bindings, not plaintext configuration, repository history or logs. Redact authorization headers, signatures and sensitive telemetry from error reporting.

Instrument limits and failure modes

Measure accepted/rejected requests, duplicate rate, ingestion delay, queue age, database errors, per-device silence and end-to-end data freshness. Add request IDs without using them as credentials. Test expired signatures, replay, malformed payloads, D1 outage, queue retry exhaustion, clock drift, network loss and key revocation. Confirm the device still performs safety-critical behavior offline.

In summary

Workers can provide a compact API edge for ESP32 fleets: authenticate one device, validate a bounded event, then write or enqueue through server-side bindings. Keep database credentials out of firmware, make retries idempotent, isolate dashboard users, and treat queueing, quotas, retention and offline behavior as first-class design choices.

References