Skip to main content Skills Marketplace Discover and explore AI skills built by the community.
Related occupations SOC
Based on SOC occupation classification
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Copy promptShow prompt details A direct command skips the review prompt. Inspect the source before running it.
npx skills add https://github.com/ARareUsername/tbats-dev --skill expo-api-routesThe command stays on one line. Scroll horizontally to inspect it before copying.
Prefer a local copy? Download the files currently available to SkillsMP.
Download Zip Downloading... More from this repository UI/UX design intelligence for web and mobile. Searchable local database with 50+ styles, 161 color palettes, 57 font pairings, 161 product types, 99 UX guidelines, and 25 chart types across 10 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind, shadcn/ui, and HTML/CSS). Use when designing, building, or reviewing UI: pages, components, color schemes, typography, layout, accessibility, animation, or data visualization.
Security audit, hardening, threat modeling (STRIDE/PASTA), Red/Blue Team, OWASP checks, code review, incident response, and infrastructure security for any project.
Ingeniero de Sistemas de Andru.ia. Diseña, redacta y despliega nuevas habilidades (skills) dentro del repositorio siguiendo el Estándar de Diamante.
name expo-api-routes description Guidelines for creating API routes in Expo Router with EAS Hosting risk unknown source https://github.com/expo/skills/tree/main/plugins/expo/skills/expo-api-routes source_repo expo/skills source_type official date_added "2026-07-01T00:00:00.000Z" license MIT license_source https://github.com/expo/skills/blob/main/LICENSE
When to Use API Routes
Use API routes when you need:
Server-side secrets — API keys, database credentials, or tokens that must never reach the client
Database operations — Direct database queries that shouldn't be exposed
Third-party API proxies — Hide API keys when calling external services (OpenAI, Stripe, etc.)
Server-side validation — Validate data before database writes
Webhook endpoints — Receive callbacks from services like Stripe or GitHub
Rate limiting — Control access at the server level
Heavy computation — Offload processing that would be slow on mobile
When NOT to Use API Routes
Avoid API routes when:
Data is already public — Use direct fetch to public APIs instead
No secrets required — Static data or client-safe operations
Real-time updates needed — Use WebSockets or services like Supabase Realtime
Simple CRUD — Consider Firebase, Supabase, or Convex for managed backends
File uploads — Use direct-to-storage uploads (S3 presigned URLs, Cloudflare R2)
Authentication only — Use Clerk, Auth0, or Firebase Auth instead
File Structure
API routes live in the app directory with +api.ts suffix:
app/
api/
hello+api.ts → GET /api/hello
users+api.ts → /api/users
users/[id]+api.ts → /api/users/:id
(tabs)/
index.tsx
Basic API Route
export function GET (request : Request ) {
return Response .json ({ message : "Hello from Expo!" });
}
HTTP Methods
Export named functions for each HTTP method:
( ) {
. ({ : [] });
}
( ) {
body = request. ();
. ({ : body }, { : });
}
( ) {
body = request. ();
. ({ : body });
}
( ) {
( , { : });
}
export
function
GET
request : Request
return
Response
json
items
export
async
function
POST
request : Request
const
await
json
return
Response
json
created
status
201
export
async
function
PUT
request : Request
const
await
json
return
Response
json
updated
export
async
function
DELETE
request : Request
return
new
Response
null
status
204
Dynamic Routes
export function GET (request : Request , { id }: { id: string } ) {
return Response .json ({ userId : id });
}
Request Handling
Query Parameters export function GET (request : Request ) {
const url = new URL (request.url );
const page = url.searchParams .get ("page" ) ?? "1" ;
const limit = url.searchParams .get ("limit" ) ?? "10" ;
return Response .json ({ page, limit });
}
Headers export function GET (request : Request ) {
const auth = request.headers .get ("Authorization" );
if (!auth) {
return Response .json ({ error : "Unauthorized" }, { status : 401 });
}
return Response .json ({ authenticated : true });
}
JSON Body export async function POST (request : Request ) {
const { email, password } = await request.json ();
if (!email || !password) {
return Response .json ({ error : "Missing fields" }, { status : 400 });
}
return Response .json ({ success : true });
}
Environment Variables Use process.env for server-side secrets:
export async function POST (request : Request ) {
const { prompt } = await request.json ();
const response = await fetch ("https://api.openai.com/v1/chat/completions" , {
method : "POST" ,
headers : {
"Content-Type" : "application/json" ,
Authorization : `Bearer ${process.env.OPENAI_API_KEY} ` ,
},
body : JSON .stringify ({
model : "gpt-4" ,
messages : [{ role : "user" , content : prompt }],
}),
});
const data = await response.json ();
return Response .json (data);
}
Set environment variables:
Local : Create .env file (never commit)
EAS Hosting : Use eas env:create or Expo dashboard
CORS Headers Add CORS for web clients:
const corsHeaders = {
"Access-Control-Allow-Origin" : "*" ,
"Access-Control-Allow-Methods" : "GET, POST, PUT, DELETE, OPTIONS" ,
"Access-Control-Allow-Headers" : "Content-Type, Authorization" ,
};
export function OPTIONS ( ) {
return new Response (null , { headers : corsHeaders });
}
export function GET ( ) {
return Response .json ({ data : "value" }, { headers : corsHeaders });
}
Error Handling export async function POST (request : Request ) {
try {
const body = await request.json ();
return Response .json ({ success : true });
} catch (error) {
console .error ("API error:" , error);
return Response .json ({ error : "Internal server error" }, { status : 500 });
}
}
Testing Locally Start the development server with API routes:
This starts a local server at http://localhost:8081 with full API route support.
curl http://localhost:8081/api/hello
curl -X POST http://localhost:8081/api/users -H "Content-Type: application/json" -d '{"name":"Test"}'
Deployment to EAS Hosting
Prerequisites npm install -g eas-cli
eas login
Deploy This builds and deploys your API routes to EAS Hosting (Cloudflare Workers).
Environment Variables for Production
eas env :create --name OPENAI_API_KEY --value sk-xxx --environment production
Custom Domain Configure in eas.json or Expo dashboard.
EAS Hosting Runtime (Cloudflare Workers) API routes run on Cloudflare Workers. Key limitations:
Missing/Limited APIs
No Node.js filesystem — fs module unavailable
No native Node modules — Use Web APIs or polyfills
Limited execution time — 30 second timeout for CPU-intensive tasks
No persistent connections — WebSockets require Durable Objects
fetch is available — Use standard fetch for HTTP requests
Use Web APIs Instead
const hash = await crypto.subtle .digest (
"SHA-256" ,
new TextEncoder ().encode ("data" )
);
const response = await fetch ("https://api.example.com" );
return new Response (JSON .stringify (data), {
headers : { "Content-Type" : "application/json" },
});
Database Options Since filesystem is unavailable, use cloud databases:
Cloudflare D1 — SQLite at the edge
Turso — Distributed SQLite
PlanetScale — Serverless MySQL
Supabase — Postgres with REST API
Neon — Serverless Postgres
import { createClient } from "@libsql/client/web" ;
const db = createClient ({
url : process.env .TURSO_URL !,
authToken : process.env .TURSO_AUTH_TOKEN !,
});
export async function GET ( ) {
const result = await db.execute ("SELECT * FROM users" );
return Response .json (result.rows );
}
Calling API Routes from Client
const response = await fetch ("/api/hello" );
const data = await response.json ();
const response = await fetch ("/api/users" , {
method : "POST" ,
headers : { "Content-Type" : "application/json" },
body : JSON .stringify ({ name : "John" }),
});
Common Patterns
Authentication Middleware
export async function requireAuth (request : Request ) {
const token = request.headers .get ("Authorization" )?.replace ("Bearer " , "" );
if (!token) {
throw new Response (JSON .stringify ({ error : "Unauthorized" }), {
status : 401 ,
headers : { "Content-Type" : "application/json" },
});
}
return { userId : "123" };
}
import { requireAuth } from "../../utils/auth" ;
export async function GET (request : Request ) {
const { userId } = await requireAuth (request);
return Response .json ({ userId });
}
Proxy External API
export async function GET (request : Request ) {
const url = new URL (request.url );
const city = url.searchParams .get ("city" );
const response = await fetch (
`https://api.weather.com/v1/current?city=${city} &key=${process.env.WEATHER_API_KEY} `
);
return Response .json (await response.json ());
}
Rules
NEVER expose API keys or secrets in client code
ALWAYS validate and sanitize user input
Use proper HTTP status codes (200, 201, 400, 401, 404, 500)
Handle errors gracefully with try/catch
Keep API routes focused — one responsibility per endpoint
Use TypeScript for type safety
Log errors server-side for debugging
Limitations
Use this skill only when the task clearly matches its upstream product or API scope.
Verify commands, API behavior, pricing, quotas, credentials, and deployment effects against current official documentation before making changes.
Do not treat generated examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.