tRPC
tRPC est un framework pour créer des API en TypeScript avec une sécurité de type de bout en bout. Avec tRPC, les mises à jour des entrées et sorties des opérations d’API sont immédiatement reflétées dans le code client et sont visibles dans votre IDE sans avoir besoin de reconstruire votre projet.
Le générateur d’API tRPC crée une nouvelle API tRPC avec une configuration d’infrastructure AWS CDK ou Terraform. Le backend généré utilise AWS Lambda pour un déploiement serverless, exposé via une API AWS API Gateway, et inclut la validation de schéma avec Zod. Il configure AWS Lambda Powertools pour l’observabilité, incluant la journalisation, le traçage AWS X-Ray et les métriques Cloudwatch.
Utilisation
Section intitulée « Utilisation »Générer une API tRPC
Section intitulée « Générer une API tRPC »Vous pouvez générer une nouvelle API tRPC de deux manières :
pnpm nx g @aws/nx-plugin:ts#api --framework=trpcyarn nx g @aws/nx-plugin:ts#api --framework=trpcnpx nx g @aws/nx-plugin:ts#api --framework=trpcbunx nx g @aws/nx-plugin:ts#api --framework=trpcVous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
pnpm nx g @aws/nx-plugin:ts#api --framework=trpc --dry-runyarn nx g @aws/nx-plugin:ts#api --framework=trpc --dry-runnpx nx g @aws/nx-plugin:ts#api --framework=trpc --dry-runbunx nx g @aws/nx-plugin:ts#api --framework=trpc --dry-run- Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
- Ouvrez la console Nx dans VSCode
- Cliquez sur
Generate (UI)dans la section "Common Nx Commands" - Recherchez
@aws/nx-plugin - ts#api - Remplissez les paramètres requis
- framework: trpc
- Cliquez sur
Generate
| Paramètre | Type | Par défaut | Description |
|---|---|---|---|
| name Requis | string | - | Le nom de l'API (requis). Utilisé pour générer les noms de classes et les chemins de fichiers. |
| framework | trpc | smithy | trpc | Le framework d'API à utiliser. |
| namespace | string | - | L'espace de noms pour l'API Smithy (applicable uniquement pour le framework smithy). Par défaut, correspond à la portée de votre monorepo |
| integrationPattern | isolated | shared | isolated | Comment les intégrations API Gateway sont générées pour l'API. Choisissez entre isolated (par défaut) et shared. |
| auth | iam | cognito | custom | iam | La méthode utilisée pour s'authentifier auprès de votre API. Choisissez entre iam (par défaut), cognito ou custom. |
| directory | string | packages | Le répertoire dans lequel stocker l'application. |
| subDirectory | string | - | Le sous-répertoire dans lequel le projet est placé. Par défaut, il s'agit du nom du projet. |
| iac | inherit | cdk | terraform | inherit | Le fournisseur IaC préféré. Par défaut, celui-ci est hérité de votre sélection initiale. |
| infra | rest-lambda | http-lambda | none | rest-lambda | Le type d'infrastructure à utiliser pour déployer cette API. |
| preferInstallDependencies | boolean | true | Indique s'il faut privilégier l'installation des dépendances après l'exécution du générateur. Définir à false pour différer l'installation lors de l'exécution de plusieurs générateurs en lot (une installation s'exécute quand même si nécessaire pour que les générateurs suivants puissent calculer le graphe de projet Nx) ; installer une seule fois à la fin. |
Sortie du générateur
Section intitulée « Sortie du générateur »Le générateur créera la structure de projet suivante dans le répertoire <directory>/<api-name> :
Répertoiresrc
- init.ts Backend tRPC initialisation
- handler.ts Lambda handler entrypoint
- router.ts tRPC router definition
Répertoireschema Schema definitions using Zod
- echo.ts Example definitions for the input and output of the “echo” procedure
- z-async-iterable.ts Zod helper for subscriptions (REST API only)
Répertoireprocedures Procedures (or operations) exposed by your API
- echo.ts Example procedure
Répertoiremiddleware
- error.ts Middleware for error handling
- logger.ts middleware for configuring AWS Powertools for Lambda logging
- tracer.ts middleware for configuring AWS Powertools for Lambda tracing
- metrics.ts middleware for configuring AWS Powertools for Lambda metrics
- local-server.ts tRPC standalone adapter entrypoint for local development server
Répertoireclient
- index.ts Type-safe client for machine-to-machine API calls
- tsconfig.json TypeScript configuration
- package.json Project manifest defining the project’s package name and dependencies
- project.json Project configuration and build targets
Infrastructure
Section intitulée « Infrastructure »Étant donné que ce générateur fournit de l’infrastructure en tant que code basée sur votre iac choisi, il créera un projet dans packages/common qui inclut les constructs CDK ou modules Terraform pertinents.
Le projet d’infrastructure en tant que code commun est structuré comme suit :
Répertoirepackages/common/constructs
Répertoiresrc
Répertoireapp/ Constructs pour l’infrastructure spécifique à un projet/générateur
- …
Répertoirecore/ Constructs génériques qui sont réutilisés par les constructs dans
app- …
- index.ts Entry point exporting constructs from
app
- project.json Project build targets and configuration
Répertoirepackages/common/terraform
Répertoiresrc
Répertoireapp/ Terraform modules for infrastructure specific to a project/generator
- …
Répertoirecore/ Generic modules which are reused by modules in
app- …
- project.json Project build targets and configuration
Pour déployer votre API, les fichiers suivants sont générés :
Répertoirepackages/common/constructs/src
Répertoireapp
Répertoireapis
- <project-name>.ts CDK construct for deploying your API
Répertoirecore
Répertoireapi
- http-api.ts CDK construct for deploying an HTTP API (if you selected to deploy an HTTP API)
- rest-api.ts CDK construct for deploying a REST API (if you selected to deploy a REST API)
- utils.ts Utilities for the API constructs
Répertoirepackages/common/terraform/src
Répertoireapp
Répertoireapis
Répertoire<project-name>
- <project-name>.tf Module for deploying your API
Répertoirecore
Répertoireapi
Répertoirehttp-api
- http-api.tf Module for deploying an HTTP API (if you selected to deploy an HTTP API)
Répertoirerest-api
- rest-api.tf Module for deploying a REST API (if you selected to deploy a REST API)
Architecture
Section intitulée « Architecture »L’application déployée a l’architecture suivante :
Les REST APIs incluent une Web ACL AWS WAFv2 devant l’étape API Gateway avec l’ensemble de règles par défaut géré par AWS activé.
Les HTTP APIs ne prennent pas en charge WAF directement — si vous avez besoin de la protection WAF, choisissez REST API à la place ou placez l’HTTP API derrière une distribution CloudFront.
Implémenter votre API tRPC
Section intitulée « Implémenter votre API tRPC »À un niveau élevé, les API tRPC consistent en un routeur qui délègue les requêtes à des procédures spécifiques. Chaque procédure a une entrée et une sortie, définies comme un schéma Zod.
Le répertoire src/schema contient les types qui sont partagés entre votre code client et serveur. Dans ce package, ces types sont définis en utilisant Zod, une bibliothèque de déclaration et de validation de schéma TypeScript-first.
Un exemple de schéma pourrait ressembler à ceci :
import { z } from 'zod';
// Schema definitionexport const UserSchema = z.object({ name: z.string(), height: z.number(), dateOfBirth: z.string().datetime(),});
// Corresponding TypeScript typeexport type User = z.TypeOf<typeof UserSchema>;Étant donné le schéma ci-dessus, le type User est équivalent au TypeScript suivant :
interface User { name: string; height: number; dateOfBirth: string;}Les schémas sont partagés par le code serveur et client, fournissant un seul endroit à mettre à jour lors de modifications des structures utilisées dans votre API.
Les schémas sont automatiquement validés par votre API tRPC au moment de l’exécution, ce qui évite de créer manuellement une logique de validation personnalisée dans votre backend.
Zod fournit des utilitaires puissants pour combiner ou dériver des schémas tels que .merge, .pick, .omit et plus encore. Vous pouvez trouver plus d’informations sur le site de documentation Zod.
Routeur et procédures
Section intitulée « Routeur et procédures »Votre routeur tRPC est défini dans src/router.ts, qui enregistre toutes les procédures. Chaque procédure définit l’entrée, la sortie et l’implémentation attendues. Le point d’entrée du gestionnaire Lambda se trouve dans src/handler.ts, qui transmet les requêtes à votre routeur.
Le routeur d’exemple généré pour vous a une seule opération, appelée echo :
import { echo } from './procedures/echo.js';
export const appRouter = router({ echo,});L’exemple de procédure echo est généré pour vous dans src/procedures/echo.ts :
export const echo = publicProcedure .input(EchoInputSchema) .output(EchoOutputSchema) .query((opts) => ({ message: opts.input.message }));Pour décomposer ce qui précède :
publicProceduredéfinit une méthode publique sur l’API, incluant le middleware configuré danssrc/middleware. Ce middleware inclut l’intégration AWS Lambda Powertools pour la journalisation, le traçage et les métriques.inputaccepte un schéma Zod qui définit l’entrée attendue pour l’opération. Les requêtes envoyées pour cette opération sont automatiquement validées par rapport à ce schéma.outputaccepte un schéma Zod qui définit la sortie attendue pour l’opération. Vous verrez des erreurs de type dans votre implémentation si vous ne retournez pas une sortie conforme au schéma.queryaccepte une fonction qui définit l’implémentation de votre API. Cette implémentation reçoitopts, qui contient l’inputpassé à votre opération, ainsi que d’autres contextes configurés par le middleware, disponibles dansopts.ctx. La fonction passée àquerydoit retourner une sortie conforme au schémaoutput.
L’utilisation de query pour définir l’implémentation indique que l’opération n’est pas mutative. Utilisez ceci pour définir des méthodes de récupération de données. Pour implémenter une opération mutative, utilisez plutôt la méthode mutation.
Si vous ajoutez une nouvelle procédure, assurez-vous de l’enregistrer en l’ajoutant au routeur dans src/router.ts.
Abonnements (Streaming)
Section intitulée « Abonnements (Streaming) »Les abonnements tRPC vous permettent de diffuser des données du serveur au client en utilisant Server-Sent Events (SSE). Lorsque vous sélectionnez rest-lambda comme type de calcul, le générateur configure automatiquement l’infrastructure requise pour le streaming, ainsi qu’un gestionnaire Lambda de streaming et l’assistant de schéma ZodAsyncIterable.
Pour définir une procédure d’abonnement, utilisez la méthode .subscription avec une fonction génératrice asynchrone. Utilisez l’assistant ZodAsyncIterable de src/schema/z-async-iterable.ts pour définir le schéma de sortie :
import { publicProcedure } from '../init.js';import { z } from 'zod';import { ZodAsyncIterable } from '../schema/z-async-iterable.js';
const InputSchema = z.object({ query: z.string() });const ChunkSchema = z.object({ text: z.string() });
export const myStream = publicProcedure .input(InputSchema) .output( ZodAsyncIterable({ yield: ChunkSchema, }), ) .subscription(async function* (opts) { // Yield data to the client as it becomes available for (const chunk of await getResults(opts.input.query)) { yield { text: chunk }; } });Enregistrez l’abonnement dans votre routeur comme n’importe quelle autre procédure :
export const appRouter = router({ echo, myStream,});L’infrastructure générée utilise un gestionnaire Lambda de streaming avec ResponseTransferMode.STREAM dans API Gateway pour toutes les opérations d’API REST, ce qui permet aux abonnements de fonctionner aux côtés des requêtes et mutations régulières.
Personnaliser votre API tRPC
Section intitulée « Personnaliser votre API tRPC »Dans votre implémentation, vous pouvez retourner des réponses d’erreur aux clients en lançant une TRPCError. Celles-ci acceptent un code qui indique le type d’erreur, par exemple :
throw new TRPCError({ code: 'NOT_FOUND', message: 'The requested resource could not be found',});Organiser vos opérations
Section intitulée « Organiser vos opérations »Au fur et à mesure que votre API grandit, vous souhaiterez peut-être regrouper les opérations connexes.
Vous pouvez regrouper les opérations en utilisant des routeurs imbriqués, par exemple :
import { getUser } from './procedures/users/get.js';import { listUsers } from './procedures/users/list.js';
const appRouter = router({ users: router({ get: getUser, list: listUsers, }), ...})Les clients reçoivent alors ce regroupement d’opérations, par exemple l’invocation de l’opération listUsers dans ce cas pourrait ressembler à ceci :
client.users.list.query();Journalisation
Section intitulée « Journalisation »Le logger AWS Lambda Powertools est configuré dans src/middleware/logger.ts, et peut être accédé dans une implémentation d’API via opts.ctx.logger. Vous pouvez l’utiliser pour journaliser dans CloudWatch Logs, et/ou contrôler des valeurs supplémentaires à inclure dans chaque message de journal structuré. Par exemple :
export const echo = publicProcedure .input(...) .output(...) .query(async (opts) => { opts.ctx.logger.info('Operation called with input', opts.input);
return ...; });Pour plus d’informations sur le logger, veuillez vous référer à la documentation AWS Lambda Powertools Logger.
Enregistrer des métriques
Section intitulée « Enregistrer des métriques »Les métriques AWS Lambda Powertools sont configurées dans src/middleware/metrics.ts, et peuvent être accédées dans une implémentation d’API via opts.ctx.metrics. Vous pouvez l’utiliser pour enregistrer des métriques dans CloudWatch sans avoir besoin d’importer et d’utiliser le SDK AWS, par exemple :
export const echo = publicProcedure .input(...) .output(...) .query(async (opts) => { opts.ctx.metrics.addMetric('Invocations', 'Count', 1);
return ...; });Pour plus d’informations, veuillez vous référer à la documentation AWS Lambda Powertools Metrics.
Affiner le traçage X-Ray
Section intitulée « Affiner le traçage X-Ray »Le traceur AWS Lambda Powertools est configuré dans src/middleware/tracer.ts, et peut être accédé dans une implémentation d’API via opts.ctx.tracer. Vous pouvez l’utiliser pour ajouter des traces avec AWS X-Ray afin de fournir des informations détaillées sur les performances et le flux des requêtes API. Par exemple :
export const echo = publicProcedure .input(...) .output(...) .query(async (opts) => { const subSegment = opts.ctx.tracer.getSegment()!.addNewSubsegment('MyAlgorithm'); // ... my algorithm logic to capture subSegment.close();
return ...; });Pour plus d’informations, veuillez vous référer à la documentation AWS Lambda Powertools Tracer.
Implémenter un middleware personnalisé
Section intitulée « Implémenter un middleware personnalisé »Vous pouvez ajouter des valeurs supplémentaires au contexte fourni aux procédures en implémentant un middleware.
À titre d’exemple, implémentons un middleware pour extraire des détails sur l’utilisateur appelant notre API dans src/middleware/identity.ts.
Cet exemple présente un middleware d’identité pour l’authentification IAM. Nous recherchons l’appelant dans Cognito en utilisant le sub extrait de l’événement API Gateway.
Tout d’abord, nous définissons ce que nous ajouterons au contexte :
export interface IIdentityContext { identity?: { sub: string; username: string; };}Notez que nous définissons une propriété supplémentaire optionnelle au contexte. tRPC gère la garantie que cela est défini dans les procédures qui ont correctement configuré ce middleware.
Ensuite, nous implémenterons le middleware lui-même. Il a la structure suivante :
export const createIdentityPlugin = () => { const t = initTRPC.context<...>().create(); return t.procedure.use(async (opts) => { // Add logic here to run before the procedure
const response = await opts.next(...);
// Add logic here to run after the procedure
return response; });};Dans notre cas, nous voulons extraire des détails sur l’utilisateur Cognito appelant. Nous le ferons en extrayant l’ID de sujet de l’utilisateur (ou “sub”) de l’événement API Gateway, et en récupérant les détails de l’utilisateur depuis Cognito. L’implémentation varie selon que l’événement a été fourni à notre fonction par une API REST ou une API HTTP :
import { CognitoIdentityProvider } from '@aws-sdk/client-cognito-identity-provider';import { initTRPC, TRPCError } from '@trpc/server';import { CreateAWSLambdaContextOptions } from '@trpc/server/adapters/aws-lambda';import { APIGatewayProxyEvent } from 'aws-lambda';
export interface IIdentityContext { identity?: { sub: string; username: string; };}
export const createIdentityPlugin = () => { const t = initTRPC.context<IIdentityContext & CreateAWSLambdaContextOptions<APIGatewayProxyEvent>>().create();
const cognito = new CognitoIdentityProvider();
return t.procedure.use(async (opts) => { const cognitoAuthenticationProvider = opts.ctx.event.requestContext?.identity?.cognitoAuthenticationProvider;
let sub: string | undefined = undefined; if (cognitoAuthenticationProvider) { const providerParts = cognitoAuthenticationProvider.split(':'); sub = providerParts[providerParts.length - 1]; }
if (!sub) { throw new TRPCError({ code: 'FORBIDDEN', message: `Unable to determine calling user`, }); }
const { Users } = await cognito.listUsers({ // Assumes user pool id is configured in lambda environment UserPoolId: process.env.USER_POOL_ID!, Limit: 1, Filter: `sub="${sub}"`, });
if (!Users || Users.length !== 1) { throw new TRPCError({ code: 'FORBIDDEN', message: `No user found with subjectId ${sub}`, }); }
// Provide the identity to other procedures in the context return await opts.next({ ctx: { ...opts.ctx, identity: { sub, username: Users[0].Username!, }, }, }); });};import { CognitoIdentityProvider } from '@aws-sdk/client-cognito-identity-provider';import { initTRPC, TRPCError } from '@trpc/server';import { CreateAWSLambdaContextOptions } from '@trpc/server/adapters/aws-lambda';import { APIGatewayProxyEventV2WithIAMAuthorizer } from 'aws-lambda';
export interface IIdentityContext { identity?: { sub: string; username: string; };}
export const createIdentityPlugin = () => { const t = initTRPC.context<IIdentityContext & CreateAWSLambdaContextOptions<APIGatewayProxyEventV2WithIAMAuthorizer>>().create();
const cognito = new CognitoIdentityProvider();
return t.procedure.use(async (opts) => { const cognitoIdentity = opts.ctx.event.requestContext?.authorizer?.iam ?.cognitoIdentity as unknown as | { amr: string[]; } | undefined;
const sub = (cognitoIdentity?.amr ?? []) .flatMap((s) => (s.includes(':CognitoSignIn:') ? [s] : [])) .map((s) => { const parts = s.split(':'); return parts[parts.length - 1]; })?.[0];
if (!sub) { throw new TRPCError({ code: 'FORBIDDEN', message: `Unable to determine calling user`, }); }
const { Users } = await cognito.listUsers({ // Assumes user pool id is configured in lambda environment UserPoolId: process.env.USER_POOL_ID!, Limit: 1, Filter: `sub="${sub}"`, });
if (!Users || Users.length !== 1) { throw new TRPCError({ code: 'FORBIDDEN', message: `No user found with subjectId ${sub}`, }); }
// Provide the identity to other procedures in the context return await opts.next({ ctx: { ...opts.ctx, identity: { sub, username: Users[0].Username!, }, }, }); });};Lorsque vous déployez avec auth: 'cognito', l’autorisateur Cognito User Pools d’API Gateway vérifie le JWT que l’appelant fournit dans l’en-tête Authorization et place les revendications vérifiées sur l’événement Lambda à event.requestContext.authorizer.claims. Notre middleware lit simplement ces revendications — pas d’appels SDK AWS supplémentaires, pas de vérification JWT manuelle.
Tout d’abord, nous définissons ce que nous ajouterons au contexte :
export interface IIdentityContext { identity?: { sub: string; username: string; };}Notez que nous définissons une propriété supplémentaire optionnelle sur le contexte. tRPC gère la garantie que cela est défini dans les procédures qui ont correctement configuré ce middleware.
Ensuite, le middleware lui-même :
import { initTRPC, TRPCError } from '@trpc/server';import { CreateAWSLambdaContextOptions } from '@trpc/server/adapters/aws-lambda';import { APIGatewayProxyEvent } from 'aws-lambda';
export interface IIdentityContext { identity?: { sub: string; username: string; };}
export const createIdentityPlugin = () => { const t = initTRPC .context<IIdentityContext & CreateAWSLambdaContextOptions<APIGatewayProxyEvent>>() .create();
return t.procedure.use(async (opts) => { const claims = opts.ctx.event.requestContext?.authorizer?.claims as | Record<string, string> | undefined;
const sub = claims?.sub; const username = claims?.username;
if (!sub || !username) { throw new TRPCError({ code: 'FORBIDDEN', message: 'Unable to determine calling user', }); }
return await opts.next({ ctx: { ...opts.ctx, identity: { sub, username, }, }, }); });};Vous pouvez ensuite mélanger le plugin dans n’importe quelle procédure qui a besoin de l’identité de l’appelant :
import { publicProcedure } from '../init.js';import { createIdentityPlugin } from '../middleware/identity.js';import { z } from 'zod';
export const me = publicProcedure .concat(createIdentityPlugin()) .output(z.object({ sub: z.string(), username: z.string() })) .query(({ ctx }) => ({ sub: ctx.identity!.sub, username: ctx.identity!.username, }));Déployer votre API tRPC
Section intitulée « Déployer votre API tRPC »Le générateur d’API tRPC crée une infrastructure en tant que code CDK ou Terraform en fonction de votre iac sélectionné. Vous pouvez l’utiliser pour déployer votre API tRPC.
Le construct CDK pour déployer votre API se trouve dans le dossier common/constructs. Vous pouvez le consommer dans une application CDK, par exemple :
import { MyApi } from '@my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { // Add the api to your stack const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(), }); }}import { MyApi, UserIdentity } from '@my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { // Add the api to your stack const identity = new UserIdentity(this, 'Identity');
const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(), identity, }); }}Le construct UserIdentity peut être généré en utilisant le générateur ts#website#auth.
Cela configure votre infrastructure d’API, incluant une API AWS API Gateway REST ou HTTP, des fonctions AWS Lambda pour la logique métier, et l’authentification basée sur votre méthode auth choisie.
Les modules Terraform pour déployer votre API sont dans le dossier common/terraform. Vous pouvez l’utiliser dans une configuration Terraform.
Le module API stocke son zip de déploiement Lambda dans un bucket S3 d’actifs partagé — voir le guide d’infrastructure Terraform pour plus de détails. Instanciez le module core/asset-bucket une fois par déploiement et passez sa sortie bucket_name dans chaque module API / Lambda via l’entrée asset_bucket_name :
module "asset_bucket" { source = "../../common/terraform/src/core/asset-bucket"}
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
asset_bucket_name = module.asset_bucket.bucket_name
# Environment variables for the Lambda function env = { ENVIRONMENT = var.environment LOG_LEVEL = "INFO" }
# Additional IAM policies if needed additional_iam_policy_statements = [ # Add any additional permissions your API needs ]
tags = local.common_tags}module "asset_bucket" { source = "../../common/terraform/src/core/asset-bucket"}
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
asset_bucket_name = module.asset_bucket.bucket_name
user_pool_id = local.user_pool_id user_pool_client_ids = [local.client_id]
# Environment variables for the Lambda function env = { ENVIRONMENT = var.environment LOG_LEVEL = "INFO" }
# Additional IAM policies if needed additional_iam_policy_statements = [ # Add any additional permissions your API needs ]
tags = local.common_tags}Vous pouvez configurer le Cognito User Pool et le Client en utilisant les ressources ou modules Terraform appropriés.
Cela configure :
- Une fonction AWS Lambda qui sert toutes les procédures tRPC
- API Gateway HTTP/REST API comme déclencheur de fonction
- Rôles et permissions IAM
- Groupe de journaux CloudWatch
- Configuration du traçage X-Ray
- Configuration CORS
Le module Terraform fournit plusieurs sorties que vous pouvez utiliser :
# Access the API endpointoutput "api_url" { value = module.my_api.stage_invoke_url}
# Access Lambda function detailsoutput "lambda_function_name" { value = module.my_api.lambda_function_name}
# Access IAM role for granting additional permissionsoutput "lambda_execution_role_arn" { value = module.my_api.lambda_execution_role_arn}Vous pouvez personnaliser les paramètres CORS en passant des variables au module :
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
asset_bucket_name = module.asset_bucket.bucket_name
# Custom CORS configuration cors_allow_origins = ["https://myapp.com", "https://staging.myapp.com"] cors_allow_methods = ["GET", "POST", "PUT", "DELETE"] cors_allow_headers = [ "authorization", "content-type", "x-custom-header" ]
tags = local.common_tags}Pour les API REST, le construct généré associe par défaut une Web ACL AWS WAFv2 à l’étape API Gateway. La Web ACL utilise l’ensemble de règles par défaut géré par AWS (AWSManagedRulesCommonRuleSet et AWSManagedRulesKnownBadInputsRuleSet), offrant une protection contre les exploits web courants, y compris le Top 10 OWASP. Les journaux de requêtes WAF sont écrits dans un groupe CloudWatch Logs.
Vous pouvez modifier le construct rest-api généré pour ajouter, supprimer ou ajuster des règles (par exemple, pour ajouter des règles basées sur le taux ou des groupes de règles gérées supplémentaires).
Pour désactiver cette fonctionnalité (par exemple, pour attacher votre propre Web ACL), définissez enableWaf sur false :
const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(), enableWaf: false,});Pour désactiver cette fonctionnalité (par exemple, pour attacher votre propre Web ACL), définissez enable_waf sur false :
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
asset_bucket_name = module.asset_bucket.bucket_name enable_waf = false}Journalisation des accès
Section intitulée « Journalisation des accès »Pour les API REST, l’infrastructure générée active la journalisation des accès par défaut, écrivant une ligne JSON structurée par requête dans un groupe CloudWatch Logs dédié. Le groupe de logs est chiffré avec une clé KMS gérée par le client et conservé pendant un an.
API Gateway écrit les logs d’accès en utilisant un rôle CloudWatch Logs au niveau du compte. Ce rôle est configuré sur le paramètre AWS::ApiGateway::Account, qui est un singleton par région par compte — il n’y a qu’un seul rôle pour chaque API REST dans la région. Pour gérer cela en toute sécurité à travers plusieurs stacks déployées indépendamment, l’infrastructure générée :
- Crée un rôle CloudWatch Logs partagé et le configure sur le compte uniquement lorsqu’aucun rôle fonctionnel n’est déjà défini, de sorte que les déploiements n’écrasent jamais un rôle appartenant à une autre stack.
- Laisse le paramètre du compte intact lors du démontage, de sorte que la destruction d’une stack ne désactive jamais la journalisation pour d’autres API REST dans la région.
Le rôle du compte est géré par le construct ApiGatewayAccount, un singleton à portée de stack résolu via ApiGatewayAccount.ensure(scope). Le stage de chaque API REST en dépend, et le rôle est configuré par une ressource personnalisée basée sur Lambda.
Vous pouvez personnaliser le format du log d’accès en passant deployOptions lors de la construction de votre API :
const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(), deployOptions: { accessLogFormat: AccessLogFormat.clf(), },});Le rôle du compte est géré par le module core/api/api-gateway-account, qui est instancié par le module API généré. Il configure le compte de manière idempotente et n’est jamais réinitialisé lors d’un terraform destroy.
Vous pouvez personnaliser le format du log d’accès en modifiant le bloc access_log_settings sur la ressource aws_api_gateway_stage dans le module API généré.
Intégrations
Section intitulée « Intégrations »Les constructs CDK d’API REST/HTTP sont configurés pour fournir une interface type-safe pour définir des intégrations pour chacune de vos opérations.
Les constructs CDK fournissent un support complet d’intégration type-safe comme décrit ci-dessous.
Intégrations par défaut
Section intitulée « Intégrations par défaut »Vous pouvez utiliser la méthode statique defaultIntegrations pour utiliser le modèle par défaut, qui définit une fonction AWS Lambda individuelle pour chaque opération :
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(),});Les modules Terraform utilisent automatiquement le modèle de routeur avec une seule fonction Lambda. Aucune configuration supplémentaire n’est nécessaire :
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
asset_bucket_name = module.asset_bucket.bucket_name
# The module automatically creates a single Lambda function # that handles all API operations tags = local.common_tags}Accès aux intégrations
Section intitulée « Accès aux intégrations »Vous pouvez accéder aux fonctions AWS Lambda sous-jacentes via la propriété integrations du construct API, de manière type-safe. Par exemple, si votre API définit une opération nommée sayHello et que vous devez ajouter des permissions à cette fonction, vous pouvez le faire comme suit :
const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(),});
// sayHello is typed to the operations defined in your APIapi.integrations.sayHello.handler.addToRolePolicy(new PolicyStatement({ effect: Effect.ALLOW, actions: [...], resources: [...],}));Si votre API utilise le modèle shared, le Lambda de routeur partagé est exposé comme api.integrations.$router :
const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(),});
api.integrations.$router.handler.addEnvironment('LOG_LEVEL', 'DEBUG');Avec le modèle de routeur de Terraform, il n’y a qu’une seule fonction Lambda. Vous pouvez y accéder via les sorties du module :
# Grant additional permissions to the single Lambda functionresource "aws_iam_role_policy" "additional_permissions" { name = "additional-api-permissions" role = module.my_api.lambda_execution_role_name
policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "s3:GetObject", "s3:PutObject" ] Resource = "arn:aws:s3:::my-bucket/*" } ] })}Personnalisation des options par défaut
Section intitulée « Personnalisation des options par défaut »Si vous souhaitez personnaliser les options utilisées lors de la création de la fonction Lambda pour chaque intégration par défaut, vous pouvez utiliser la méthode withDefaultOptions. Par exemple, si vous souhaitez que toutes vos fonctions Lambda résident dans un Vpc :
const vpc = new Vpc(this, 'Vpc', ...);
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withDefaultOptions({ vpc, }) .build(),});Pour personnaliser des options comme la configuration VPC, vous devez modifier le module Terraform généré. Par exemple, pour ajouter le support VPC à toutes les fonctions Lambda :
# Add VPC variablesvariable "vpc_subnet_ids" { description = "List of VPC subnet IDs for Lambda function" type = list(string) default = []}
variable "vpc_security_group_ids" { description = "List of VPC security group IDs for Lambda function" type = list(string) default = []}
# Update the Lambda function resourceresource "aws_lambda_function" "api_lambda" { # ... existing configuration ...
# Add VPC configuration vpc_config { subnet_ids = var.vpc_subnet_ids security_group_ids = var.vpc_security_group_ids }}Ensuite, utilisez le module avec la configuration VPC :
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
asset_bucket_name = module.asset_bucket.bucket_name
# VPC configuration vpc_subnet_ids = [aws_subnet.private_a.id, aws_subnet.private_b.id] vpc_security_group_ids = [aws_security_group.lambda_sg.id]
tags = local.common_tags}Personnalisation des options par opération
Section intitulée « Personnalisation des options par opération »Pour personnaliser les options utilisées pour créer l’intégration par défaut pour des opérations spécifiques (sans affecter les autres), vous pouvez utiliser la méthode withOperationOptions. Par exemple, si vous souhaitez augmenter le timeout de la fonction Lambda pour une seule opération :
const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withOperationOptions({ sayHello: { timeout: Duration.seconds(60), }, }) .build(),});
// The selected operations remain default integrations, so they're still typed accordingly:api.integrations.sayHello.handler.addToRolePolicy(new PolicyStatement({ ... }));Les options que vous spécifiez sont fusionnées avec les options d’intégration par défaut (et toutes les options définies via withDefaultOptions). Notez que vous ne pouvez pas spécifier d’options pour les opérations que vous avez remplacées via withOverrides, car celles-ci n’utilisent plus l’intégration par défaut.
Vous rencontrerez une erreur de type si la même opération est ciblée à la fois par withOperationOptions et withOverrides, quel que soit l’ordre dans lequel vous les appelez.
Pour personnaliser les options pour des opérations spécifiques avec Terraform, vous devez modifier le module Terraform généré pour configurer des fonctions Lambda individuelles par opération (voir la section Intégrations explicites ci-dessous).
Remplacement des intégrations
Section intitulée « Remplacement des intégrations »Vous pouvez également remplacer les intégrations pour des opérations spécifiques en utilisant la méthode withOverrides. Chaque remplacement doit spécifier une propriété integration qui est typée selon le construct d’intégration CDK approprié pour l’API HTTP ou REST. La méthode withOverrides est également type-safe. Par exemple, si vous souhaitez remplacer une API getDocumentation pour pointer vers de la documentation hébergée par un site web externe, vous pourriez le faire comme suit :
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withOverrides({ getDocumentation: { integration: new HttpIntegration('https://example.com/documentation'), }, }) .build(),});Vous remarquerez également que l’intégration remplacée n’a plus de propriété handler lors de l’accès via api.integrations.getDocumentation.
Vous pouvez ajouter des propriétés supplémentaires à une intégration qui seront également typées en conséquence, permettant à d’autres types d’intégration d’être abstraits tout en restant type-safe, par exemple si vous avez créé une intégration S3 pour une API REST et souhaitez plus tard référencer le bucket pour une opération particulière, vous pouvez le faire comme suit :
const storageBucket = new Bucket(this, 'Bucket', { ... });
const apiGatewayRole = new Role(this, 'ApiGatewayS3Role', { assumedBy: new ServicePrincipal('apigateway.amazonaws.com'),});
storageBucket.grantRead(apiGatewayRole);
const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withOverrides({ getFile: { bucket: storageBucket, integration: new AwsIntegration({ service: 's3', integrationHttpMethod: 'GET', path: `${storageBucket.bucketName}/{fileName}`, options: { credentialsRole: apiGatewayRole, requestParameters: { 'integration.request.path.fileName': 'method.request.querystring.fileName', }, integrationResponses: [{ statusCode: '200' }], }, }), options: { requestParameters: { 'method.request.querystring.fileName': true, }, methodResponses: [{ statusCode: '200', }], } }, }) .build(),});
// Later, perhaps in another file, you can access the bucket property we defined// in a type-safe mannerapi.integrations.getFile.bucket.grantRead(...);Remplacement des autorisateurs
Section intitulée « Remplacement des autorisateurs »Vous pouvez également fournir des options dans votre intégration pour remplacer des options de méthode particulières telles que les autorisateurs, par exemple si vous souhaitez utiliser l’authentification Cognito pour votre opération getDocumentation :
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withOverrides({ getDocumentation: { integration: new HttpIntegration('https://example.com/documentation'), options: { authorizer: new CognitoUserPoolsAuthorizer(...) // for REST, or HttpUserPoolAuthorizer for an HTTP API } }, }) .build(),});Intégrations explicites
Section intitulée « Intégrations explicites »Si vous préférez, vous pouvez choisir de ne pas utiliser les intégrations par défaut et d’en fournir directement une pour chaque opération. Ceci est utile si, par exemple, chaque opération doit utiliser un type d’intégration différent ou si vous souhaitez recevoir une erreur de type lors de l’ajout de nouvelles opérations :
new MyApi(this, 'MyApi', { integrations: { sayHello: { integration: new LambdaIntegration(...), }, getDocumentation: { integration: new HttpIntegration(...), }, },});Pour des intégrations explicites par opération avec Terraform, vous devez modifier le module spécifique à l’application généré pour remplacer l’intégration proxy par défaut par des intégrations spécifiques pour chaque opération.
Modifiez packages/common/terraform/src/app/apis/my-api/my-api.tf :
- Supprimez les routes proxy par défaut (par exemple,
resource "aws_apigatewayv2_route" "proxy_routes") - Remplacez la fonction Lambda unique par des fonctions individuelles pour chaque opération
- Créez des intégrations et routes spécifiques pour chaque opération, en réutilisant le même bundle ZIP :
# Remove the default single Lambda function resource "aws_lambda_function" "api_lambda" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApiHandler" role = aws_iam_role.lambda_execution_role.arn handler = "index.handler" runtime = "nodejs22.x" timeout = 30 # ... rest of configuration }
# Remove the default proxy integration resource "aws_apigatewayv2_integration" "lambda_integration" { api_id = module.http_api.api_id integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.api_lambda.invoke_arn # ... rest of configuration }
# Remove the default proxy routes resource "aws_apigatewayv2_route" "proxy_routes" { for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]) api_id = module.http_api.api_id route_key = "${each.key} /{proxy+}" target = "integrations/${aws_apigatewayv2_integration.lambda_integration.id}" # ... rest of configuration }
# Add individual Lambda functions for each operation using the same bundle resource "aws_lambda_function" "say_hello_handler" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApi-SayHello" role = aws_iam_role.lambda_execution_role.arn handler = "sayHello.handler" # Specific handler for this operation runtime = "nodejs22.x" timeout = 30 source_code_hash = data.archive_file.lambda_zip.output_base64sha256
tracing_config { mode = "Active" }
environment { variables = var.env }
tags = var.tags }
resource "aws_lambda_function" "get_documentation_handler" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApi-GetDocumentation" role = aws_iam_role.lambda_execution_role.arn handler = "getDocumentation.handler" # Specific handler for this operation runtime = "nodejs22.x" timeout = 30 source_code_hash = data.archive_file.lambda_zip.output_base64sha256
tracing_config { mode = "Active" }
environment { variables = var.env }
tags = var.tags }
# Add specific integrations for each operation resource "aws_apigatewayv2_integration" "say_hello_integration" { api_id = module.http_api.api_id integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.say_hello_handler.invoke_arn payload_format_version = "2.0" timeout_milliseconds = 30000 }
resource "aws_apigatewayv2_integration" "get_documentation_integration" { api_id = module.http_api.api_id integration_type = "HTTP_PROXY" integration_uri = "https://example.com/documentation" integration_method = "GET" }
# Add specific routes for each operation resource "aws_apigatewayv2_route" "say_hello_route" { api_id = module.http_api.api_id route_key = "POST /sayHello" target = "integrations/${aws_apigatewayv2_integration.say_hello_integration.id}" authorization_type = "AWS_IAM" }
resource "aws_apigatewayv2_route" "get_documentation_route" { api_id = module.http_api.api_id route_key = "GET /documentation" target = "integrations/${aws_apigatewayv2_integration.get_documentation_integration.id}" authorization_type = "NONE" }
# Add Lambda permissions for each function resource "aws_lambda_permission" "say_hello_permission" { statement_id = "AllowExecutionFromAPIGateway-SayHello" action = "lambda:InvokeFunction" function_name = aws_lambda_function.say_hello_handler.function_name principal = "apigateway.amazonaws.com" source_arn = "${module.http_api.api_execution_arn}/*/*" }
resource "aws_lambda_permission" "get_documentation_permission" { statement_id = "AllowExecutionFromAPIGateway-GetDocumentation" action = "lambda:InvokeFunction" function_name = aws_lambda_function.get_documentation_handler.function_name principal = "apigateway.amazonaws.com" source_arn = "${module.http_api.api_execution_arn}/*/*" }# Remove the default single Lambda function resource "aws_lambda_function" "api_lambda" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApiHandler" role = aws_iam_role.lambda_execution_role.arn handler = "index.handler" runtime = "nodejs22.x" timeout = 30 # ... rest of configuration }
# Remove the default proxy integration resource "aws_apigatewayv2_integration" "lambda_integration" { api_id = module.http_api.api_id integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.api_lambda.invoke_arn # ... rest of configuration }
# Remove the default proxy routes resource "aws_apigatewayv2_route" "proxy_routes" { for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]) api_id = module.http_api.api_id route_key = "${each.key} /{proxy+}" target = "integrations/${aws_apigatewayv2_integration.lambda_integration.id}" # ... rest of configuration }
# Add individual Lambda functions for each operation using the same bundle resource "aws_lambda_function" "say_hello_handler" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApi-SayHello" role = aws_iam_role.lambda_execution_role.arn handler = "sayHello.handler" # Specific handler for this operation runtime = "nodejs22.x" timeout = 30 source_code_hash = data.archive_file.lambda_zip.output_base64sha256
tracing_config { mode = "Active" }
environment { variables = var.env }
tags = var.tags }
resource "aws_lambda_function" "get_documentation_handler" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApi-GetDocumentation" role = aws_iam_role.lambda_execution_role.arn handler = "getDocumentation.handler" # Specific handler for this operation runtime = "nodejs22.x" timeout = 30 source_code_hash = data.archive_file.lambda_zip.output_base64sha256
tracing_config { mode = "Active" }
environment { variables = var.env }
tags = var.tags }
# Add specific resources and methods for each operation resource "aws_api_gateway_resource" "say_hello_resource" { rest_api_id = module.rest_api.api_id parent_id = module.rest_api.api_root_resource_id path_part = "sayHello" }
resource "aws_api_gateway_method" "say_hello_method" { rest_api_id = module.rest_api.api_id resource_id = aws_api_gateway_resource.say_hello_resource.id http_method = "POST" authorization = "AWS_IAM" }
resource "aws_api_gateway_integration" "say_hello_integration" { rest_api_id = module.rest_api.api_id resource_id = aws_api_gateway_resource.say_hello_resource.id http_method = aws_api_gateway_method.say_hello_method.http_method
integration_http_method = "POST" type = "AWS_PROXY" uri = aws_lambda_function.say_hello_handler.invoke_arn }
resource "aws_api_gateway_resource" "get_documentation_resource" { rest_api_id = module.rest_api.api_id parent_id = module.rest_api.api_root_resource_id path_part = "documentation" }
resource "aws_api_gateway_method" "get_documentation_method" { rest_api_id = module.rest_api.api_id resource_id = aws_api_gateway_resource.get_documentation_resource.id http_method = "GET" authorization = "NONE" }
resource "aws_api_gateway_integration" "get_documentation_integration" { rest_api_id = module.rest_api.api_id resource_id = aws_api_gateway_resource.get_documentation_resource.id http_method = aws_api_gateway_method.get_documentation_method.http_method
integration_http_method = "GET" type = "HTTP" uri = "https://example.com/documentation" }
# Update deployment to depend on new integrations~ resource "aws_api_gateway_deployment" "api_deployment" { rest_api_id = module.rest_api.api_id
depends_on = [ aws_api_gateway_integration.lambda_integration, aws_api_gateway_integration.say_hello_integration, aws_api_gateway_integration.get_documentation_integration, ]
lifecycle { create_before_destroy = true }
triggers = { redeployment = sha1(jsonencode([ aws_api_gateway_integration.say_hello_integration, aws_api_gateway_integration.get_documentation_integration, ])) } }
# Add Lambda permissions for each function resource "aws_lambda_permission" "say_hello_permission" { statement_id = "AllowExecutionFromAPIGateway-SayHello" action = "lambda:InvokeFunction" function_name = aws_lambda_function.say_hello_handler.function_name principal = "apigateway.amazonaws.com" source_arn = "${module.rest_api.api_execution_arn}/*/*" }
resource "aws_lambda_permission" "get_documentation_permission" { statement_id = "AllowExecutionFromAPIGateway-GetDocumentation" action = "lambda:InvokeFunction" function_name = aws_lambda_function.get_documentation_handler.function_name principal = "apigateway.amazonaws.com" source_arn = "${module.rest_api.api_execution_arn}/*/*" }Modèle d’intégration
Section intitulée « Modèle d’intégration »Les constructs d’API CDK générés prennent en charge deux modèles d’intégration :
isolatedcrée une fonction Lambda par opération. C’est le modèle par défaut pour les API générées.sharedcrée un seul Lambda de routeur par défaut et le réutilise pour chaque opération sauf si vous remplacez des intégrations spécifiques.
isolated vous donne des permissions et une configuration plus granulaires par opération. shared réduit la prolifération des fonctions Lambda et des intégrations API Gateway tout en permettant des remplacements sélectifs.
Par exemple, définir pattern à 'shared' crée une seule fonction au lieu d’une par intégration :
export class MyApi<...> extends ... {
public static defaultIntegrations = (scope: Construct) => { ... return IntegrationBuilder.rest({ pattern: 'shared', ... }); };}Les modules Terraform utilisent automatiquement le modèle de routeur - c’est l’approche par défaut et la seule prise en charge. Le module généré crée une seule fonction Lambda qui gère toutes les opérations API.
Vous pouvez simplement instancier le module par défaut pour obtenir le modèle de routeur :
# Default router pattern - single Lambda function for all operationsmodule "my_api" { source = "../../common/terraform/src/app/apis/my-api"
asset_bucket_name = module.asset_bucket.bucket_name
# Single Lambda function handles all operations automatically tags = local.common_tags}Accorder l’accès (IAM uniquement)
Section intitulée « Accorder l’accès (IAM uniquement) »Vous pouvez accorder l’accès à votre API comme suit :
api.grantInvokeAccess(myIdentityPool.authenticatedRole);# Create an IAM policy to allow invoking the APIresource "aws_iam_policy" "api_invoke_policy" { name = "MyApiInvokePolicy" description = "Policy to allow invoking the tRPC API"
policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = "execute-api:Invoke" Resource = "${module.my_api.api_execution_arn}/*/*" } ] })}
# Attach the policy to an IAM role (e.g., for authenticated users)resource "aws_iam_role_policy_attachment" "api_invoke_access" { role = aws_iam_role.authenticated_user_role.name policy_arn = aws_iam_policy.api_invoke_policy.arn}
# Or attach to an existing role by nameresource "aws_iam_role_policy_attachment" "api_invoke_access_existing" { role = "MyExistingRole" policy_arn = aws_iam_policy.api_invoke_policy.arn}Les sorties clés du module API que vous pouvez utiliser pour les politiques IAM sont :
module.my_api.api_execution_arn- Pour accorder les permissions execute-api:Invokemodule.my_api.api_arn- L’ARN de l’API Gatewaymodule.my_api.lambda_function_arn- L’ARN de la fonction Lambda
Cible de bundle
Section intitulée « Cible de bundle »Le générateur configure automatiquement une cible bundle qui utilise Rolldown pour créer un package de déploiement :
pnpm nx bundle <project-name>yarn nx bundle <project-name>npx nx bundle <project-name>bunx nx bundle <project-name>La configuration de Rolldown se trouve dans rolldown.config.ts, avec une entrée par bundle à générer. Rolldown gère la création de plusieurs bundles en parallèle s’ils sont définis.
Serveur tRPC local
Section intitulée « Serveur tRPC local »Vous pouvez utiliser la cible serve pour exécuter un serveur local pour votre API, par exemple :
pnpm nx serve my-apiyarn nx serve my-apinpx nx serve my-apibunx nx serve my-apiLe point d’entrée pour le serveur local est src/local-server.ts.
Cela se rechargera automatiquement lorsque vous apporterez des modifications à votre API.
Invoquer votre API tRPC
Section intitulée « Invoquer votre API tRPC »Vous pouvez créer un client tRPC pour invoquer votre API de manière type-safe. Si vous appelez votre API tRPC depuis un autre backend, vous pouvez utiliser le client dans src/client/index.ts, par exemple :
import { createMyApiClient } from '@my-scope/my-api';
const client = createMyApiClient({ url: 'https://my-api-url.example.com/' });
await client.echo.query({ message: 'Hello world!' });Si vous appelez votre API depuis un site web React, envisagez d’utiliser le générateur Connection pour configurer le client.
Plus d’informations
Section intitulée « Plus d’informations »Pour plus d’informations sur tRPC, veuillez vous référer à la documentation tRPC.
Connexions
Section intitulée « Connexions »Utilisez le générateur connection pour intégrer ce projet avec d’autres dans votre espace de travail. Les connexions suivantes impliquent ce projet :