Ir al contenido

tRPC

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

tRPC es un marco para construir APIs en TypeScript con seguridad de tipos de extremo a extremo. Usando tRPC, las actualizaciones en las entradas y salidas de las operaciones de la API se reflejan inmediatamente en el código del cliente y son visibles en tu IDE sin necesidad de reconstruir tu proyecto.

El generador de API tRPC crea una nueva API tRPC con configuración de infraestructura usando AWS CDK o Terraform. El backend generado utiliza AWS Lambda para despliegues serverless, expuesto a través de un API Gateway de AWS, e incluye validación de esquemas usando Zod. Configura AWS Lambda Powertools para observabilidad, incluyendo logging, trazado con AWS X-Ray y métricas de CloudWatch.

Puedes generar una nueva API tRPC de dos formas:

  1. Instale el Nx Console VSCode Plugin si aún no lo ha hecho
  2. Abra la consola Nx en VSCode
  3. Haga clic en Generate (UI) en la sección "Common Nx Commands"
  4. Busque @aws/nx-plugin - ts#api
  5. Complete los parámetros requeridos
    • framework: trpc
  6. Haga clic en Generate
ParámetroTipoPredeterminadoDescripción
name Requeridostring-El nombre de la API (requerido). Se utiliza para generar nombres de clases y rutas de archivos.
framework trpc | smithytrpcEl framework de API a utilizar.
namespace string-El espacio de nombres para la API Smithy (solo aplicable para el framework smithy). Por defecto es el alcance de tu monorepo
integrationPattern isolated | sharedisolatedCómo se generan las integraciones de API Gateway para la API. Elija entre isolated (predeterminado) y shared.
auth iam | cognito | customiamEl método utilizado para autenticar con tu API. Elige entre iam (predeterminado), cognito o custom.
directory stringpackagesEl directorio donde almacenar la aplicación.
subDirectory string-El subdirectorio en el que se coloca el proyecto. Por defecto es el nombre del proyecto.
iac inherit | cdk | terraforminheritEl proveedor de IaC preferido. Por defecto, se hereda de tu selección inicial.
infra rest-lambda | http-lambda | nonerest-lambdaEl tipo de infraestructura a utilizar para desplegar esta API.
preferInstallDependencies booleantrueSi se prefiere instalar las dependencias después de que se ejecute el generador. Establece en false para diferir la instalación cuando se ejecutan múltiples generadores en lote (la instalación aún se ejecuta si es necesario para que los generadores subsecuentes puedan calcular el grafo de proyectos de Nx); instala una vez al final.

El generador creará la siguiente estructura de proyecto en el directorio <directory>/<api-name>:

  • Directoriosrc
    • init.ts Inicialización del backend tRPC
    • handler.ts Punto de entrada del manejador Lambda
    • router.ts Definición del router tRPC
    • Directorioschema Definiciones de esquemas usando Zod
      • echo.ts Ejemplo de definiciones para entrada y salida del procedimiento “echo”
      • z-async-iterable.ts Helper de Zod para subscripciones (solo REST API)
    • Directorioprocedures Procedimientos (u operaciones) expuestos por tu API
      • echo.ts Procedimiento de ejemplo
    • Directoriomiddleware
      • error.ts Middleware para manejo de errores
      • logger.ts Middleware para configurar AWS Powertools para logging en Lambda
      • tracer.ts Middleware para configurar AWS Powertools para trazado en Lambda
      • metrics.ts Middleware para configurar AWS Powertools para métricas en Lambda
    • local-server.ts Punto de entrada del adaptador standalone de tRPC para servidor de desarrollo local
    • Directorioclient
      • index.ts Cliente con seguridad de tipos para llamadas máquina-a-máquina
  • tsconfig.json Configuración de TypeScript
  • project.json Configuración de proyecto y objetivos de build

Dado que este generador proporciona infraestructura como código basada en tu proveedor de iacProvider seleccionado, creará un proyecto en packages/common que incluye los constructos CDK o módulos de Terraform correspondientes.

El proyecto común de infraestructura como código tiene la siguiente estructura:

  • Directoriopackages/common/constructs
    • Directoriosrc
      • Directorioapp/ Constructos para infraestructura específica de un proyecto/generador
      • Directoriocore/ Constructos genéricos reutilizados por los constructos en app
      • index.ts Punto de entrada que exporta los constructos de app
    • project.json Objetivos de compilación y configuración del proyecto

Para implementar tu API, se generan los siguientes archivos:

  • Directoriopackages/common/constructs/src
    • Directorioapp
      • Directorioapis
        • <project-name>.ts Constructo de CDK para implementar tu API
    • Directoriocore
      • Directorioapi
        • http-api.ts Constructo de CDK para implementar una API HTTP (si seleccionaste implementar una API HTTP)
        • rest-api.ts Constructo de CDK para implementar una API REST (si seleccionaste implementar una API REST)
        • utils.ts Utilidades para los constructos de API

La aplicación desplegada tiene la siguiente arquitectura:

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

Las REST APIs incluyen una Web ACL de AWS WAFv2 frente al stage de API Gateway con el conjunto de reglas predeterminado administrado por AWS habilitado.

A alto nivel, las APIs tRPC consisten en un router que delega solicitudes a procedimientos específicos. Cada procedimiento tiene una entrada y salida definidas como esquemas Zod.

El directorio src/schema contiene los tipos compartidos entre tu código cliente y servidor. En este paquete, estos tipos se definen usando Zod, una biblioteca de declaración y validación de esquemas TypeScript-first.

Un esquema de ejemplo podría verse así:

import { z } from 'zod';
// Definición del esquema
export const UserSchema = z.object({
name: z.string(),
height: z.number(),
dateOfBirth: z.string().datetime(),
});
// Tipo TypeScript correspondiente
export type User = z.TypeOf<typeof UserSchema>;

Dado el esquema anterior, el tipo User es equivalente al siguiente TypeScript:

interface User {
name: string;
height: number;
dateOfBirth: string;
}

Los esquemas son compartidos por el código del servidor y cliente, proporcionando un único lugar para actualizar cuando se realizan cambios en las estructuras usadas en tu API.

Los esquemas son validados automáticamente por tu API tRPC en tiempo de ejecución, lo que evita tener que crear lógica de validación manual en el backend.

Zod proporciona utilidades poderosas para combinar o derivar esquemas como .merge, .pick, .omit y más. Puedes encontrar más información en el sitio de documentación de Zod.

Tu router tRPC está definido en src/router.ts, que registra todos los procedimientos. Cada procedimiento define la entrada esperada, la salida y la implementación. El punto de entrada del manejador Lambda está en src/handler.ts, que reenvía las solicitudes a tu router.

El router de ejemplo generado tiene una sola operación llamada echo:

import { echo } from './procedures/echo.js';
export const appRouter = router({
echo,
});

El procedimiento echo de ejemplo se genera en src/procedures/echo.ts:

export const echo = publicProcedure
.input(EchoInputSchema)
.output(EchoOutputSchema)
.query((opts) => ({ message: opts.input.message }));

Desglosando lo anterior:

  • publicProcedure define un método público en la API, incluyendo el middleware configurado en src/middleware. Este middleware incluye integración con AWS Lambda Powertools para logging, trazado y métricas.
  • input acepta un esquema Zod que define la entrada esperada para la operación. Las solicitudes enviadas para esta operación se validan automáticamente contra este esquema.
  • output acepta un esquema Zod que define la salida esperada para la operación. Verás errores de tipo en tu implementación si no devuelves una salida que cumpla con el esquema.
  • query acepta una función que define la implementación de tu API. Esta implementación recibe opts, que contiene el input pasado a tu operación, así como otro contexto configurado por el middleware, disponible en opts.ctx. La función pasada a query debe devolver una salida que cumpla con el esquema output.

El uso de query para definir la implementación indica que la operación no es mutativa. Úsalo para definir métodos de recuperación de datos. Para implementar una operación mutativa, usa el método mutation en su lugar.

Si agregas un nuevo procedimiento, asegúrate de registrarlo añadiéndolo al router en src/router.ts.

infra = rest-lambda

Las subscripciones de tRPC te permiten transmitir datos del servidor al cliente usando Server-Sent Events (SSE). Cuando seleccionas rest-lambda como tu tipo de cómputo, el generador configura automáticamente la infraestructura requerida para streaming, así como un manejador Lambda de streaming y el helper de esquema ZodAsyncIterable.

Para definir un procedimiento de subscripción, usa el método .subscription con una función generadora asíncrona. Usa el helper ZodAsyncIterable de src/schema/z-async-iterable.ts para definir el esquema de salida:

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) {
// Emite datos al cliente a medida que estén disponibles
for (const chunk of await getResults(opts.input.query)) {
yield { text: chunk };
}
});

Registra la subscripción en tu router como cualquier otro procedimiento:

export const appRouter = router({
echo,
myStream,
});

La infraestructura generada usa un manejador Lambda de streaming con ResponseTransferMode.STREAM en API Gateway para todas las operaciones de REST API, lo que permite que las subscripciones funcionen junto con queries y mutations regulares.

En tu implementación, puedes devolver respuestas de error a los clientes lanzando un TRPCError. Estos aceptan un code que indica el tipo de error, por ejemplo:

throw new TRPCError({
code: 'NOT_FOUND',
message: 'No se pudo encontrar el recurso solicitado',
});

A medida que tu API crece, puedes querer agrupar operaciones relacionadas.

Puedes agrupar operaciones usando routers anidados, por ejemplo:

import { getUser } from './procedures/users/get.js';
import { listUsers } from './procedures/users/list.js';
const appRouter = router({
users: router({
get: getUser,
list: listUsers,
}),
...
})

Los clientes entonces reciben esta agrupación de operaciones, por ejemplo, invocar la operación listUsers en este caso se vería así:

client.users.list.query();

El logger de AWS Lambda Powertools se configura en src/middleware/logger.ts, y se puede acceder a él en una implementación de API vía opts.ctx.logger. Puedes usarlo para registrar en CloudWatch Logs, y/o controlar valores adicionales a incluir en cada mensaje de log estructurado. Por ejemplo:

export const echo = publicProcedure
.input(...)
.output(...)
.query(async (opts) => {
opts.ctx.logger.info('Operación llamada con entrada', opts.input);
return ...;
});

Para más información sobre el logger, consulta la documentación de AWS Lambda Powertools Logger.

Las métricas de AWS Lambda Powertools se configuran en src/middleware/metrics.ts, y se puede acceder a ellas en una implementación de API vía opts.ctx.metrics. Puedes usarlas para registrar métricas en CloudWatch sin necesidad de importar y usar el SDK de AWS, por ejemplo:

export const echo = publicProcedure
.input(...)
.output(...)
.query(async (opts) => {
opts.ctx.metrics.addMetric('Invocations', 'Count', 1);
return ...;
});

Para más información, consulta la documentación de AWS Lambda Powertools Metrics.

El tracer de AWS Lambda Powertools se configura en src/middleware/tracer.ts, y se puede acceder a él en una implementación de API vía opts.ctx.tracer. Puedes usarlo para agregar trazas con AWS X-Ray y obtener insights detallados sobre el rendimiento y flujo de las solicitudes de API. Por ejemplo:

export const echo = publicProcedure
.input(...)
.output(...)
.query(async (opts) => {
const subSegment = opts.ctx.tracer.getSegment()!.addNewSubsegment('MyAlgorithm');
// ... lógica de mi algoritmo para capturar
subSegment.close();
return ...;
});

Para más información, consulta la documentación de AWS Lambda Powertools Tracer.

Puedes agregar valores adicionales al contexto proporcionado a los procedimientos implementando middleware.

Como ejemplo, implementemos un middleware para extraer detalles sobre el usuario que llama a nuestra API en src/middleware/identity.ts.

auth = iam

Este ejemplo recorre el middleware de identidad para autenticación IAM. Buscamos al llamante en Cognito usando el sub extraído del evento de API Gateway.

Primero, definimos lo que agregaremos al contexto:

export interface IIdentityContext {
identity?: {
sub: string;
username: string;
};
}

Nota que definimos una propiedad adicional opcional al contexto. tRPC se encarga de asegurar que esto esté definido en procedimientos que hayan configurado correctamente este middleware.

Luego, implementamos el middleware mismo. Tiene la siguiente estructura:

export const createIdentityPlugin = () => {
const t = initTRPC.context<...>().create();
return t.procedure.use(async (opts) => {
// Agrega lógica aquí para ejecutar antes del procedimiento
const response = await opts.next(...);
// Agrega lógica aquí para ejecutar después del procedimiento
return response;
});
};

En nuestro caso, queremos extraer detalles del usuario de Cognito que llama. Hacemos esto extrayendo el ID de sujeto (o “sub”) del usuario del evento de API Gateway, y recuperando detalles del usuario de Cognito. La implementación varía dependiendo de si el evento fue proporcionado a nuestra función por una REST API o una HTTP API:

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: `No se pudo determinar el usuario llamante`,
});
}
const { Users } = await cognito.listUsers({
// Asume que el ID del user pool está configurado en el entorno de Lambda
UserPoolId: process.env.USER_POOL_ID!,
Limit: 1,
Filter: `sub="${sub}"`,
});
if (!Users || Users.length !== 1) {
throw new TRPCError({
code: 'FORBIDDEN',
message: `No se encontró usuario con subjectId ${sub}`,
});
}
// Proporciona la identidad a otros procedimientos en el contexto
return await opts.next({
ctx: {
...opts.ctx,
identity: {
sub,
username: Users[0].Username!,
},
},
});
});
};
auth = cognito

Cuando despliegas con auth: 'cognito', el autorizador de Cognito User Pools de API Gateway verifica el JWT que el llamante proporciona en el encabezado Authorization y coloca las claims verificadas en el evento Lambda en event.requestContext.authorizer.claims. Nuestro middleware simplemente lee esas claims — sin llamadas adicionales al SDK de AWS, sin verificación manual de JWT.

Primero, definimos lo que agregaremos al contexto:

export interface IIdentityContext {
identity?: {
sub: string;
username: string;
};
}

Nota que definimos una propiedad adicional opcional en el contexto. tRPC se encarga de asegurar que esto esté definido en procedimientos que hayan configurado correctamente este middleware.

A continuación, el middleware mismo:

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: 'No se pudo determinar el usuario llamante',
});
}
return await opts.next({
ctx: {
...opts.ctx,
identity: {
sub,
username,
},
},
});
});
};

Luego puedes mezclar el plugin en cualquier procedimiento que necesite la identidad del llamante:

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

El generador de API tRPC crea infraestructura como código con CDK o Terraform basado en tu iacProvider seleccionado. Puedes usar esto para desplegar tu API tRPC.

El constructo CDK para desplegar tu API está en la carpeta common/constructs. Puedes consumirlo en una aplicación CDK, por ejemplo:

auth = iam | custom
import { MyApi } from ':my-scope/common-constructs';
export class ExampleStack extends Stack {
constructor(scope: Construct, id: string) {
// Agrega la API a tu 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) {
// Agrega la API a tu stack
const identity = new UserIdentity(this, 'Identity');
const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
identity,
});
}
}

El constructo UserIdentity puede generarse usando el generador ts#website#auth.

Esto configura la infraestructura de tu API, incluyendo un AWS API Gateway REST o HTTP API, funciones AWS Lambda para lógica de negocio, y autenticación basada en tu método auth elegido.

infra = rest-lambda

Para las API REST, el constructo generado asocia una Web ACL de AWS WAFv2 con la etapa de API Gateway de forma predeterminada. La Web ACL utiliza el conjunto de reglas predeterminado administrado por AWS (AWSManagedRulesCommonRuleSet y AWSManagedRulesKnownBadInputsRuleSet), proporcionando protección contra exploits web comunes, incluido el OWASP Top 10. Los registros de solicitudes de WAF se escriben en un grupo de CloudWatch Logs.

Puedes editar el constructo rest-api generado para agregar, eliminar o ajustar reglas (por ejemplo, para agregar reglas basadas en tasas o grupos de reglas administradas adicionales).

Para optar por no participar (por ejemplo, para adjuntar tu propia Web ACL), establece enableWaf en false:

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
enableWaf: false,
});
infra = rest-lambda

Para las APIs REST, la infraestructura generada habilita el registro de acceso por defecto, escribiendo una línea JSON estructurada por solicitud en un grupo de CloudWatch Logs dedicado. El grupo de registros está cifrado con una clave KMS administrada por el cliente y se conserva durante un año.

API Gateway escribe registros de acceso utilizando un rol de CloudWatch Logs a nivel de cuenta. Este rol se configura en el ajuste AWS::ApiGateway::Account, que es un singleton por región por cuenta — solo hay un rol para cada API REST en la región. Para gestionar esto de forma segura a través de múltiples stacks desplegados de forma independiente, la infraestructura generada:

  • Crea un rol compartido de CloudWatch Logs y lo configura en la cuenta solo cuando no hay un rol funcional ya establecido, por lo que los despliegues nunca sobrescriben un rol que pertenece a otro stack.
  • Deja el ajuste de la cuenta intacto durante el desmontaje, por lo que destruir un stack nunca deshabilita el registro para otras APIs REST en la región.

El rol de cuenta es gestionado por el constructo ApiGatewayAccount, un singleton con ámbito de stack resuelto a través de ApiGatewayAccount.ensure(scope). Cada etapa de la API REST depende de él, y el rol se configura mediante un recurso personalizado respaldado por Lambda.

Puedes personalizar el formato del registro de acceso pasando deployOptions al construir tu API:

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
deployOptions: {
accessLogFormat: AccessLogFormat.clf(),
},
});

Los constructos CDK de la API REST/HTTP están configurados para proporcionar una interfaz type-safe que define integraciones para cada una de tus operaciones.

Los constructos CDK proporcionan soporte completo de integración con seguridad de tipos como se describe a continuación.

Puedes usar el método estático defaultIntegrations para utilizar el patrón por defecto, que define una función AWS Lambda individual para cada operación:

new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
});

Puedes acceder a las funciones AWS Lambda subyacentes a través de la propiedad integrations del constructo de la API, de manera type-safe. Por ejemplo, si tu API define una operación llamada sayHello y necesitas agregar permisos a esta función, puedes hacerlo así:

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
});
// sayHello está tipado según las operaciones definidas en tu API
api.integrations.sayHello.handler.addToRolePolicy(new PolicyStatement({
effect: Effect.ALLOW,
actions: [...],
resources: [...],
}));

Si tu API usa el patrón shared, el router Lambda compartido se expone como api.integrations.$router:

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
});
api.integrations.$router.handler.addEnvironment('LOG_LEVEL', 'DEBUG');

Si deseas personalizar las opciones usadas al crear la función Lambda para cada integración por defecto, puedes usar el método withDefaultOptions. Por ejemplo, si quieres que todas tus funciones Lambda residan en una VPC:

const vpc = new Vpc(this, 'Vpc', ...);
new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withDefaultOptions({
vpc,
})
.build(),
});

Para personalizar las opciones usadas al crear la integración por defecto para operaciones específicas (sin afectar a las demás), puedes usar el método withOperationOptions. Por ejemplo, si deseas aumentar el timeout de la función Lambda para solo una operación:

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withOperationOptions({
sayHello: {
timeout: Duration.seconds(60),
},
})
.build(),
});
// Las operaciones seleccionadas siguen siendo integraciones por defecto, por lo que siguen tipadas en consecuencia:
api.integrations.sayHello.handler.addToRolePolicy(new PolicyStatement({ ... }));

Las opciones que especificas se fusionan con las opciones de integración por defecto (y cualquier opción establecida mediante withDefaultOptions). Ten en cuenta que no puedes especificar opciones para operaciones que hayas reemplazado mediante withOverrides, ya que estas ya no usan la integración por defecto.

Encontrarás un error de tipo si la misma operación es objetivo tanto de withOperationOptions como de withOverrides, independientemente del orden en que los llames.

También puedes sobrescribir integraciones para operaciones específicas usando el método withOverrides. Cada override debe especificar una propiedad integration que está tipada al constructo de integración CDK apropiado para la API HTTP o REST. El método withOverrides también es type-safe. Por ejemplo, si quieres sobrescribir una API getDocumentation para apuntar a documentación alojada en un sitio externo:

new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withOverrides({
getDocumentation: {
integration: new HttpIntegration('https://example.com/documentation'),
},
})
.build(),
});

Notarás que la integración sobrescrita ya no tiene una propiedad handler cuando se accede a través de api.integrations.getDocumentation.

Puedes agregar propiedades adicionales a una integración que también estarán tipadas, permitiendo abstraer otros tipos de integración manteniendo el type-safe. Por ejemplo, si creaste una integración S3 para una API REST y luego quieres referenciar el bucket para una operación particular:

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(),
});
// Más tarde, quizás en otro archivo, puedes acceder a la propiedad bucket que definimos
// de manera type-safe
api.integrations.getFile.bucket.grantRead(...);

También puedes proveer options en tu integración para sobrescribir opciones de método específicas como autorizadores. Por ejemplo, si deseas usar autenticación con Cognito para tu operación getDocumentation:

new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withOverrides({
getDocumentation: {
integration: new HttpIntegration('https://example.com/documentation'),
options: {
authorizer: new CognitoUserPoolsAuthorizer(...) // para REST, o HttpUserPoolAuthorizer para HTTP API
}
},
})
.build(),
});

Si prefieres, puedes no usar las integraciones por defecto y proveer directamente una para cada operación. Esto es útil si, por ejemplo, cada operación necesita usar un tipo diferente de integración o quieres recibir un error de tipo al agregar nuevas operaciones:

new MyApi(this, 'MyApi', {
integrations: {
sayHello: {
integration: new LambdaIntegration(...),
},
getDocumentation: {
integration: new HttpIntegration(...),
},
},
});

Los constructos de API CDK generados admiten dos patrones de integración:

  • isolated crea una función Lambda por operación. Este es el valor por defecto para las APIs generadas.
  • shared crea un único router Lambda por defecto y lo reutiliza para cada operación a menos que sobrescribas integraciones específicas.

isolated te brinda permisos y configuración más granulares por operación. shared reduce la proliferación de funciones Lambda e integraciones de API Gateway mientras aún permite sobrescritas selectivas.

Por ejemplo, establecer pattern a 'shared' crea una única función en lugar de una por integración:

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

Puedes otorgar acceso a tu API de la siguiente manera:

api.grantInvokeAccess(myIdentityPool.authenticatedRole);

El generador configura automáticamente un objetivo bundle que utiliza Rolldown para crear un paquete de despliegue:

Terminal window
pnpm nx bundle <project-name>

La configuración de Rolldown se encuentra en rolldown.config.ts, con una entrada por cada bundle a generar. Rolldown gestiona la creación de múltiples bundles en paralelo si están definidos.

Puedes usar el objetivo serve para ejecutar un servidor local para tu API, por ejemplo:

Terminal window
pnpm nx serve my-api

El punto de entrada para el servidor local es src/local-server.ts.

Esto recargará automáticamente cuando realices cambios en tu API.

Puedes crear un cliente tRPC para invocar tu API de manera type-safe. Si estás llamando a tu API tRPC desde otro backend, puedes usar el cliente en src/client/index.ts, por ejemplo:

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 estás llamando a tu API desde un sitio web React, considera usar el generador Connection para configurar el cliente.

Para más información sobre tRPC, consulta la documentación de tRPC.

Usa el generador connection para integrar este proyecto con otros en tu workspace. Las siguientes conexiones involucran este proyecto:

tRPC
React a tRPCLlama a una API tRPC desde un sitio web React
tRPCAmazon Aurora
API tRPC a Base de Datos RelacionalConecta una API tRPC a una base de datos relacional Aurora
tRPCAmazon DynamoDB
API tRPC a TypeScript DynamoDBConecta una API tRPC a una tabla DynamoDB