expo-api-routes – skill Lập trình cho agent AI
Hướng dẫn tạo API routes trong Expo Router với EAS Hosting
Cài skill expo-api-routes bằng một lệnh
Chọn công cụ bạn đang dùng, copy lệnh và dán vào terminal. Skill sẽ nằm trong thư mục skills của công cụ đó và sẵn sàng dùng ở phiên làm việc tiếp theo.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/expo-api-routes-fce9a.tgz | tar xz -C ~/.claude/skills
Gọi trong Claude Code: /expo-api-routes – rồi mô tả công việc bạn muốn làm.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/expo-api-routes-fce9a.tgz | tar xz -C ~/.codex/skills
Gọi trong Codex: $expo-api-routes – rồi mô tả công việc bạn muốn làm.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/expo-api-routes-fce9a.tgz | tar xz -C ~/.hermes/skills
Gọi trong Hermes Agent: /expo-api-routes – rồi mô tả công việc bạn muốn làm.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/expo-api-routes-fce9a.tgz | tar xz -C ~/.deepcode/skills
Gọi trong DeepSeek: /expo-api-routes – rồi mô tả công việc bạn muốn làm.
Nội dung SKILL.md
Nội dung SKILL.md gốc bằng tiếng Anh.
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
Confirm the installed Expo/EAS version and configure web output for server routes. Fragments are incomplete demos: header presence and nonempty password fields do not authenticate anyone. Implement token verification, per-user authorization, body/schema/size limits and rate/cost limits before exposing database or paid AI proxy routes. Keep secrets out of EXPO_PUBLIC variables/client imports. Native callers need the configured server origin; do not assume a relative URL works identically on every platform. Validate webhook signatures over the required raw body and deduplicate events. Bound upstream requests, check response.ok and return only approved fields; the weather endpoint is illustrative, not a verified API contract.
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.tsxBasic API Route
// app/api/hello+api.ts
export function GET(request: Request) {
return Response.json({ message: "Hello from Expo!" });
}HTTP Methods
Export named functions for each HTTP method:
// app/api/items+api.ts
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 });
}Dynamic Routes
// app/api/users/[id]+api.ts
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 async function GET(request: Request) {
const auth = request.headers.get("Authorization");
if (!auth || !(await verifyAccessToken(auth))) { // implement signature/issuer/audience/expiry checks
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:
// app/api/ai+api.ts
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
.envfile (never commit) - EAS Hosting: Use
eas env:createor 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();
// Process...
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:
npx expo start
# To test an exported production bundle instead:
# npx expo export --platform web
# npx expo serveThis starts a local server at http://localhost:8081 with full API route support.
Test with curl:
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 loginDeploy
eas deployExport the configured server web output first, then deploy that artifact with the supported EAS CLI; deployment is a separate authorized public action.
Environment Variables for Production
# Create a secret
eas env:create --name OPENAI_API_KEY --environment production
# Use the supported interactive secret input; never pass real keys in command arguments.
# Or use the Expo dashboardCustom 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 —
fsmodule 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
// Use Web Crypto instead of Node crypto
const hash = await crypto.subtle.digest(
"SHA-256",
new TextEncoder().encode("data")
);
// Use fetch instead of node-fetch
const response = await fetch("https://api.example.com");
// Use Response/Request (already available)
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
Example with Turso:
// app/api/users+api.ts
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
// From React Native components
const response = await fetch("/api/hello");
const data = await response.json();
// With body
const response = await fetch("/api/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "John" }),
});Common Patterns
Authentication Middleware
// utils/auth.ts
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" },
});
}
// Implement with the application identity provider; fail closed until implemented.
throw new Error("Token verification must be implemented; never return a fabricated user");
}
// app/api/protected+api.ts
import { requireAuth } from "../../utils/auth";
export async function GET(request: Request) {
const { userId } = await requireAuth(request);
return Response.json({ use… (nội dung đầy đủ có trong gói tải về)
Skill liên quan
- expo-brownfield – Tích hợp Expo và React Native vào ứng dụng iOS hoặc Android native có sẵn. Dùng khi…
- expo-module – Hướng dẫn tạo và viết module và view native của Expo bằng Expo Modules API (Swift…
- zarr-python – Lưu trữ và truy vấn mảng khoa học N chiều phân khối (chunked) với Zarr-Python 3, bao gồm…
- faf-go – Phỏng vấn có hướng dẫn để đạt Gold Code (100% sẵn sàng cho AI). Dùng khi giúp người dùng…
- web3-testing – Kiểm thử smart contract toàn diện bằng Hardhat và Foundry với unit test, integration…
- roast – Dùng khi ai đó yêu cầu roast một ý tưởng, kiểm tra áp lực hoặc stress-test một ý tưởng…
- fastapi-pro – Xây dựng API bất đồng bộ hiệu năng cao với FastAPI, SQLAlchemy 2.0 và Pydantic V2. Thành…
- fastapi-templates – Tạo dự án FastAPI sẵn sàng production với mẫu async, dependency injection và xử lý lỗi…