encore-api

Par encoredev · skills

Créez des endpoints d'API type-safe avec Encore.ts.

npx skills add https://github.com/encoredev/skills --skill encore-api

Points de terminaison API Encore

Instructions

Lors de la création de points de terminaison API avec Encore.ts, suivez ces modèles :

1. Importer le module API

import { api } from "encore.dev/api";

2. Définir les interfaces de requête/réponse typées

Définissez toujours des interfaces TypeScript explicites pour les types de requête et de réponse :

interface CreateUserRequest {
  email: string;
  name: string;
}

interface CreateUserResponse {
  id: string;
  email: string;
  name: string;
}

3. Créer le point de terminaison

export const createUser = api(
  { method: "POST", path: "/users", expose: true },
  async (req: CreateUserRequest): Promise<CreateUserResponse> => {
    // Implementation
  }
);

Options API

Option Type Description
method string Méthode HTTP : GET, POST, PUT, PATCH, DELETE
path string Chemin URL, supporte :param et *wildcard
expose boolean Si true, accessible de l'extérieur (défaut : false)
auth boolean Si true, nécessite une authentification
sensitive boolean Si true, masque les payloads de requête/réponse des traces

Modèles de requête/réponse

Encore supporte quatre configurations de points de terminaison :

// Requête et réponse
export const createUser = api(
  { method: "POST", path: "/users", expose: true },
  async (req: CreateRequest): Promise<CreateResponse> => { ... }
);

// Réponse uniquement (pas de corps de requête)
export const listUsers = api(
  { method: "GET", path: "/users", expose: true },
  async (): Promise<ListResponse> => { ... }
);

// Requête uniquement (pas de corps de réponse)
export const deleteUser = api(
  { method: "DELETE", path: "/users/:id", expose: true },
  async (req: DeleteRequest): Promise<void> => { ... }
);

// Ni requête ni réponse
export const ping = api(
  { method: "GET", path: "/ping", expose: true },
  async (): Promise<void> => { ... }
);

Codes de statut HTTP personnalisés

Incluez un champ HttpStatus dans votre réponse pour retourner des codes de statut personnalisés :

import { api, HttpStatus } from "encore.dev/api";

interface CreateResponse {
  id: string;
  status: HttpStatus;
}

export const create = api(
  { method: "POST", path: "/items", expose: true },
  async (req: CreateRequest): Promise<CreateResponse> => {
    const item = await createItem(req);
    return { id: item.id, status: HttpStatus.Created };  // Retourne 201
  }
);

Types de paramètres

Paramètres de chemin

// Path: "/users/:id"
interface GetUserRequest {
  id: string;  // Extrait automatiquement de :id
}

Paramètres de requête

import { Query } from "encore.dev/api";

interface ListUsersRequest {
  limit?: Query<number>;
  offset?: Query<number>;
}

En-têtes

import { Header } from "encore.dev/api";

interface WebhookRequest {
  signature: Header<"X-Webhook-Signature">;
  payload: string;
}

Cookies

import { Cookie } from "encore.dev/api";

interface SessionRequest {
  session?: Cookie<"session">;
  settings?: Cookie<"user-settings">;
}

Validation de requête

Encore valide les requêtes à l'exécution en utilisant les types TypeScript. Ajoutez des contraintes pour une validation plus stricte :

import { api } from "encore.dev/api";
import { Min, Max, MinLen, MaxLen, IsEmail, IsURL } from "encore.dev/validate";

interface CreateUserRequest {
  email: string & IsEmail;                    // Doit être une adresse e-mail valide
  username: string & MinLen<3> & MaxLen<20>;  // 3-20 caractères
  age: number & Min<13> & Max<120>;           // Entre 13 et 120
  website?: string & IsURL;                   // Optionnel, doit être une URL si fourni
}

Combinaison de règles de validation

Utilisez & pour la logique ET (doit passer toutes les règles) et | pour la logique OU (doit passer au moins une) :

import { IsEmail, IsURL, MinLen, MaxLen } from "encore.dev/validate";

interface ContactRequest {
  // Doit être une adresse e-mail valide OU une URL valide
  contact: string & (IsEmail | IsURL);
  // Doit faire 5-100 caractères ET être une URL valide
  website: string & MinLen<5> & MaxLen<100> & IsURL;
}

Validateurs disponibles

Validateur S'applique à Exemple
Min<N> number age: number & Min<18>
Max<N> number count: number & Max<100>
MinLen<N> string, array name: string & MinLen<1>
MaxLen<N> string, array tags: string[] & MaxLen<10>
IsEmail string email: string & IsEmail
IsURL string link: string & IsURL
StartsWith<S> string id: string & StartsWith<"usr_">
EndsWith<S> string file: string & EndsWith<".json">
MatchesRegexp<R> string code: string & MatchesRegexp<"^[A-Z]{3}$">

Réponse d'erreur de validation

Les requêtes invalides retournent 400 avec les détails :

{
  "code": "invalid_argument",
  "message": "validation failed",
  "details": { "field": "email", "error": "must be a valid email" }
}

Points de terminaison bruts

Utilisez api.raw pour les webhooks ou quand vous avez besoin d'un accès direct à la requête/réponse :

export const stripeWebhook = api.raw(
  { expose: true, path: "/webhooks/stripe", method: "POST" },
  async (req, res) => {
    const sig = req.headers["stripe-signature"];
    // Gérer la requête brute...
    res.writeHead(200);
    res.end();
  }
);

Gestion des erreurs

Utilisez APIError pour les réponses d'erreur HTTP appropriées :

import { APIError, ErrCode } from "encore.dev/api";

// Lever avec code d'erreur
throw new APIError(ErrCode.NotFound, "user not found");

// Ou utiliser la forme abrégée
throw APIError.notFound("user not found");
throw APIError.invalidArgument("email is required");
throw APIError.unauthenticated("invalid token");

Codes d'erreur courants

Code Statut HTTP Utilisation
NotFound 404 La ressource n'existe pas
InvalidArgument 400 Mauvaise entrée
Unauthenticated 401 Authentification manquante/invalide
PermissionDenied 403 Non autorisé
AlreadyExists 409 Ressource en doublon

Actifs statiques

Servez des fichiers statiques (HTML, CSS, JS, images) avec api.static :

import { api } from "encore.dev/api";

// Servir les fichiers de ./assets sous /static/*
export const assets = api.static(
  { expose: true, path: "/static/*path", dir: "./assets" }
);

// Servir à la racine (utiliser !path pour le routage de secours)
export const frontend = api.static(
  { expose: true, path: "/!path", dir: "./dist" }
);

// Page 404 personnalisée
export const app = api.static(
  { expose: true, path: "/!path", dir: "./public", notFound: "./404.html" }
);

Syntaxe du chemin

  • *path - Wildcard standard : correspond à tous les chemins sous le préfixe (par ex., /static/*path)
  • !path - Routage de secours : sert les fichiers statiques à la racine du domaine sans confliter avec les autres points de terminaison API. Utilisez ceci pour les SPA où les routes non trouvées doivent servir index.html

Directives

  • Utilisez toujours import et non require
  • Définissez des interfaces explicites pour la sécurité des types
  • Utilisez expose: true uniquement pour les points de terminaison publics
  • Utilisez api.raw pour les webhooks, api pour tout le reste
  • Levez APIError au lieu de retourner des objets d'erreur
  • Les paramètres de chemin sont automatiquement extraits du motif de chemin
  • Utilisez des contraintes de validation (Min, MaxLen, etc.) pour les entrées utilisateur

Skills similaires