Build Serverless API Routes with Expo
Expo API Routes covers +api.ts server endpoints, secrets, CORS, and deploying to EAS Hosting on Cloudflare Workers.
Why it matters
Develop secure and efficient server-side logic for your Expo applications. This asset enables you to manage secrets, interact with databases, and proxy third-party APIs directly within your Expo project.
Outcomes
What it gets done
Securely store and access API keys and database credentials using environment variables.
Implement dynamic routes and handle various HTTP methods (GET, POST, PUT, DELETE).
Integrate with cloud databases like Turso, Supabase, or PlanetScale.
Deploy your API routes to EAS Hosting for a serverless backend.
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/ag-expo-api-routes | bash Overview
Create a secret
Covers building server-side +api.ts endpoints in Expo apps - secrets, CORS, error handling, edge-compatible databases - and deploying them to EAS Hosting on Cloudflare Workers. Use when an Expo app needs server-side secrets, database access, third-party API proxying, or webhooks - not for public data, real-time updates, simple CRUD, or auth-only needs.
What it does
Expo API Routes covers building and deploying server-side endpoints inside an Expo app using the +api.ts file convention. Routes live in the app directory with a +api.ts suffix - app/api/hello+api.ts maps to GET /api/hello, app/api/users/[id]+api.ts maps to /api/users/:id - and each file exports named functions per HTTP method (GET, POST, PUT, DELETE), reading query parameters via new URL(request.url).searchParams, headers via request.headers.get(), and JSON bodies via await request.json(), and returning Response.json() with appropriate status codes.
Server-side secrets are read from process.env (e.g. proxying to the OpenAI API with process.env.OPENAI_API_KEY), set locally via an uncommitted .env file and in production via eas env:create or the Expo dashboard. CORS is handled by returning shared headers on an OPTIONS handler and attaching them to other responses. Errors are wrapped in try/catch, logged server-side, and returned as a JSON error body with a 500 status.
Local testing runs via npx expo serve, which starts a full API-route-capable server at http://localhost:8081, testable with curl. Deployment goes through EAS Hosting: install eas-cli, eas login, then eas deploy, which builds and deploys the API routes to Cloudflare Workers, with production secrets set via eas env:create --name KEY --value val --environment production and custom domains configured in eas.json or the dashboard.
Because API routes run on Cloudflare Workers, several Node.js APIs are unavailable: no filesystem (fs), no native Node modules, a 30-second execution timeout for CPU-intensive tasks, and no persistent connections (WebSockets require Durable Objects) - though standard fetch is available. Node-specific APIs are replaced with Web APIs: crypto.subtle.digest() instead of Node's crypto, standard fetch instead of node-fetch, and the built-in Response/Request objects. Since there's no filesystem, database access goes through edge-compatible cloud databases - Cloudflare D1, Turso, PlanetScale, Supabase, or Neon - with a worked Turso example using @libsql/client/web.
Client code calls these routes with plain fetch('/api/hello') from React Native components. Common patterns include an requireAuth() middleware that reads a Bearer token from the Authorization header and throws a 401 Response if missing, and a proxy pattern that reads a query parameter, calls an external API with a server-side key, and returns the result as JSON.
When to use - and when NOT to
Use API routes when you need server-side secrets (API keys, database credentials, tokens that must never reach the client), direct database operations that shouldn't be exposed, a proxy for third-party APIs (OpenAI, Stripe, etc.), server-side validation before database writes, webhook endpoints (Stripe, GitHub), server-level rate limiting, or heavy computation offloaded from mobile.
Avoid API routes when data is already public (fetch it directly instead), no secrets are required, real-time updates are needed (use WebSockets or Supabase Realtime instead), it's simple CRUD better served by a managed backend (Firebase, Supabase, Convex), file uploads are needed (use direct-to-storage presigned URLs instead), or the need is authentication only (use Clerk, Auth0, or Firebase Auth instead). Never expose API keys or secrets in client code, always validate and sanitize user input, and keep each route focused on one responsibility. Do not treat the output as a substitute for environment-specific validation, testing, or expert review, and stop to ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
Who it's for
Expo/React Native developers who need server-side logic - secret handling, third-party API proxying, webhooks, or database access - without standing up a separate backend service.
FAQ
Common questions
Discussion
Questions & comments ยท 0
Sign In Sign in to leave a comment.