Skip to main content 홈 크리에이터 kayforkind skill-slice expo-api-routes
expo-api-routes Creates Expo Router server endpoints as +api.ts files on EAS Hosting for secrets, database calls, third-party proxies, webhooks, and rate limits. Use when the user needs API routes, server functions, or EAS Hosting in an Expo Router app. Not for public client-side fetch, realtime sockets, direct file uploads, or Convex/Firebase as the backend.
설치로 이동 Skills Marketplace 커뮤니티가 만든 AI 스킬을 발견하고 탐색하세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/Kayforkind/skill-slice --skill expo-api-routes명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Zip 다운로드 다운로드 중... name expo-api-routes description Creates Expo Router server endpoints as +api.ts files on EAS Hosting for secrets, database calls, third-party proxies, webhooks, and rate limits. Use when the user needs API routes, server functions, or EAS Hosting in an Expo Router app. Not for public client-side fetch, realtime sockets, direct file uploads, or Convex/Firebase as the backend. version 1.0.1 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
Use this skill when the task involves creating server-side endpoints in an Expo Router project via +api.ts files. Trigger keywords: "API route", "Expo API", "EAS Hosting", "server function", "backend endpoint", "webhook", "proxy API", "server-side secret".
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
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
Prerequisites
An Expo Router project with an app/ directory
Expo SDK with API route support
For deployment: eas-cli installed and an Expo/EAS account
Install EAS CLI globally (Windows PowerShell):
npm install -g eas-cli
eas login
Procedure
1. Create the API route file
API routes live in the app directory with the +api.ts suffix. The file path maps to the URL path.
app/
api/
hello+api.ts -> GET /api/hello
users+api.ts -> /api/users
users/[id]+api.ts -> /api/users/:id
(tabs)/
index.tsx
Create a basic route:
( ) {
. ({ : });
}
export
function
GET
request : Request
return
Response
json
message
"Hello from Expo!"
2. Define HTTP methods Export named functions for each HTTP method you want to support:
export function GET (request : Request ) {
return Response .json ({ items : [] });
}
export async function POST (request : Request ) {
const body = await request.json ();
return Response .json ({ created : body }, { status : 201 });
}
export async function PUT (request : Request ) {
const body = await request.json ();
return Response .json ({ updated : body });
}
export async function DELETE (request : Request ) {
return new Response (null , { status : 204 });
}
3. Handle dynamic routes
export function GET (request : Request , { id }: { id: string } ) {
return Response .json ({ userId : id });
}
4. Read 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 });
}
5. Read headers and authorize 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 });
}
6. Parse 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 });
}
7. Use environment variables for secrets Use process.env for server-side secrets. NEVER expose API keys or secrets in client code.
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 a .env file (never commit it)
EAS Hosting : Use eas env:create or the Expo dashboard
eas env:create --name OPENAI_API_KEY --value YOUR_KEY --environment production
8. Add CORS headers 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 });
}
9. Handle errors gracefully 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 });
}
}
10. Test locally Start the development server with API route support:
This starts a local server at http://localhost:8081 with full API route support.
Test with curl (PowerShell):
curl http://localhost:8081/api/hello
curl -X POST http://localhost:8081/api/users -H "Content-Type: application/json" -d '{\"name\":\"Test\"}'
11. Deploy to EAS Hosting This builds and deploys your API routes to EAS Hosting (Cloudflare Workers).
Configure a custom domain in eas.json or the Expo dashboard.
12. Call API routes from the 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" }),
});
EAS Hosting Runtime (Cloudflare Workers) API routes run on Cloudflare Workers. Respect these 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 of Node APIs
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 the 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 );
}
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 ());
}
Pitfalls
NEVER expose API keys or secrets in client code. Keep them in process.env and reference only inside +api.ts files.
No Node.js fs or native modules on EAS Hosting. The runtime is Cloudflare Workers; use Web APIs (fetch, crypto.subtle, Response, Request).
30 second CPU timeout. Long-running computations will be killed; offload or chunk them.
No persistent connections / WebSockets without Durable Objects.
Always validate and sanitize user input. Never trust request bodies or query params.
Use correct HTTP status codes : 200, 201, 400, 401, 404, 500.
Wrap handlers in try/catch and log errors server-side; never leak stack traces to clients.
One responsibility per endpoint. Keep routes focused.
.env must never be committed. Add it to .gitignore.
CORS is required for web clients. Browser requests will fail without Access-Control-Allow-Origin and an OPTIONS handler.
File path maps to URL. app/api/users/[id]+api.ts becomes /api/users/:id; misnamed files silently 404.
Use TypeScript for type safety to catch request/response shape errors at build time.
Verification
Confirm the dev server is running:
Expected: server starts at http://localhost:8081.
Verify a GET route responds:
curl http://localhost:8081/api/hello
Expected: {"message":"Hello from Expo!"}
Verify a POST route accepts JSON:
curl -X POST http://localhost:8081/api/users -H "Content-Type: application/json" -d '{\"name\":\"Test\"}'
Expected: JSON response with status 201.
Verify environment variables are loaded (create a temporary debug route):
export function GET ( ) {
return Response .json ({ hasKey : !!process.env .OPENAI_API_KEY });
}
Expected: {"hasKey":true} when .env is present. Remove this route before deploying.
Expected: build completes and a hosting URL is printed. Fetch <deployed-url>/api/hello to confirm.
Verify production secrets exist:
eas env:list --environment production
Expected: OPENAI_API_KEY (and others) listed.
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.
Open a creative mind on the current context and invent a leap the user did not know to ask for — a feature, protocol, CLI, demo, architecture, product move, prose, experiment, or visual. Use when they run /awe-me or /inspire-me, say “awe me” or “inspire me”, want surprise, adjacent possible, make-strange, or are stuck recognizing instead of seeing. Not a brainstorm list. Not /better (quality pass). Not a feasibility grill.
ab-testing-design-and-analysis Design, power, run, and analyze statistically valid A/B/N product experiments (sample size, duration, SRM, sequential monitoring, multiple comparisons). Use when pre-registering a test or reading out a controlled split. Not for event instrumentation, funnel diagnosis, wandb ML tracking (weights-and-biases), MVP smoke tests (mvp-scoping-and-risk-test), or RICE scoring (feature-prioritization-frameworks).