Aller au contenu

API tRPC vers Base de Données Relationnelle

Le générateur connection connecte une API tRPC à un projet de Base de Données Relationnelle, en générant un plugin middleware tRPC type-safe qui rend un client Prisma disponible dans le contexte de vos procédures.

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

  1. Un projet ts#trpc-api
  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 d’API tRPC comme source et votre projet de base de données relationnelle comme cible.

    Paramètre Type Par défaut Description
    sourceProject Requis string - Le projet source
    targetProject Requis string - 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.

    Le générateur crée un fichier middleware dans votre projet d’API tRPC :

    • Répertoirepackages/api/src
      • Répertoiremiddleware
        • <db-name>.ts Plugin tRPC exposant le client Prisma dans le contexte de procédure

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

    Ajoutez le plugin généré à votre routeur tRPC afin que toutes les procédures qui l’utilisent aient accès à la base de données :

    packages/api/src/router.ts
    import { t } from './init.js';
    import { createMyDbPlugin } from './middleware/my-db.js';
    export const authenticatedProcedure = t.procedure
    .concat(createMyDbPlugin());

    Accéder à la Base de Données dans les Procédures

    Section intitulée « Accéder à la Base de Données dans les Procédures »

    Le plugin fusionne IMyDbContext dans le contexte de votre procédure, rendant myDb disponible en tant que propriété optionnelle :

    packages/api/src/procedures/users.ts
    import { z } from 'zod';
    import { authenticatedProcedure } from '../router.js';
    export const listUsers = authenticatedProcedure
    .output(z.array(z.object({ id: z.string(), name: z.string() })))
    .query(async ({ ctx }) => {
    // ctx.myDb est le client Prisma — typé comme Awaited<ReturnType<typeof getPrisma>>
    return await ctx.myDb!.user.findMany();
    });

    Lorsque la base de données cible utilise le moteur MySQL, le middleware généré enveloppe opts.next() dans un bloc try/finally qui appelle $disconnect() :

    packages/api/src/middleware/my-db.ts
    return t.procedure.use(async (opts) => {
    const myDb = await getPrisma();
    try {
    return await opts.next({ ctx: { ...opts.ctx, myDb } });
    } finally {
    await myDb.$disconnect();
    }
    });

    Cela résout le problème de l’adaptateur MySQL qui maintient la boucle d’événements Node.js ouverte après une requête, ce qui empêcherait autrement Lambda de vider les réponses en streaming. La déconnexion dans finally libère la boucle d’événements afin que la réponse puisse se terminer. Voir MySQL : Mode Streaming API Gateway pour plus de détails.

    PostgreSQL ne nécessite pas cela — son adaptateur utilise un pool de connexions configuré avec allowExitOnIdle: true.

    Vous pouvez connecter des bases de données supplémentaires en exécutant à nouveau le générateur avec une cible différente. Chaque base de données obtient son propre plugin et interface de contexte :

    packages/api/src/router.ts
    export const dbProcedure = t.procedure
    .concat(createPostgresDbPlugin())
    .concat(createMySqlDbPlugin());

    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.

    Le générateur configure la cible serve-local de votre API tRPC pour dépendre de la cible serve-local de la base de données, donc l’exécution de :

    Terminal window
    pnpm nx serve-local <api-project-name>

    démarrera automatiquement la base de données locale en même temps que votre API.