Zoeken op website

typescript - Drizzle ORM: Hoe genereer je "CREATE TABLE" SQL vanuit een pgTable-definitie?


Is er een ingebouwde manier in Drizzle ORM om de CREATE TABLE IF NOT EXISTS SQL direct te genereren vanuit de pgTable schema-definitie?

Hier is een voorbeeld van hoe ik momenteel een in-memory pglite-database instel voor testen:

import {lower} from '@/drizzle/db.utils';
import {pgTable, serial, text, timestamp, uniqueIndex} from 'drizzle-orm/pg-core';

export const emailSubmissionTable = pgTable(
  'email_submission',
  {
    id: serial('id').primaryKey(),
    name: text('name').notNull(),
    email: text('email').notNull().unique(),
    createdAt: timestamp('created_at').notNull().defaultNow(),
  },
  (table) => [uniqueIndex('unique_email_idx').on(lower(table.email))]
);

En hier is hoe ik momenteel de in-memory database opzet:

import {emailSubmissionTable} from '@/drizzle/db';
import {PGlite} from '@electric-sql/pglite';
import {drizzle} from 'drizzle-orm/pglite';
import {beforeAll, describe} from 'vitest';

describe('Email Submission', () => {
  let db;

  beforeAll(async () => {
    const inMemoryDbClient = new PGlite();

    await inMemoryDbClient.exec(`
          CREATE TABLE IF NOT EXISTS "email_submission" (
            "id" serial PRIMARY KEY NOT NULL,
            "name" text NOT NULL,
            "email" text NOT NULL,
            "created_at" timestamp DEFAULT now() NOT NULL,
            CONSTRAINT "email_submission_email_unique" UNIQUE("email")
          );
      `);

    db = drizzle(inMemoryDbClient, {schema: {emailSubmissionTable}});
  });

  // ...
});

Probleem :

Ik wil automatisch de ruwe CREATE TABLE IF NOT EXISTS SQL-string genereren vanuit de pgTable schema-definitie, die er als volgt uit zou zien:

CREATE TABLE IF NOT EXISTS "email_submission" (
  "id" serial PRIMARY KEY NOT NULL,
  "name" text NOT NULL,
  "email" text NOT NULL,
  "created_at" timestamp DEFAULT now() NOT NULL,
  CONSTRAINT "unique_email_idx" UNIQUE("email")
);

De reden dat ik dit nodig heb, is zodat ik de gegenereerde SQL-string kan doorgeven aan de in-memory database om mijn queries te testen. Ik gebruik diffTestSchemas uit de Drizzle ORM-repository om dit te genereren, maar het is niet handig.

Hoe het eruit zou kunnen zien:

Idealiter zou Drizzle ORM een hulpfunctie bieden die direct de CREATE TABLE IF NOT EXISTS SQL kan genereren vanuit de pgTable schema-definitie, zoiets als dit:

import {generateCreateTableSQL} from 'drizzle-orm/utils'; // Hypothetical helper function

const inMemoryDbClient = new PGlite();

// Generate the SQL string from the pgTable schema
const sql = generateCreateTableSQL(emailSubmissionTable);

// Execute the generated SQL in the in-memory database
await inMemoryDbClient.exec(sql);

// Log the SQL string to see it
console.log(sql);
/* Output:
  CREATE TABLE IF NOT EXISTS "email_submission" (
    "id" serial PRIMARY KEY NOT NULL,
    "name" text NOT NULL,
    "email" text NOT NULL,
    "created_at" timestamp DEFAULT now() NOT NULL,
    CONSTRAINT "unique_email_idx" UNIQUE("email")
  );
*/

Dit zou het veel gemakkelijker maken om de SQL-string direct uit de schema-definitie te genereren om mijn queries, selecties, invoegingen, enz. te testen.

Antwoord #1

Ik had een soortgelijk probleem, dat ik heb opgelost door gebruik te maken van de programmeerbare API van drizzle-kit om mijn schema naar de testdatabase te pushen — specifiek de pushSchema-functie.

Gebaseerd op jouw bovenstaande testgeval, zou jouw testgeval er in plaats daarvan zo uit kunnen zien (niet rechtstreeks getest):

import {emailSubmissionTable} from '@/drizzle/db';
import {PGlite} from '@electric-sql/pglite';
import {drizzle} from 'drizzle-orm/pglite';
import {pushSchema} from 'drizzle-kit/api'; // New import
import {beforeAll, describe} from 'vitest';

describe('Email Submission', () => {
  let db;

  beforeAll(async () => {
    const inMemoryDbClient = new PGlite();
    const schema = {emailSubmissionTable}; // Re-usable schema definition.

    // Create a separate, inline Drizzle instance without the schema.
    const { apply } = await pushSchema(schema, drizzle(inMemoryDbClient));
    await apply();

    db = drizzle(inMemoryDbClient, {schema});
  });

  // ...
});

Als je de SQL-verklaringen echt als strings wilt hebben om te inspecteren of handmatig toe te passen, kun je ook het volgende doen:

import {sql} from 'drizzle-orm';

// ...

const { statementsToExecute } = await pushSchema(schema, drizzle(inMemoryDbClient));
for (const statement of statementsToExecute) {
  console.log(statement)
  await db.execute(sql.raw(statement))
}

// ...

Aanvullende notities

Niet verplicht, maar over het algemeen zou ik sterk aanbevelen om voor het testen dezelfde database-implementatie te gebruiken als die je in productie zult gebruiken. Als je ook PGlite in productie gebruikt, geweldig! Je hoeft niets te veranderen.

Anders wilt u misschien overwegen om over te schakelen naar iets zoals @testcontainers/postgresql om een gecontaineriseerde versie van uw productie-database-implementatie te gebruiken (bijv. postgres:16-alpine). Op die manier worden eventuele potentiële verschillen die zouden kunnen bestaan (d.w.z. tussen de PGlite Postgres-implementatie en uw productie-Postgres-implementatie) volledig geëlimineerd.