Aller au contenu

API Smithy vers Base de Données Relationnelle

Le générateur connection relie une API Smithy à un projet de Base de Données Relationnelle, en injectant un client Prisma dans le contexte du service afin que toutes les implémentations d’opérations puissent accéder à la base de données.

Avant d’utiliser ce générateur, assurez-vous d’avoir :

  1. Un projet API TypeScript Smithy (généré avec ts#api en utilisant --framework=smithy)
  2. Un projet ts#rdb
  1. Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
  2. Ouvrez la console Nx dans VSCode
  3. Cliquez sur Generate (UI) dans la section "Common Nx Commands"
  4. Recherchez @aws/nx-plugin - connection
  5. Remplissez les paramètres requis
    • Cliquez sur Generate

    Sélectionnez votre projet backend d’API Smithy comme source et votre projet de base de données relationnelle comme cible.

    ParamètreTypePar défautDescription
    sourceProject Requisstring-Le projet source
    targetProject Requisstring-Le projet cible auquel se connecter
    sourceComponent string-Le composant source depuis lequel se connecter (nom du composant, chemin relatif à la racine du projet source, ou identifiant du générateur). Utilisez '.' pour sélectionner explicitement le projet comme source.
    targetComponent string-Le composant cible auquel se connecter (nom du composant, chemin relatif à la racine du projet cible, ou identifiant du générateur). Utilisez '.' pour sélectionner explicitement le projet comme cible.
    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 modifie trois fichiers existants dans votre backend d’API Smithy :

    • Répertoirepackages/api/src
      • context.ts propriété db ajoutée à ServiceContext
      • handler.ts Client Prisma créé dans lambdaHandler, passé à serviceHandler.handle
      • local-server.ts Client Prisma créé dans le gestionnaire de requêtes, passé à serviceHandler.handle

    De plus, il met à jour la cible serve-local de l’API pour démarrer automatiquement la base de données.

    Le générateur ajoute une propriété db typée à ServiceContext dans 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>>;
    }

    Le client Prisma est instancié dans lambdaHandler et passé via le contexte du service :

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

    Accédez à db depuis le contexte dans vos implémentations d’opérations :

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

    Exécuter à nouveau le générateur avec une cible différente ajoute la deuxième base de données à côté de la première. Les deux clients sont ajoutés à ServiceContext et instanciés dans 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,
    });

    Pour permettre à votre API de se connecter à la base de données lors de l’exécution, les fonctions Lambda de l’API doivent être déployées dans le même VPC que la base de données et bénéficier d’un accès réseau et IAM.

    Dans votre stack d’application, déployez l’API dans le même VPC que la base de données, puis appelez allowDefaultPortFrom et grantConnect pour ouvrir le chemin réseau et accorder la permission IAM rds-db:connect à chaque gestionnaire 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);
    });

    Déployez les fonctions Lambda de l’API dans un sous-réseau privé avec sortie, et non dans un sous-réseau privé isolé. Lors de l’exécution, getPrisma() récupère les détails de connexion à la base de données depuis AWS AppConfig, qui est un point de terminaison de service AWS public nécessitant un accès Internet sortant.

    Pour les connexions directes au cluster Aurora depuis les runtimes Lambda Node.js 20 ou ultérieurs, chargez le bundle CA Amazon RDS en définissant NODE_EXTRA_CA_CERTS :

    packages/infra/src/stacks/application-stack.ts
    const api = new Api(this, 'Api', {
    integrations: Api.defaultIntegrations(this)
    .withDefaultOptions({
    environment: {
    NODE_EXTRA_CA_CERTS: '/var/runtime/ca-cert.pem',
    },
    })
    .build(),
    });

    Pour plus de détails, consultez les exigences SSL/TLS pour les connexions Amazon RDS d’AWS Lambda et la documentation TLS d’Amazon RDS Proxy. Lors de l’utilisation de RDS Proxy, vous n’avez pas besoin de configurer le bundle CA RDS dans votre fonction Lambda.

    Le générateur applique la même injection de client Prisma dans le gestionnaire de requêtes dans 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 dev <api-project-name>

    Cela démarre à la fois l’API et la base de données locale. La variable d’environnement LOCAL_DEV=true est définie automatiquement, de sorte que le client Prisma se connecte à la base de données Docker locale au lieu d’Aurora.