Set up a server-side proxy to forward Electric shape requests securely. Covers ELECTRIC_PROTOCOL_QUERY_PARAMS forwarding, server-side shape definition (table, where, params), content-encoding/content-length header cleanup, CORS configuration for electric-offset/electric-handle/ electric-schema/electric-cursor headers, auth token injection, Bun fetch concurrency cap (BUN_CONFIG_MAX_HTTP_REQUESTS default 256), ELECTRIC_SECRET/SOURCE_SECRET server-side only, tenant isolation via WHERE positional params, onError 401 token refresh, and subset security (AND semantics). Load when creating proxy routes, adding auth, or configuring CORS for Electric.
Set up a server-side proxy to forward Electric shape requests securely. Covers ELECTRIC_PROTOCOL_QUERY_PARAMS forwarding, server-side shape definition (table, where, params), content-encoding/content-length header cleanup, CORS configuration for electric-offset/electric-handle/ electric-schema/electric-cursor headers, auth token injection, Bun fetch concurrency cap (BUN_CONFIG_MAX_HTTP_REQUESTS default 256), ELECTRIC_SECRET/SOURCE_SECRET server-side only, tenant isolation via WHERE positional params, onError 401 token refresh, and subset security (AND semantics). Load when creating proxy routes, adding auth, or configuring CORS for Electric.
Electric combines the main shape WHERE (set in proxy) with subset WHERE (from POST body) using AND. Subsets can only narrow results, never widen them:
-- Main shape: WHERE org_id = $1 (set by proxy)-- Subset: WHERE status = 'active' (from client POST)-- Effective: WHERE org_id = $1 AND status = 'active'
Even WHERE 1=1 in the subset cannot bypass the main shape's WHERE.
String interpolation in WHERE clauses enables SQL injection. Use positional params ($1, $2).
Source: website/docs/guides/auth.md
HIGH Bun fetch concurrency cap (256) bottlenecks the proxy under load
Wrong:
bun run proxy.ts
Correct:
BUN_CONFIG_MAX_HTTP_REQUESTS=4096 bun run proxy.ts
Bun caps simultaneous fetch() calls at 256 per process by default. Excess requests queue silently — the proxy keeps accepting inbound connections but upstream calls to Electric stall, surfacing as latency spikes rather than errors. Raise BUN_CONFIG_MAX_HTTP_REQUESTS (max 65,336) to match your expected concurrent shape requests. Node and Deno do not impose this cap.
The client throws MissingHeadersError if Electric response headers are stripped by CORS. Expose electric-offset, electric-handle, electric-schema, and electric-cursor.
newShapeStream({
url: '/api/todos', // Your proxy route
})
Electric's HTTP API is public by default with no auth. Always proxy through your server so the server controls shape definitions and injects secrets.
Source: AGENTS.md:19-20
See also: electric-shapes/SKILL.md — Shape URLs must point to proxy routes, not directly to Electric.
See also: electric-deployment/SKILL.md — Production requires ELECTRIC_SECRET and proxy; dev uses ELECTRIC_INSECURE=true.
See also: electric-postgres-security/SKILL.md — Proxy injects secrets that Postgres security enforces.