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.
