From c5ea0f2c9c35776ff0b7af0fa245c62a72f59c66 Mon Sep 17 00:00:00 2001 From: Zoey Kaiser Date: Fri, 25 Oct 2024 22:23:40 +0200 Subject: [PATCH 1/7] Updated Prisma config to include a local development database --- src/configs/prisma.ts | 116 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 111 insertions(+), 5 deletions(-) diff --git a/src/configs/prisma.ts b/src/configs/prisma.ts index 0c012f6..f801e3a 100644 --- a/src/configs/prisma.ts +++ b/src/configs/prisma.ts @@ -1,5 +1,6 @@ import { generateModuleHTMLComponent, generateModuleHTMLSnippet } from '../generators/generateModuleComponents' import type { ModuleConfig } from '../types' +import { getUserPkgManager } from '../utils/getUserPkgManager' const prismaRootSchema = `// This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema @@ -10,11 +11,14 @@ generator client { } datasource db { - // NOTE: You probably want to change this to another database later on - provider = "sqlite" + provider = "postgres" - // This value is read from the .env file. - url = env("DATABASE_URL") + // These \`env(..)\` value will be read from the \`.env\` file or from the environment. + url = env("DATABASE_URL") + directUrl = env("DIRECT_DATABASE_URL") + + // This is required for development only. + shadowDatabaseUrl = "postgres://postgres@localhost/prisma-shadow?pgbouncer=true&connection_limit=1" } ` @@ -86,6 +90,84 @@ export function resetDatabase(databaseUrl?: string) { } ` +const pglite = `/** + * Script that starts a postgres database using pg-gateway (https://github.com/supabase-community/pg-gateway) and pglite (https://github.com/electric-sql/pglite). + * + * We use this database for local development with prisma ORM. The script also supports creating a \`shadow-database\`, which is a second, separate database + * that prisma uses for certain commands, such as \`pnpm prisma migrate dev\`: https://www.prisma.io/docs/orm/prisma-migrate/understanding-prisma-migrate/shadow-database. + * + * To make use of the shadow-database add \`/prisma-shadow\` to the DSN you provide. This script will then spin up a second, in-memory-only database and connect you to it. + * + * This whole script approach is novel to us (before we used sqlite locally). Here is the PR that brought it all together: https://github.com/sidestream-tech/hanselmann-os/pull/3356 + */ +import net from 'node:net' +import { unlinkSync, writeFileSync } from 'node:fs' +import { PGlite } from '@electric-sql/pglite' +import { fromNodeSocket } from 'pg-gateway/node' +import { join } from 'pathe' + +// If env var is set, we write a file to disk once the server is done starting up. This file can then be used by other processes to check whether the database is ready +const doWriteHealthFile = process.env.WRITE_HEALTH_FILE === 'true' +const HEALTH_FILE_NAME = 'pgliteHealthz' + +const db = new PGlite({ dataDir: join(import.meta.dirname, 'pglite-data') }) +let activeDb = db + +const server = net.createServer(async (socket) => { + activeDb = db + + console.info(\`Client connected: \${socket.remoteAddress}:\${socket.remotePort}\`) + await fromNodeSocket(socket, { + serverVersion: '16.3', + + auth: { + // No password required + method: 'trust', + }, + + async onStartup({ clientParams }) { + // create a temp in-memory instance if connecting to the prisma shadow DB + if (clientParams?.database === 'prisma-shadow') { + console.info('Connecting client to shadow database') + activeDb = new PGlite() + } + + // Wait for PGlite to be ready before further processing + await activeDb.waitReady + }, + + // Hook into each client message + async onMessage(data, { isAuthenticated }) { + // Only forward messages to PGlite after authentication + if (!isAuthenticated) { + return + } + + // Forward raw message to PGlite and send response to client + return await activeDb.execProtocolRaw(data) + }, + }) + + socket.on('end', () => { + logger.info('Client disconnected') + }) +}) + +server.listen(5432, () => { + if (doWriteHealthFile) { + writeFileSync(HEALTH_FILE_NAME, '') + } + + logger.info('Server listening on port 5432') +}) + +server.on('close', () => { + if (doWriteHealthFile) { + unlinkSync(HEALTH_FILE_NAME) + } +}) +` + const prismaDemoComponent = ` @@ -106,7 +188,12 @@ const { data: examples } = useFetch('/api/examples') const prisma: ModuleConfig = { humanReadableName: 'Prisma ORM', description: 'Next-generation Node.js and TypeScript ORM. See more: https://www.prisma.io/', - scripts: [], + scripts: [ + { + name: 'db', + command: 'vite-node prisma/pglite.ts', + } + ], dependencies: [ { name: 'prisma', @@ -117,6 +204,21 @@ const prisma: ModuleConfig = { name: '@prisma/client', version: '^5.18.0', isDev: false + }, + { + name: '@electric-sql/pglite', + version: '^0.2.9', + isDev: true, + }, + { + name: 'pg-gateway', + version: '0.3.0-beta.3', + isDev: true, + }, + { + name: 'vite-node', + version: '^2.1.1', + isDev: true, } ], nuxtConfig: {}, @@ -141,9 +243,13 @@ const prisma: ModuleConfig = { }, { path: 'components/Welcome/PrismaDemo.vue', content: prismaDemoComponent, + }, { + path: 'prisma/pglite.ts', + content: pglite, }], tasksPostInstall: [ '- [ ] Prisma: Edit your `prisma/prisma.schema` to your liking', + `- [ ] Prisma: Start your local postgres database using \`${getUserPkgManager()} run db\``, '- [ ] Prisma: Run `npx prisma db push` to sync the schema to your database & generate the Prisma Client', ], indexVue: generateModuleHTMLSnippet('WelcomePrismaDemo'), From 1bc88e9d0bfd2149a07335c57e8f6b8632d46175 Mon Sep 17 00:00:00 2001 From: Zoey Kaiser Date: Fri, 25 Oct 2024 22:24:37 +0200 Subject: [PATCH 2/7] Updated demo prisma environment file --- src/configs/prisma.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/configs/prisma.ts b/src/configs/prisma.ts index f801e3a..dadcb75 100644 --- a/src/configs/prisma.ts +++ b/src/configs/prisma.ts @@ -29,7 +29,8 @@ const prismaExampleSchema = `model Example { ` const prismaEnvFile = `# Prisma -DATABASE_URL=file:./db.sqlite +DATABASE_URL="postgres://postgres@localhost:5432/postgres?pgbouncer=true&connection_limit=1" +DIRECT_DATABASE_URL="postgres://postgres@localhost:5432/postgres?connection_limit=1" ` const prismaExampleEndpoint = `/** From 64960194d80dd898e4388bde17c913bcbe316f2c Mon Sep 17 00:00:00 2001 From: Zoey Kaiser Date: Fri, 25 Oct 2024 22:27:27 +0200 Subject: [PATCH 3/7] Added prisma todo to add local data to gitignore --- src/configs/prisma.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/configs/prisma.ts b/src/configs/prisma.ts index dadcb75..5083653 100644 --- a/src/configs/prisma.ts +++ b/src/configs/prisma.ts @@ -252,6 +252,7 @@ const prisma: ModuleConfig = { '- [ ] Prisma: Edit your `prisma/prisma.schema` to your liking', `- [ ] Prisma: Start your local postgres database using \`${getUserPkgManager()} run db\``, '- [ ] Prisma: Run `npx prisma db push` to sync the schema to your database & generate the Prisma Client', + '- [ ] Prisma: Add `**/*/pglite-data` and `pgliteHealthz` to your `.gitignore` file' ], indexVue: generateModuleHTMLSnippet('WelcomePrismaDemo'), } From ba24732b94e7f7750000bd3611bc0fd0bd03436f Mon Sep 17 00:00:00 2001 From: Zoey Kaiser Date: Fri, 25 Oct 2024 22:28:30 +0200 Subject: [PATCH 4/7] fix: logger to console instance --- src/configs/prisma.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/configs/prisma.ts b/src/configs/prisma.ts index 5083653..9b3cca5 100644 --- a/src/configs/prisma.ts +++ b/src/configs/prisma.ts @@ -150,7 +150,7 @@ const server = net.createServer(async (socket) => { }) socket.on('end', () => { - logger.info('Client disconnected') + console.info('Client disconnected') }) }) @@ -159,7 +159,7 @@ server.listen(5432, () => { writeFileSync(HEALTH_FILE_NAME, '') } - logger.info('Server listening on port 5432') + console.info('Server listening on port 5432') }) server.on('close', () => { From 3dc91abc0d8179b0557bf3a845587e2857b8c62d Mon Sep 17 00:00:00 2001 From: Zoey Kaiser Date: Fri, 25 Oct 2024 22:32:54 +0200 Subject: [PATCH 5/7] Added a comment about DIRECT_DATABASE_URL --- src/configs/prisma.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/configs/prisma.ts b/src/configs/prisma.ts index 9b3cca5..f22f411 100644 --- a/src/configs/prisma.ts +++ b/src/configs/prisma.ts @@ -13,8 +13,9 @@ generator client { datasource db { provider = "postgres" - // These \`env(..)\` value will be read from the \`.env\` file or from the environment. url = env("DATABASE_URL") + + // This environment variable can be the same as \`DATABASE_URL\` for non-pglite environments directUrl = env("DIRECT_DATABASE_URL") // This is required for development only. From 6764e17cc21c219e7976726bf5e5c772333434b9 Mon Sep 17 00:00:00 2001 From: Zoey Kaiser Date: Fri, 25 Oct 2024 22:38:11 +0200 Subject: [PATCH 6/7] fix: ci pipeline to start postgres db for testing --- .github/workflows/ci.yaml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 03d80bc..9bd8a10 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -37,6 +37,23 @@ jobs: testCli: runs-on: ubuntu-latest + services: + postgres: + image: postgres + ports: + - 5432:5432 + env: + POSTGRES_PASSWORD: postgres + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + env: + DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432' + DIRECT_DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432' + AUTH_ORIGIN: http://localhost:3000 + AUTH_SECRET: test123 steps: - uses: actions/checkout@v4 @@ -74,7 +91,7 @@ jobs: # start prod-app - name: app:run in prod - run: "export AUTH_ORIGIN=http://localhost:3000 && export AUTH_SECRET=test123 && cd my-sidebase-app && npm run build && timeout 30 npm run preview || ( [[ $? -eq 124 ]] && echo \"app started and did not exit within first 30 seconds, thats good\" )" + run: "cd my-sidebase-app && npm run build && timeout 30 npm run preview || ( [[ $? -eq 124 ]] && echo \"app started and did not exit within first 30 seconds, thats good\" )" # start dev-app and curl from it - name: app:test in prod From e8ecc54a141a716cb5cea33fcf17084713e337ce Mon Sep 17 00:00:00 2001 From: Zoey Kaiser Date: Fri, 25 Oct 2024 22:40:44 +0200 Subject: [PATCH 7/7] fix: goodbye message to include starting local postgres db --- src/messages/goodbye.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/messages/goodbye.ts b/src/messages/goodbye.ts index c4e4dbd..42ca3ce 100644 --- a/src/messages/goodbye.ts +++ b/src/messages/goodbye.ts @@ -30,6 +30,7 @@ export function sayGoodbye(preferences: Preferences) { } if (preferences.addModules?.includes('prisma') || preferences.setStack === 'cheviot') { + sayCommand(`${packageManager} run db`, 'Start the local postgres database in a new window') sayCommand('npx prisma db push', 'Initialize the database & Prisma client') }