OAuth DPoP Response Mode
When implementing ATProto OAuth, use responseMode: 'query' not 'fragment'. Fragment mode silently fails because Cloudflare Workers cannot read URL fragments from the client.
Cloudflare Error 1042
If you get error 1042 (Worker exceeded CPU time limit) when accessing D1, it often means your D1 binding is misconfigured. Use shared D1 bindings across workers in the same account. Double-check the database_id in wrangler.toml matches your actual D1 database.
Two-Step Write Pattern
When writing data: always update the PDS first via putRecord, then sync to your D1 index. If PDS fails, don't write to D1. If D1 fails after PDS succeeds, your cron crawler will eventually catch it. Never write to D1 alone -- the PDS is the source of truth.
Lexicon DNS Discovery
To make your custom lexicon discoverable, add a _lexicon.{authority} TXT record to your DNS pointing to your DID. For example: _lexicon.filae.site TXT "did:plc:dcb6ifdsru63appkbffy3foy"
Wrangler Deploy Hangs
wrangler deploy sometimes hangs on CI. As a fallback, use the Cloudflare REST API: upload the Worker script directly to https://api.cloudflare.com/client/v4/accounts/{id}/workers/scripts/{name}. The GitHub Actions workflow uses wrangler-action@v3 which handles timeouts better.
CORS: Proxy Through Worker
Browser JavaScript cannot call bsky.social/xrpc directly due to CORS. Always proxy ATProto API calls through your Worker using a /api/proxy route. The SPA template includes this pattern.
D1 Batch Init
Use db.batch() for multi-statement D1 initialization (CREATE TABLE, CREATE INDEX). Individual db.exec() calls with multiple statements separated by semicolons can fail silently. The db.batch() approach also runs in a single round-trip.