EAS Hosting
EAS-Dienst – Kosten fallen an. EAS Hosting ist ein kostenpflichtiges Expo Application Services-Produkt mit Free-Tier-Limits; Produktions-Deployments nutzen das Anfrage- und Bandbreitenkontingent Ihres Plans. Siehe https://expo.dev/pricing. Das Erstellen von API-Routen und der Export des Web-Bundles sind kostenlos und Open Source; Sie können das exportierte Server-Output auch selbst hosten anstatt EAS Hosting zu verwenden.
EAS Hosting stellt Ihre Expo- Web-App und API-Routen auf dem verwalteten Edge von Expo (Cloudflare Workers) bereit. Exportieren Sie das Web-Bundle mit npx expo export -p web und stellen Sie es mit eas deploy bereit – derselbe Befehl stellt alle Expo-Router-API-Routen bereit, die damit gebündelt sind. Diese Skill-Anleitung behandelt das Bereitstellen einer Website, das Erstellen von API-Routen und die Hosting-Laufzeit; siehe den Abschnitt „Bereitstellung“ weiter unten für den Deployment-Workflow.
Wann API-Routen verwendet werden sollten
Verwenden Sie API-Routen, wenn Sie Folgendes benötigen:
- Serverseitige Geheimnisse – API-Schlüssel, Datenbank-Anmeldedaten oder Tokens, die niemals an den Client gelangen dürfen
- Datenbankoperationen – Direkte Datenbankabfragen, die nicht offengelegt werden sollten
- Drittanbieter-API-Proxy – API-Schlüssel beim Aufruf externer Dienste (OpenAI, Stripe usw.) verbergen
- Serverseitige Validierung – Daten vor dem Schreiben in die Datenbank validieren
- Webhook-Endpunkte – Rückrufe von Diensten wie Stripe oder GitHub empfangen
- Ratenbegrenzung – Zugriff auf Serverebene steuern
- Aufwändige Berechnungen – Verarbeitung auslagern, die auf mobilen Geräten langsam wäre
Wann API-Routen NICHT verwendet werden sollten
Vermeiden Sie API-Routen, wenn:
- Daten bereits öffentlich sind – Verwenden Sie stattdessen direkte Fetch-Aufrufe an öffentliche APIs
- Keine Geheimnisse erforderlich – Statische Daten oder client-seitig sichere Operationen
- Echtzeit-Updates benötigt – Verwenden Sie WebSockets oder Dienste wie Supabase Realtime
- Einfaches CRUD – Ziehen Sie Firebase, Supabase oder Convex für verwaltete Backends in Betracht
- Datei-Uploads – Verwenden Sie Direkt-zu-Speicher-Uploads (S3-Presigned-URLs, Cloudflare R2)
- Nur Authentifizierung – Verwenden Sie stattdessen Clerk, Auth0 oder Firebase Auth
Dateistruktur
API-Routen befinden sich im app-Verzeichnis mit dem Suffix +api.ts:
app/
api/
hello+api.ts → GET /api/hello
users+api.ts → /api/users
users/[id]+api.ts → /api/users/:id
(tabs)/
index.tsx
Grundlegende API-Route
// app/api/hello+api.ts
export function GET(request: Request) {
return Response.json({ message: "Hallo von Expo!" });
}
HTTP-Methoden
Exportieren Sie benannte Funktionen für jede HTTP-Methode:
// 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 });
}
Dynamische Routen
// app/api/users/[id]+api.ts
export function GET(request: Request, { id }: { id: string }) {
return Response.json({ userId: id });
}
Request-Handling
Abfrageparameter
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 });
}
Header
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: "Fehlende Felder" }, { status: 400 });
}
return Response.json({ success: true });
}
Umgebungsvariablen
Verwenden Sie process.env für serverseitige Geheimnisse:
// 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);
}
Umgebungsvariablen festlegen:
- Lokal: Erstellen Sie eine
.env-Datei (niemals einchecken) - EAS Hosting: Verwenden Sie
eas env:createoder das Expo-Dashboard
CORS-Header
Fügen Sie CORS für Web-Clients hinzu:
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: "Wert" }, { headers: corsHeaders });
}
Fehlerbehandlung
export async function POST(request: Request) {
try {
const body = await request.json();
// Verarbeitung...
return Response.json({ success: true });
} catch (error) {
console.error("API-Fehler:", error);
return Response.json({ error: "Interner Serverfehler" }, { status: 500 });
}
}
Lokales Testen
Starten Sie den Entwicklungsserver mit API-Routen:
npx expo serve
Dies startet einen lokalen Server unter http://localhost:8081 mit vollständiger API-Routen-Unterstützung.
Testen Sie mit curl:
curl http://localhost:8081/api/hello
curl -X POST http://localhost:8081/api/users -H "Content-Type: application/json" -d '{"name":"Test"}'
Bereitstellung auf EAS Hosting
Voraussetzungen
npm install -g eas-cli
eas login
Bereitstellen
Das Deployment liefert Ihr Web-Bundle und alle Expo-Router-API-Routen zusammen aus – eas deploy übernimmt beides. Der Export läuft unabhängig davon, ob Sie eine vollständige Website, ein reines API-Routen-Backend oder beides haben.
# Exportieren des Web-Bundles (enthält alle API-Routen)
npx expo export -p web
# Bereitstellen einer Vorschau (PR-artige URL)
npx eas-cli@latest deploy
# Bereitstellen in die Produktion
npx eas-cli@latest deploy --prod
Alles landet auf EAS Hosting (Cloudflare Workers).
Umgebungsvariablen für die Produktion
# Erstellen eines Geheimnisses
eas env:create --name OPENAI_API_KEY --value sk-xxx --environment production
# Oder das Expo-Dashboard verwenden
Benutzerdefinierte Domain
Konfigurieren Sie diese in eas.json oder im Expo-Dashboard.
Automatisieren mit EAS Workflows
Stellen Sie die Website (und API-Routen) bei jedem Push auf main mit einem type: deploy-Workflow bereit:
.eas/workflows/deploy.yml
name: Bereitstellen
on:
push:
branches:
- main
# https://docs.expo.dev/eas/workflows/syntax/#deploy
jobs:
deploy_web:
type: deploy
params:
prod: true
Vorschau-Bereitstellungen für Pull-Requests verwenden denselben Job-Typ mit prod: false:
name: Web-PR-Vorschau
on:
pull_request:
types: [opened, synchronize]
jobs:
preview:
type: deploy
params:
prod: false
Verwenden Sie die Skill eas-workflows, um Workflow-YAML über diese Beispiele hinaus zu erstellen oder zu validieren.
EAS Hosting-Laufzeit (Cloudflare Workers)
API-Routen laufen auf Cloudflare Workers. Wichtige Einschränkungen:
Fehlende / Eingeschränkte APIs
- Kein Node.js-Dateisystem –
fs-Modul nicht verfügbar - Keine nativen Node-Module – Verwenden Sie Web-APIs oder Polyfills
- Begrenzte Ausführungszeit – 30-Sekunden-Timeout für CPU-intensive Aufgaben
- Keine persistierenden Verbindungen – WebSockets erfordern Durable Objects
- fetch ist verfügbar – Verwenden Sie standardmäßiges fetch für HTTP-Anfragen
Verwenden Sie stattdessen Web-APIs
// Web Crypto statt Node crypto verwenden
const hash = await crypto.subtle.digest(
"SHA-256",
new TextEncoder().encode("data")
);
// fetch statt node-fetch verwenden
const response = await fetch("https://api.example.com");
// Response/Request (bereits verfügbar) verwenden
return new Response(JSON.stringify(data), {
headers: { "Content-Type": "application/json" },
});
Datenbank-Optionen
Da das Dateisystem nicht verfügbar ist, verwenden Sie Cloud-Datenbanken:
- Cloudflare D1 – SQLite am Edge
- Turso – Verteiltes SQLite
- PlanetScale – Serverloses MySQL
- Supabase – Postgres mit REST-API
- Neon – Serverloses Postgres
Beispiel mit 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);
}
Aufruf von API-Routen vom Client aus
// Von React-Native-Komponenten
const response = await fetch("/api/hello");
const data = await response.json();
// Mit Body
const response = await fetch("/api/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "John" }),
});
Häufige Muster
Authentifizierungs-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" },
});
}
// Token verifizieren...
return { userId: "123" };
}
// app/api/protected+api.ts
import { requireAuth } from "../../utils/auth";
export async function GET(request: Request) {
const { userId } = await requireAuth(request);
return Response.json({ userId });
}
Externe API proxieren
// app/api/weather+api.ts
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());
}
Regeln
- NIEMALS API-Schlüssel oder Geheimnisse im Client-Code preisgeben
- IMMER Benutzereingaben validieren und bereinigen
- Korrekte HTTP-Statuscodes verwenden (200, 201, 400, 401, 404, 500)
- Fehler mit try/catch ordnungsgemäß behandeln
- API-Routen fokussiert halten – eine Verantwortung pro Endpunkt
- TypeScript für Typsicherheit verwenden
- Fehler serverseitig für Debugging protokollieren
Feedback einreichen
Wenn Sie Fehler, irreführende oder veraltete Informationen in dieser Skill-Anleitung finden, melden Sie diese, damit Expo sie verbessern kann:
npx --yes @expo/agent-cli@latest feedback --category skills --subject "eas-hosting" "<konstruktives Feedback>"
Reichen Sie nur dann etwas ein, wenn Sie etwas Konkretes und Umsetzbares zu melden haben. Fügen Sie so viel relevanten Kontext wie möglich hinzu.
Wenn ein Agent wiederholt bei einer Expo-Aufgabe scheitert oder der Benutzer eingreifen muss, reichen Sie einen kurzen Bericht über die Aufgabe, das erwartete Ergebnis und was schiefgelaufen ist mit --category evals und dem entsprechenden Expo-Paket oder -Befehl als --subject ein.