Ir al contenido

API Smithy a Base de Datos Relacional

El generador connection conecta una API Smithy a un proyecto de Base de Datos Relacional, inyectando un cliente Prisma en el contexto del servicio para que todas las implementaciones de operaciones puedan acceder a la base de datos.

Antes de usar este generador, asegúrate de tener:

  1. Un proyecto ts#smithy-api (backend TypeScript)
  2. Un proyecto ts#rdb
  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 - connection
  5. Complete los parámetros requeridos
    • Haga clic en Generate

    Selecciona tu proyecto de backend de API Smithy como origen y tu proyecto de base de datos relacional como destino.

    Parámetro Tipo Predeterminado Descripción
    sourceProject Requerido string - El proyecto de origen
    targetProject Requerido string - El proyecto de destino al que conectar
    sourceComponent string - El componente de origen desde el que conectar (nombre del componente, ruta relativa a la raíz del proyecto de origen, o id del generador). Use '.' para seleccionar explícitamente el proyecto como origen.
    targetComponent string - El componente de destino al que conectar (nombre del componente, ruta relativa a la raíz del proyecto de destino, o id del generador). Use '.' para seleccionar explícitamente el proyecto como destino.

    El generador modifica tres archivos existentes en tu backend de API Smithy:

    • Directoriopackages/api/src
      • context.ts propiedad db agregada a ServiceContext
      • handler.ts Cliente Prisma creado dentro de lambdaHandler, pasado a serviceHandler.handle
      • local-server.ts Cliente Prisma creado dentro del manejador de solicitudes, pasado a serviceHandler.handle

    Además, actualiza el target serve-local de la API para iniciar la base de datos automáticamente.

    El generador agrega una propiedad db tipada a ServiceContext en context.ts:

    packages/api/src/context.ts
    import { getPrisma as getMyDb } from ':my-scope/my-db';
    export interface ServiceContext {
    tracer: Tracer;
    logger: Logger;
    metrics: Metrics;
    myDb: Awaited<ReturnType<typeof getMyDb>>;
    }

    El cliente Prisma se instancia dentro de lambdaHandler y se pasa a través del contexto del servicio:

    packages/api/src/handler.ts
    import { getPrisma as getMyDb } from ':my-scope/my-db';
    export const lambdaHandler = async (event: APIGatewayProxyEvent) => {
    const httpRequest = convertEvent(event);
    const myDb = await getMyDb();
    const httpResponse = await serviceHandler.handle(httpRequest, {
    tracer,
    logger,
    metrics,
    myDb,
    });
    return convertVersion1Response(httpResponse);
    };

    Accede a db desde el contexto en tus implementaciones de operaciones:

    packages/api/src/operations/list-users.ts
    import { ListUsersOperationInput, ListUsersOperationOutput } from '../generated/ssdk/index.js';
    import { ServiceContext } from '../context.js';
    export const listUsers = async (
    input: ListUsersOperationInput,
    ctx: ServiceContext,
    ): Promise<ListUsersOperationOutput> => {
    const users = await ctx.myDb.user.findMany();
    return { users };
    };

    Ejecutar el generador nuevamente con un destino diferente agrega la segunda base de datos junto a la primera. Ambos clientes se agregan a ServiceContext y se instancian en handler.ts:

    packages/api/src/context.ts
    export interface ServiceContext {
    tracer: Tracer;
    logger: Logger;
    metrics: Metrics;
    myDb: Awaited<ReturnType<typeof getMyDb>>;
    otherDb: Awaited<ReturnType<typeof getOtherDb>>;
    }
    packages/api/src/handler.ts
    const myDb = await getMyDb();
    const otherDb = await getOtherDb();
    const httpResponse = await serviceHandler.handle(httpRequest, {
    tracer,
    logger,
    metrics,
    myDb,
    otherDb,
    });

    Para permitir que tu API se conecte a la base de datos en tiempo de ejecución, las funciones Lambda de la API deben desplegarse en la misma VPC que la base de datos y se les debe otorgar acceso de red e IAM.

    En tu stack de aplicación, despliega la API en la misma VPC que la base de datos, luego llama a allowDefaultPortFrom y grantConnect para abrir la ruta de red y otorgar el permiso IAM rds-db:connect a cada manejador Lambda:

    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', { vpc, ... });
    const api = new MyApi(this, 'Api', {
    integrations: MyApi.defaultIntegrations(this)
    .withDefaultOptions({
    vpc,
    vpcSubnets: { subnetType: SubnetType.PRIVATE_WITH_EGRESS },
    })
    .build(),
    });
    Object.entries(api.integrations).forEach(([operation, integration]) => {
    db.allowDefaultPortFrom(integration.handler, `Allow ${operation} to connect to the database`);
    db.grantConnect(integration.handler);
    });

    Despliega las funciones Lambda de la API en una subred privada con salida, no en una subred privada aislada. En tiempo de ejecución, getPrisma() recupera los detalles de conexión de la base de datos desde AWS AppConfig, que es un endpoint de servicio público de AWS que requiere acceso a internet saliente.

    El generador aplica la misma inyección de cliente Prisma dentro del manejador de solicitudes en local-server.ts:

    packages/api/src/local-server.ts
    import { getPrisma as getMyDb } from ':my-scope/my-db';
    const server = createServer(async function (req, res) {
    const httpRequest = convertRequest(req);
    const myDb = await getMyDb();
    const httpResponse = await serviceHandler.handle(httpRequest, {
    tracer,
    logger,
    metrics,
    myDb,
    });
    return writeResponse(httpResponse, res);
    });
    Terminal window
    pnpm nx serve-local <api-project-name>

    Esto inicia tanto la API como la base de datos local. La variable de entorno SERVE_LOCAL=true se establece automáticamente, por lo que el cliente Prisma se conecta a la base de datos Docker local en lugar de Aurora.