move to drizzle instead of prisma

This commit is contained in:
JonLuca De Caro
2026-03-25 09:24:14 -07:00
parent e7686fd6d7
commit c1098561fb
14 changed files with 757 additions and 951 deletions

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
auto-install-peers=false

View File

@@ -4,7 +4,7 @@ Minimal Next.js web boilerplate with:
- Next.js 16 Pages Router - Next.js 16 Pages Router
- React 19 - React 19
- Prisma 7 + PostgreSQL - Drizzle + PostgreSQL
- `@t3-oss/env-nextjs` runtime env parsing - `@t3-oss/env-nextjs` runtime env parsing
- `oxfmt` and `oxlint` - `oxfmt` and `oxlint`
- `vinext` for local dev and Cloudflare-targeted builds - `vinext` for local dev and Cloudflare-targeted builds
@@ -14,7 +14,7 @@ Minimal Next.js web boilerplate with:
- Node.js 24+ - Node.js 24+
- `pnpm` 10+ - `pnpm` 10+
- PostgreSQL access if you plan to run Prisma queries - PostgreSQL access if you plan to run Drizzle queries
## Setup ## Setup
@@ -30,7 +30,7 @@ Create a local env file:
cp .env.example .env cp .env.example .env
``` ```
Sync the sample Prisma schema when your database is ready: Sync the sample Drizzle schema when your database is ready:
```bash ```bash
pnpm db:push pnpm db:push
@@ -54,14 +54,14 @@ pnpm dev
- `pnpm lint`: run Oxfmt checks and Oxlint - `pnpm lint`: run Oxfmt checks and Oxlint
- `pnpm format:write`: format supported files with Oxfmt - `pnpm format:write`: format supported files with Oxfmt
- `pnpm typecheck`: run TypeScript checks - `pnpm typecheck`: run TypeScript checks
- `pnpm db:push`: push the Prisma schema to your database - `pnpm db:push`: push the Drizzle schema to your database
- `pnpm db:studio`: open Prisma Studio - `pnpm db:studio`: open Drizzle Studio
## Project layout ## Project layout
- `src/env.ts`: runtime env validation and defaults - `src/env.ts`: runtime env validation and defaults
- `src/server/db.ts`: Prisma singleton using the Postgres adapter - `src/server/db.ts`: Drizzle client using postgres-js
- `prisma/schema.prisma`: sample `Post` model and Prisma client generator - `src/server/schema.ts`: sample `Post` table schema
- `wrangler.jsonc`: Cloudflare Workers runtime configuration - `wrangler.jsonc`: Cloudflare Workers runtime configuration
## Cloudflare ## Cloudflare

11
drizzle.config.ts Normal file
View File

@@ -0,0 +1,11 @@
import "dotenv/config";
import { defineConfig } from "drizzle-kit";
export default defineConfig({
schema: "./src/server/schema.ts",
out: "./drizzle",
dialect: "postgresql",
dbCredentials: {
url: process.env.DATABASE_URL ?? "",
},
});

View File

@@ -18,18 +18,16 @@
"format:check": "oxfmt --check .", "format:check": "oxfmt --check .",
"format:write": "oxfmt .", "format:write": "oxfmt .",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"db:push": "prisma db push", "db:push": "drizzle-kit push",
"db:studio": "prisma studio", "db:studio": "drizzle-kit studio"
"postinstall": "prisma generate"
}, },
"dependencies": { "dependencies": {
"@prisma/adapter-pg": "7.5.0",
"@prisma/client": "7.5.0",
"@t3-oss/env-nextjs": "^0.13.10", "@t3-oss/env-nextjs": "^0.13.10",
"babel-plugin-react-compiler": "^1.0.0", "babel-plugin-react-compiler": "^1.0.0",
"drizzle-orm": "^0.45.1",
"dotenv": "^17.3.1", "dotenv": "^17.3.1",
"next": "^16.2.0", "next": "^16.2.0",
"pg": "^8.20.0", "postgres": "^3.4.7",
"react": "^19.2.4", "react": "^19.2.4",
"react-dom": "^19.2.4", "react-dom": "^19.2.4",
"zod": "^4.3.6" "zod": "^4.3.6"
@@ -40,9 +38,9 @@
"@types/react": "^19.2.14", "@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1", "@vitejs/plugin-react": "^6.0.1",
"drizzle-kit": "^0.31.8",
"oxfmt": "^0.41.0", "oxfmt": "^0.41.0",
"oxlint": "^1.56.0", "oxlint": "^1.56.0",
"prisma": "^7.5.0",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vinext": "0.0.32", "vinext": "0.0.32",
"vite": "^8.0.1", "vite": "^8.0.1",

1559
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,5 @@
onlyBuiltDependencies: onlyBuiltDependencies:
- "@prisma/engines"
- esbuild - esbuild
- prisma
- sharp - sharp
- workerd - workerd
- wrangler - wrangler

View File

@@ -1,9 +0,0 @@
import "dotenv/config";
import { defineConfig } from "prisma/config";
export default defineConfig({
schema: "prisma/schema.prisma",
datasource: {
url: process.env.DATABASE_URL,
},
});

View File

@@ -1,25 +0,0 @@
generator client {
provider = "prisma-client"
output = "../generated/prisma"
moduleFormat = "esm"
generatedFileExtension = "ts"
importFileExtension = "ts"
engineType = "client"
}
datasource db {
provider = "postgresql"
}
model Post {
id String @id @default(cuid())
title String
slug String @unique
body String?
published Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([published, createdAt])
}

8
src/pages/api/posts.ts Normal file
View File

@@ -0,0 +1,8 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { listRecentPosts } from "~/server/posts";
export default async function handler(_request: NextApiRequest, response: NextApiResponse) {
const posts = await listRecentPosts();
response.status(200).json({ posts });
}

View File

@@ -13,11 +13,11 @@ export default function HomePage() {
<> <>
<Head> <Head>
<title>Vinext Boilerplate</title> <title>Vinext Boilerplate</title>
<meta content={"Minimal Next.js, Prisma, Oxc, Vinext, and Wrangler starter."} name={"description"} /> <meta content={"Minimal Next.js, Drizzle, Oxc, Vinext, and Wrangler starter."} name={"description"} />
</Head> </Head>
<main className={"page-shell"}> <main className={"page-shell"}>
<section className={"hero-card"}> <section className={"hero-card"}>
<p className={"eyebrow"}>Next.js + Prisma + Cloudflare</p> <p className={"eyebrow"}>Next.js + Drizzle + Cloudflare</p>
<h1>Vinext Boilerplate</h1> <h1>Vinext Boilerplate</h1>
<p className={"lead"}> <p className={"lead"}>
Minimal Pages Router starter built for local web development first and Cloudflare Workers deployment second. Minimal Pages Router starter built for local web development first and Cloudflare Workers deployment second.
@@ -25,7 +25,7 @@ export default function HomePage() {
<div className={"chip-row"}> <div className={"chip-row"}>
<span className={"chip"}>Next 16</span> <span className={"chip"}>Next 16</span>
<span className={"chip"}>React 19</span> <span className={"chip"}>React 19</span>
<span className={"chip"}>Prisma 7</span> <span className={"chip"}>Drizzle</span>
<span className={"chip"}>vinext</span> <span className={"chip"}>vinext</span>
<span className={"chip"}>wrangler</span> <span className={"chip"}>wrangler</span>
<span className={"chip"}>oxfmt + oxlint</span> <span className={"chip"}>oxfmt + oxlint</span>

View File

@@ -1,23 +1,22 @@
import { PrismaClient } from "@/db"; import { drizzle } from "drizzle-orm/postgres-js";
import { PrismaPg } from "@prisma/adapter-pg"; import postgres from "postgres";
import { env } from "~/env"; import { env } from "~/env";
import * as schema from "~/server/schema";
const adapter = new PrismaPg({ const createDb = () => {
connectionString: env.DATABASE_URL, const client = postgres(env.DATABASE_URL, {
}); prepare: false,
const createPrismaClient = () =>
new PrismaClient({
adapter,
log: env.NODE_ENV === "development" ? ["error", "warn"] : ["error"],
}); });
const globalForPrisma = globalThis as unknown as { return drizzle(client, { schema });
prisma: ReturnType<typeof createPrismaClient> | undefined;
}; };
export const db = globalForPrisma.prisma ?? createPrismaClient(); const globalForDb = globalThis as unknown as {
db: ReturnType<typeof createDb> | undefined;
};
export const db = globalForDb.db ?? createDb();
if (env.NODE_ENV !== "production") { if (env.NODE_ENV !== "production") {
globalForPrisma.prisma = db; globalForDb.db = db;
} }

View File

@@ -1,9 +1,6 @@
import { desc } from "drizzle-orm";
import { db } from "~/server/db"; import { db } from "~/server/db";
import { posts } from "~/server/schema";
export const listRecentPosts = async (limit: number = 5) => export const listRecentPosts = async (limit: number = 5) =>
db.post.findMany({ db.select().from(posts).orderBy(desc(posts.createdAt)).limit(limit);
take: limit,
orderBy: {
createdAt: "desc",
},
});

22
src/server/schema.ts Normal file
View File

@@ -0,0 +1,22 @@
import { index, pgTable, text, timestamp, boolean } from "drizzle-orm/pg-core";
export const posts = pgTable(
"posts",
{
id: text("id")
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
title: text("title").notNull(),
slug: text("slug").notNull().unique(),
body: text("body"),
published: boolean("published").notNull().default(false),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true })
.notNull()
.defaultNow()
.$onUpdate(() => new Date()),
},
(table) => ({
publishedCreatedAtIdx: index("posts_published_created_at_idx").on(table.published, table.createdAt),
}),
);

View File

@@ -23,8 +23,7 @@
} }
], ],
"paths": { "paths": {
"~/*": ["./src/*"], "~/*": ["./src/*"]
"@/db": ["./generated/prisma/client"]
} }
}, },
"include": [ "include": [