Communitygithub.com

Hébergement EAS et routes API Expo

Déployez des sites web Expo et des routes API Expo Router sur EAS Hosting – exportez le bundle web, exécutez eas deploy pour la production et les aperçus PR, gérez les secrets d'environnement et les domaines personnalisés, et travaillez dans l'environnement d'exécution Cloudflare Workers. Couvre également la création de routes API (gestionnaires +api.ts, méthodes HTTP, traitement des requêtes, CORS).

Qu'est-ce que Hébergement EAS et routes API Expo ?

Cette compétence permet aux développeurs Expo de déployer leurs applications web et leurs routes API (Expo Router) sur EAS Hosting, un service managé basé sur Cloudflare Workers. Elle couvre l'ensemble du cycle de vie : export du bundle web avec `npx expo export -p web`, déploiement en production ou en aperçu (PR) avec `eas deploy`, gestion des variables d'environnement secrètes via `eas env:create` ou le tableau de bord Expo, et configuration de domaines personnalisés. Elle explique également en détail la création de routes API avec le suffixe `+api.ts`, incluant la gestion des méthodes HTTP (GET, POST, PUT, DELETE), des paramètres d'URL, des en-têtes, du corps JSON, du CORS et de l'authentification. La compétence précise quand utiliser ou non des routes API, insiste sur la sécurité (ne jamais exposer les clés secrètes côté client) et décrit les limitations de l'environnement d'exécution Cloudflare Workers (pas de système de fichiers, temps d'exécution limité, pas de modules Node.js natifs). Enfin, elle propose des exemples concrets comme l'intégration de bases de données (Turso, D1), la création de middleware d'authentification et le proxy d'APIs externes.

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

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Hébergement EAS

Service EAS – des frais peuvent s'appliquer. EAS Hosting est un produit payant d'Expo Application Services avec des limites de niveau gratuit ; les déploiements de production utilisent les quotas de requêtes et de bande passante de votre forfait. Voir https://expo.dev/pricing. La création de routes API et l'export du bundle web sont gratuits et open source, et vous pouvez auto-héberger la sortie du serveur exporté au lieu d'utiliser EAS Hosting.

EAS Hosting déploie votre application web Expo et vos routes API sur le edge managé d'Expo (Cloudflare Workers). Exportez le bundle web avec npx expo export -p web et expédiez-le avec eas deploy – la même commande déploie toutes les routes API Expo Router regroupées avec lui. Cette compétence couvre le déploiement d'un site web, la création de routes API et l'environnement d'exécution d'hébergement ; voir la section Déploiement ci-dessous pour le workflow de déploiement.

Quand utiliser les routes API

Utilisez les routes API lorsque vous avez besoin de :

  • Secrets côté serveur — Clés API, identifiants de base de données ou jetons qui ne doivent jamais atteindre le client
  • Opérations de base de données — Requêtes directes à la base de données qui ne doivent pas être exposées
  • Proxys d'API tierces — Masquer les clés API lors d'appels à des services externes (OpenAI, Stripe, etc.)
  • Validation côté serveur — Valider les données avant les écritures en base
  • Points de terminaison Webhook — Recevoir des rappels de services comme Stripe ou GitHub
  • Limitation de débit — Contrôler l'accès au niveau serveur
  • Calculs lourds — Externaliser le traitement qui serait lent sur mobile

Quand NE PAS utiliser les routes API

Évitez les routes API lorsque :

  • Les données sont déjà publiques — Utilisez plutôt un fetch direct vers les API publiques
  • Aucun secret requis — Données statiques ou opérations sûres côté client
  • Mises à jour en temps réel nécessaires — Utilisez des WebSockets ou des services comme Supabase Realtime
  • CRUD simple — Envisagez Firebase, Supabase ou Convex pour les backends gérés
  • Téléchargements de fichiers — Utilisez des téléchargements directs vers le stockage (URLs présignées S3, Cloudflare R2)
  • Authentification uniquement — Utilisez Clerk, Auth0 ou Firebase Auth à la place

Structure des fichiers

Les routes API se trouvent dans le répertoire app avec le suffixe +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

Route API de base

// app/api/hello+api.ts
export function GET(request: Request) {
  return Response.json({ message: "Bonjour depuis Expo !" });
}

Méthodes HTTP

Exportez des fonctions nommées pour chaque méthode HTTP :

// 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 });
}

Routes dynamiques

// app/api/users/[id]+api.ts
export function GET(request: Request, { id }: { id: string }) {
  return Response.json({ userId: id });
}

Gestion des requêtes

Paramètres de requête

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 });
}

En-têtes

export function GET(request: Request) {
  const auth = request.headers.get("Authorization");

  if (!auth) {
    return Response.json({ error: "Non autorisé" }, { status: 401 });
  }

  return Response.json({ authenticated: true });
}

Corps JSON

export async function POST(request: Request) {
  const { email, password } = await request.json();

  if (!email || !password) {
    return Response.json({ error: "Champs manquants" }, { status: 400 });
  }

  return Response.json({ success: true });
}

Variables d'environnement

Utilisez process.env pour les secrets côté serveur :

// 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);
}

Définissez les variables d'environnement :

  • Local : Créez un fichier .env (ne jamais le versionner)
  • EAS Hosting : Utilisez eas env:create ou le tableau de bord Expo

En-têtes CORS

Ajoutez CORS pour les clients web :

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: "valeur" }, { headers: corsHeaders });
}

Gestion des erreurs

export async function POST(request: Request) {
  try {
    const body = await request.json();
    // Traitement…
    return Response.json({ success: true });
  } catch (error) {
    console.error("Erreur API :", error);
    return Response.json({ error: "Erreur interne du serveur" }, { status: 500 });
  }
}

Tests en local

Démarrez le serveur de développement avec les routes API :

npx expo serve

Ceci démarre un serveur local sur http://localhost:8081 avec la prise en charge complète des routes API.

Testez avec curl :

curl http://localhost:8081/api/hello
curl -X POST http://localhost:8081/api/users -H "Content-Type: application/json" -d '{"name":"Test"}'

Déploiement vers EAS Hosting

Prérequis

npm install -g eas-cli
eas login

Déploiement

Le déploiement expédie votre bundle web et toutes les routes API Expo Router ensemble – eas deploy gère les deux. L'export s'exécute que vous ayez un site web complet, un backend composé uniquement de routes API, ou les deux.

# Export du bundle web (inclut toutes les routes API)
npx expo export -p web

# Déploiement d'un aperçu (URL de type PR)
npx eas-cli@latest deploy

# Déploiement en production
npx eas-cli@latest deploy --prod

Tout arrive sur EAS Hosting (Cloudflare Workers).

Variables d'environnement pour la production

# Créer un secret
eas env:create --name OPENAI_API_KEY --value sk-xxx --environment production

# Ou utiliser le tableau de bord Expo

Domaine personnalisé

Configurez dans eas.json ou le tableau de bord Expo.

Automatisation avec les workflows EAS

Déployez le site web (et les routes API) à chaque push sur main avec un workflow de type deploy :

.eas/workflows/deploy.yml

name: Deploy

on:
  push:
    branches:
      - main

# https://docs.expo.dev/eas/workflows/syntax/#deploy
jobs:
  deploy_web:
    type: deploy
    params:
      prod: true

Les déploiements d'aperçu pour les pull requests utilisent le même type de job avec prod: false :

name: Web PR Preview

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  preview:
    type: deploy
    params:
      prod: false

Pour créer ou valider du YAML de workflow au-delà de ces exemples, utilisez la compétence eas-workflows.

Environnement d'exécution EAS Hosting (Cloudflare Workers)

Les routes API s'exécutent sur Cloudflare Workers. Limitations clés :

API manquantes ou limitées

  • Pas de système de fichiers Node.js — module fs indisponible
  • Pas de modules Node natifs — Utilisez les API Web ou des polyfills
  • Temps d'exécution limité — 30 secondes de timeout pour les tâches intensives en CPU
  • Pas de connexions persistantes — Les WebSockets nécessitent des Durable Objects
  • fetch est disponible — Utilisez le fetch standard pour les requêtes HTTP

Utilisez plutôt les API Web

// Utilisez Web Crypto au lieu de Node crypto
const hash = await crypto.subtle.digest(
  "SHA-256",
  new TextEncoder().encode("données")
);

// Utilisez fetch au lieu de node-fetch
const response = await fetch("https://api.example.com");

// Utilisez Response/Request (déjà disponible)
return new Response(JSON.stringify(data), {
  headers: { "Content-Type": "application/json" },
});

Options de base de données

Puisque le système de fichiers n'est pas disponible, utilisez des bases de données cloud :

  • Cloudflare D1 — SQLite à la périphérie
  • Turso — SQLite distribué
  • PlanetScale — MySQL serverless
  • Supabase — Postgres avec API REST
  • Neon — Postgres serverless

Exemple avec 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);
}

Appel des routes API depuis le client

// Depuis les composants React Native
const response = await fetch("/api/hello");
const data = await response.json();

// Avec corps de requête
const response = await fetch("/api/users", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "Jean" }),
});

Patterns courants

Middleware d'authentification

// 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: "Non autorisé" }), {
      status: 401,
      headers: { "Content-Type": "application/json" },
    });
  }

  // Vérifier le jeton…
  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 });
}

Proxy d'API externe

// 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());
}

Règles

  • N'exposez JAMAIS les clés API ou secrets dans le code client
  • VALIDEZ et assainissez TOUJOURS les entrées utilisateur
  • Utilisez des codes de statut HTTP appropriés (200, 201, 400, 401, 404, 500)
  • Gérez les erreurs avec grâce à l'aide de try/catch
  • Gardez les routes API ciblées — une responsabilité par point de terminaison
  • Utilisez TypeScript pour la sécurité des types
  • Enregistrez les erreurs côté serveur pour le débogage

Soumettre un retour

Si vous rencontrez des erreurs, des informations trompeuses ou obsolètes dans cette compétence, signalez-le pour qu'Expo puisse s'améliorer :

npx --yes @expo/agent-cli@latest feedback --category skills --subject "eas-hosting" "<retour actionnable>"

Soumettez uniquement lorsque vous avez quelque chose de spécifique et d'actionnable à signaler. Incluez autant de contexte pertinent que possible. Si un agent échoue de manière répétée à une tâche Expo ou si l'utilisateur doit intervenir, soumettez un bref rapport de la tâche, du résultat attendu et de ce qui a mal fonctionné en utilisant --category evals et le paquet ou la commande Expo concernée comme --subject.

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`).

Skills associés