Communitygithub.com

expo/skills

Bereitstellung von Expo-Websites und Expo-Router-API-Routen auf EAS Hosting – Export des Web-Bundles, Ausführen von ‚eas deploy‘ für Produktion und PR-Vorschau-URLs, Verwalten von Umgebungsgeheimnissen und benutzerdefinierten Domains sowie Arbeiten in der Cloudflare-Workers-Laufzeitumgebung.

Was ist skills?

Diese Skill-Anleitung beschreibt die Bereitstellung von Expo-Webanwendungen und Expo-Router-API-Routen auf EAS Hosting, einem verwalteten Edge-Hosting-Dienst auf Basis von Cloudflare Workers. Sie deckt den gesamten Workflow ab: das Exportieren des Web-Bundles mit `npx expo export -p web`, das Deployment von Produktions- und Vorschau-Umgebungen über `eas deploy`, die Verwaltung von Umgebungsvariablen, Geheimnissen und benutzerdefinierten Domains sowie die Nutzung der Cloudflare-Workers-Laufzeit. Darüber hinaus wird detailliert erklärt, wann API-Routen sinnvoll sind (z. B. für serverseitige Geheimnisse, Datenbankoperationen oder Drittanbieter-Proxy) und wann Alternativen wie WebSockets oder verwaltete Backends vorzuziehen sind. Die Anleitung enthält Codebeispiele für HTTP-Methoden, dynamische Routen, Request-Handling (Query-Parameter, Header, JSON-Body), CORS, Fehlerbehandlung und die Integration von Datenbanken wie Turso. Zusätzlich werden Best Practices für Authentifizierungs-Middleware und externe API-Proxys vermittelt sowie die Einschränkungen der Cloudflare-Workers-Laufzeit (z. B. kein Node.js-Dateisystem, begrenzte Ausführungszeit) erläutert. Diese Skill ist für Entwickler gedacht, die Expo-Web-Apps oder API-Routen hosten, EAS Hosting einrichten oder Hosting-Umgebungen und -Domains konfigurieren möchten. Nicht geeignet für native Builds oder Store-Veröffentlichungen – dafür ist die Skill ‚eas-app-stores‘ zuständig.

Funktioniert mit~Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/expo/skills/tree/HEAD/plugins/expo/skills/eas-hosting

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

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:create oder 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.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

expo/add-app-clip

Add an iOS App Clip target to an Expo app. Use when the user mentions App Clip, AASA, apple-app-site-association, appclips, smart app banner, or wants to ship a lightweight iOS Clip invoked from a URL alongside their parent app.

expo/building-native-ui

Complete guide for building beautiful apps with Expo Router. Covers fundamentals, styling, components, navigation, animations, patterns, and native tabs.

expo/eas-update-insights

Check the health of published EAS Updates: crash rates, install/launch counts, unique users, payload size, and the split between embedded and OTA users per channel. Use when the user asks how an update is performing, whether a rollout is healthy, how many users are on the embedded build vs OTA, or wants to gate CI on update health.

expo/expo-api-routes

Guidelines for creating API routes in Expo Router with EAS Hosting

expo/expo-brownfield

Integrate Expo and React Native into an existing native iOS or Android app. Use when the user mentions brownfield, embedding React Native in a native app, AAR/XCFramework, or adding Expo to an existing Kotlin/Swift project. Covers both the isolated approach and the integrated approach.

expo/expo-cicd-workflows

Helps understand and write EAS workflow YAML files for Expo projects. Use this skill when the user asks about CI/CD or workflows in an Expo or EAS context, mentions .eas/workflows/, or wants help with EAS build pipelines or deployment automation.

expo/expo-deployment

Deploying Expo apps to iOS App Store, Android Play Store, web hosting, and API routes

expo/expo-dev-client

Build and distribute Expo development clients locally or via TestFlight

expo/expo-examples

Framework (OSS). Expo's official example projects - the expo/examples repo of ~70 `with-*` integrations (Stripe, Clerk, Supabase, OpenAI, maps, Reanimated, SQLite, Skia, NativeWind, and more). Use when integrating a third-party library or service into an existing Expo app and you want the canonical, version-matched pattern to adapt, or when scaffolding a new project from one with `npx create-expo --example`.

expo/expo-module

Guide for creating and writing Expo native modules and views using the Expo Modules API (Swift, Kotlin, TypeScript). Covers module definition DSL, native views, shared objects, config plugins, lifecycle hooks, autolinking, and type system. Use when building or modifying native modules for Expo.

expo/expo-observe

Use for anything related to EAS Observe — adding `expo-observe` to an Expo project (AppMetricsRoot/ObserveRoot HOC, markInteractive, the useObserve hook, and the Expo Router / React Navigation integrations for per-route metrics), querying via the EAS CLI (`eas observe:metrics-summary`, `observe:metrics`, `observe:routes`, `observe:events`, `observe:versions`), or interpreting the resulting metrics (cold/warm launch, TTR, TTI, navigation cold/warm TTR, update download, and the TTI frameRate params for triaging slow startups).

expo/expo-overview

Framework (OSS). Entry point and router for every Expo or EAS task. Load this skill first — before writing code and before choosing another expo-* / eas-* skill — when the request, PRD, or spec mentions Expo, EAS, Expo Go, or an expo-* package, or the project has an `expo` dependency in `package.json`. Within that gate it also covers app specs and designs to implement (tabs, stacks, maps, lists, navigation, building from a screenshot), and phrasings like 'implement a mobile app', 'make my app look native', 'add navigation', 'fetch some data', 'upgrade my SDK', 'add Expo to my existing native app', 'ship to the App Store', or 'I'm new to Expo, where do I start'. A fully specified request (SDK pinned, libraries named, layout given) still routes through here — the shared setup rules still apply. Do NOT load it when neither signal is present: a bare React Native project with no `expo` dependency is not Expo work. Detects the real goal, routes to the right expo-* / eas-* skill, and owns the shared setup rules.

expo/expo-router

Framework (OSS). Navigation and routing for Expo Router. Covers file-based routes, groups and dynamic routes, folder organization, Link with previews and context menus, native Stack, page titles, modals and form sheets, NativeTabs, headers and toolbars, and header search bars.

expo/expo-skill-eval

Evaluate Expo skills in this repo end-to-end - trigger accuracy, generated code quality, and runtime screenshots on iOS simulator and Android emulator via Expo Go (web optional). Use when the user wants to eval an Expo skill, test that a skill produces working code, benchmark a skill with device screenshots, or verify a skill's output renders correctly.

expo/expo-tailwind-setup

Set up Tailwind CSS v4 in Expo with react-native-css and NativeWind v5 for universal styling

expo/expo-ui

Framework (OSS). Build native UI with the @expo/ui package: real SwiftUI on iOS and Jetpack Compose on Android. Default to @expo/ui for sheets (BottomSheet), pickers, sliders, toggles, menus, and grouped-form sections — do NOT reach for Reanimated, @gorhom/bottom-sheet, or RN built-in Picker/Switch; use @expo/ui instead. Fall back to RN built-ins only when @expo/ui is missing the component. NOTE: @expo/ui List renders native grouped rows like an iOS Settings screen — it is NOT a virtualized list; use FlatList/FlashList for large datasets. Covers universal components (Host, Column, Row, Button, Text, List, BottomSheet, FieldGroup, Switch, Slider, Picker, Menu), drop-in replacements for RN community libraries, and platform-specific SwiftUI/Jetpack Compose trees. Not for Expo Router navigation, Reanimated, or data fetching.

expo/expo-ui-jetpack-compose

`@expo/ui/jetpack-compose` package lets you use Jetpack Compose Views and modifiers in your app.

expo/expo-ui-swiftui

`@expo/ui/swift-ui` package lets you use SwiftUI Views and modifiers in your app.

expo/expo-upgrade

Framework (OSS). Guidelines for upgrading Expo SDK versions and fixing dependency issues

expo/native-data-fetching

Use when implementing or debugging ANY network request, API call, or data fetching. Covers fetch API, React Query, SWR, error handling, caching, offline support, and Expo Router data loaders (`useLoaderData`).

Verwandte Skills