Aller au contenu

tRPC

Filter this guidePick generator option values to hide sections that don't apply.

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.

Vous pouvez générer une nouvelle API tRPC de deux manières :

Terminal window
pnpm nx g @aws/nx-plugin:ts#api --framework=trpc
Vous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
Terminal window
pnpm nx g @aws/nx-plugin:ts#api --framework=trpc --dry-run
ParamètreTypePar défautDescription
name Requisstring-Le nom de l'API (requis). Utilisé pour générer les noms de classes et les chemins de fichiers.
framework trpc | smithytrpcLe 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 | sharedisolatedComment les intégrations API Gateway sont générées pour l'API. Choisissez entre isolated (par défaut) et shared.
auth iam | cognito | customiamLa méthode utilisée pour s'authentifier auprès de votre API. Choisissez entre iam (par défaut), cognito ou custom.
directory stringpackagesLe 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 | terraforminheritLe fournisseur IaC préféré. Par défaut, celui-ci est hérité de votre sélection initiale.
infra rest-lambda | http-lambda | nonerest-lambdaLe type d'infrastructure à utiliser pour déployer cette API.
preferInstallDependencies booleantrueIndique 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.

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

É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

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

L’application déployée a l’architecture suivante :

ClientWAFAPI Gateway(REST API)LambdaCloudWatch(Logs, Metrics)X-Ray(Traces)

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é.

À 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 definition
export const UserSchema = z.object({
name: z.string(),
height: z.number(),
dateOfBirth: z.string().datetime(),
});
// Corresponding TypeScript type
export 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.

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 :

  • publicProcedure définit une méthode publique sur l’API, incluant le middleware configuré dans src/middleware. Ce middleware inclut l’intégration AWS Lambda Powertools pour la journalisation, le traçage et les métriques.
  • input accepte 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.
  • output accepte 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.
  • query accepte une fonction qui définit l’implémentation de votre API. Cette implémentation reçoit opts, qui contient l’input passé à votre opération, ainsi que d’autres contextes configurés par le middleware, disponibles dans opts.ctx. La fonction passée à query doit retourner une sortie conforme au schéma output.

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.

infra = rest-lambda

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.

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

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

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.

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.

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.

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.

auth = iam

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!,
},
},
});
});
};
auth = cognito

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

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 :

auth = iam | custom
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(),
});
}
}
auth = cognito
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.

infra = rest-lambda

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,
});
infra = rest-lambda

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

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.

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

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 API
api.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');

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

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 manner
api.integrations.getFile.bucket.grantRead(...);

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

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

Les constructs d’API CDK générés prennent en charge deux modèles d’intégration :

  • isolated crée une fonction Lambda par opération. C’est le modèle par défaut pour les API générées.
  • shared cré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 :

packages/common/constructs/src/app/apis/my-api.ts
export class MyApi<...> extends ... {
public static defaultIntegrations = (scope: Construct) => {
...
return IntegrationBuilder.rest({
pattern: 'shared',
...
});
};
}
auth = iam

Vous pouvez accorder l’accès à votre API comme suit :

api.grantInvokeAccess(myIdentityPool.authenticatedRole);

Le générateur configure automatiquement une cible bundle qui utilise Rolldown pour créer un package de déploiement :

Terminal window
pnpm 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.

Vous pouvez utiliser la cible serve pour exécuter un serveur local pour votre API, par exemple :

Terminal window
pnpm nx serve my-api

Le point d’entrée pour le serveur local est src/local-server.ts.

Cela se rechargera automatiquement lorsque vous apporterez des modifications à votre API.

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.

Pour plus d’informations sur tRPC, veuillez vous référer à la documentation tRPC.

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 :

tRPC
React to tRPCCall a tRPC API from a React website
tRPCAmazon Aurora
tRPC API to Relational DatabaseConnect a tRPC API to an Aurora relational database
tRPCAmazon DynamoDB
tRPC API to TypeScript DynamoDBConnect a tRPC API to a DynamoDB table