Skip to content

Commit

Permalink
Migrate from Prisma to Drizzle
Browse files Browse the repository at this point in the history
  • Loading branch information
wKovacs64 committed Mar 5, 2024
1 parent eb2af5d commit 06cd6fb
Show file tree
Hide file tree
Showing 21 changed files with 1,298 additions and 165 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ CONTENTFUL_ACCESS_TOKEN=
CONTENTFUL_URL=
CONTENTFUL_PREVIEW=true
CONTENTFUL_WEBHOOK_TOKEN=
DATABASE_URL=file:./cache.db
DATABASE_FILE_PATH="./app/db.server/cache.db"
SITE_IMAGE_URL=
SITE_IMAGE_ALT=
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -247,9 +247,9 @@ $RECYCLE.BIN/
/build
.env

# Prisma
/prisma/*.db
/prisma/*.db-journal
# Drizzle
/app/db.server/*.db
/app/db.server/*.db-*

# Generated SVG icons sprite and component
/app/icons
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# .gitignore and...

package*.json
**/*.typegen.ts
app/db.server/meta/*.json
9 changes: 2 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# base node image
FROM node:20-bullseye-slim as base

# Install openssl for Prisma
RUN apt-get update && apt-get install -y openssl fuse3 ca-certificates
# Install fuse3 and ca-certificates for litefs
RUN apt-get update && apt-get install -y fuse3 ca-certificates

ENV NODE_ENV production

Expand Down Expand Up @@ -33,9 +33,6 @@ WORKDIR /app

COPY --from=deps /app/node_modules /app/node_modules

ADD prisma .
RUN npx prisma generate

ADD . .
RUN npm run build

Expand All @@ -47,14 +44,12 @@ ENV NODE_ENV="production"
WORKDIR /app

COPY --from=production-deps /app/node_modules /app/node_modules
COPY --from=build /app/node_modules/.prisma /app/node_modules/.prisma
COPY --from=build /app/build/server /app/build/server
COPY --from=build /app/build/client /app/build/client

# prepare litefs
ENV LITEFS_DIR="/litefs"
ENV DATABASE_FILE_PATH="$LITEFS_DIR/cache.db"
ENV DATABASE_URL="file:$DATABASE_FILE_PATH"
ENV INTERNAL_PORT="8080"
ENV PORT="8081"
COPY --from=flyio/litefs:0.5 /usr/local/bin/litefs /usr/local/bin/litefs
Expand Down
14 changes: 14 additions & 0 deletions app/db.server/0000_thick_korvac.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-- Migrating from Prisma
DROP TABLE IF EXISTS `_prisma_migrations`;
--> statement-breakpoint
DROP TABLE IF EXISTS `CacheEntry`;
--> statement-breakpoint
CREATE TABLE `cacheEntry` (
`id` integer PRIMARY KEY NOT NULL,
`createdAt` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
`updatedAt` text NOT NULL,
`key` text NOT NULL,
`value` text NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX `cacheEntry_key_unique` ON `cacheEntry` (`key`);
14 changes: 14 additions & 0 deletions app/db.server/drizzle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { resolve } from 'pathe';
import { drizzle } from 'drizzle-orm/better-sqlite3';
import Database from 'better-sqlite3';
import { getEnvVars } from '~/utils/env.server';
import * as schema from './schema';

const { DATABASE_FILE_PATH } = getEnvVars();

export const betterSqlite = new Database(resolve(DATABASE_FILE_PATH));
betterSqlite.pragma('journal_mode = WAL');

export const db = drizzle(betterSqlite, { schema });

export * from './schema';
67 changes: 67 additions & 0 deletions app/db.server/meta/0000_snapshot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"version": "5",
"dialect": "sqlite",
"id": "0433b681-285e-4166-8dde-28c23948ca32",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"cacheEntry": {
"name": "cacheEntry",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP"
},
"updatedAt": {
"name": "updatedAt",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"key": {
"name": "key",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"value": {
"name": "value",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"cacheEntry_key_unique": {
"name": "cacheEntry_key_unique",
"columns": [
"key"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
}
}
13 changes: 13 additions & 0 deletions app/db.server/meta/_journal.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": "5",
"dialect": "sqlite",
"entries": [
{
"idx": 0,
"version": "5",
"when": 1709622306297,
"tag": "0000_thick_korvac",
"breakpoints": true
}
]
}
7 changes: 7 additions & 0 deletions app/db.server/migrate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import 'dotenv/config';
import { migrate } from 'drizzle-orm/better-sqlite3/migrator';
import { betterSqlite, db } from './drizzle';

migrate(db, { migrationsFolder: './app/db.server' });

betterSqlite.close();
14 changes: 14 additions & 0 deletions app/db.server/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
import { sql } from 'drizzle-orm';

// Note: CURRENT_TIMESTAMP will be in GMT.

export const cacheEntry = sqliteTable('cacheEntry', {
id: integer('id').primaryKey(),
createdAt: text('createdAt')
.default(sql`CURRENT_TIMESTAMP`)
.notNull(),
updatedAt: text('updatedAt').notNull(),
key: text('key').unique().notNull(),
value: text('value').notNull(),
});
4 changes: 2 additions & 2 deletions app/routes/[_].healthcheck.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { LoaderFunctionArgs } from '@remix-run/node';
import { prisma } from '~/utils/db.server';
import { db, cacheEntry } from '~/db.server/drizzle';

export async function loader({ request }: LoaderFunctionArgs) {
const host =
Expand All @@ -8,7 +8,7 @@ export async function loader({ request }: LoaderFunctionArgs) {

try {
await Promise.all([
prisma.cacheEntry.count(),
db.select({ id: cacheEntry.id }).from(cacheEntry).limit(1),
fetch(url.toString(), {
method: 'HEAD',
headers: { 'x-from-healthcheck': 'true' },
Expand Down
23 changes: 16 additions & 7 deletions app/utils/cache.server.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { prisma } from '~/utils/db.server';
import { count, eq, sql } from 'drizzle-orm';
import { db, cacheEntry } from '~/db.server/drizzle';

export const cache = {
async get(key: string) {
const cachedData = await prisma.cacheEntry.findUnique({ where: { key } });
const cachedData = await db.query.cacheEntry.findFirst({
where: eq(cacheEntry.key, key),
});
if (cachedData) {
try {
return JSON.parse(cachedData.value);
Expand All @@ -13,30 +16,36 @@ export const cache = {
},
async set(key: string, value: Parameters<typeof JSON.stringify>[0]) {
try {
await prisma.cacheEntry.create({
data: { key, value: JSON.stringify(value) },
await db.insert(cacheEntry).values({
key,
value: JSON.stringify(value),
updatedAt: sql`CURRENT_TIMESTAMP`,
});
} catch {
// noop, cache failures shouldn't break the app
}
},
async has(key: string) {
try {
return (await prisma.cacheEntry.count({ where: { key } })) > 0;
const [{ count: cacheEntryCount }] = await db
.select({ count: count() })
.from(cacheEntry)
.where(eq(cacheEntry.key, key));
return cacheEntryCount > 0;
} catch {
return false;
}
},
async delete(key: string) {
try {
await prisma.cacheEntry.delete({ where: { key } });
await db.delete(cacheEntry).where(eq(cacheEntry.key, key));
} catch {
// noop, cache failures shouldn't break the app
}
},
async clear() {
try {
await prisma.cacheEntry.deleteMany();
await db.delete(cacheEntry);
} catch {
// noop, cache failures shouldn't break the app
}
Expand Down
5 changes: 0 additions & 5 deletions app/utils/db.server.ts

This file was deleted.

1 change: 1 addition & 0 deletions app/utils/env.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const envSchema = z.object({
CONTENTFUL_URL: z.string().min(1),
CONTENTFUL_PREVIEW: z.string().optional(),
CONTENTFUL_WEBHOOK_TOKEN: z.string().min(1),
DATABASE_FILE_PATH: z.string().min(1),
SITE_IMAGE_URL: z.string().min(1),
SITE_IMAGE_ALT: z.string().min(1),
});
Expand Down
18 changes: 18 additions & 0 deletions drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// This config file is only used for drizzle-kit commands, AFAIK.
import 'dotenv/config';
import { resolve } from 'pathe';
import type { Config } from 'drizzle-kit';
import { getEnvVars } from '~/utils/env.server';

const { DATABASE_FILE_PATH } = getEnvVars();

export default {
driver: 'better-sqlite',
out: './app/db.server',
schema: [resolve('./app/db.server/schema.ts')],
dbCredentials: { url: DATABASE_FILE_PATH },
// Print all statements
verbose: true,
// Always ask for confirmation
strict: true,
} satisfies Config;
2 changes: 1 addition & 1 deletion litefs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ exit-on-error: false
exec:
# Only run migrations on candidate nodes. Combined with `promote: true` above,
# this ensures that migrations are only run on the primary node.
- cmd: npx prisma migrate deploy
- cmd: npm run setup
if-candidate: true

# Then run the application on all nodes.
Expand Down
Loading

0 comments on commit 06cd6fb

Please sign in to comment.