move to drizzle instead of prisma
This commit is contained in:
14
README.md
14
README.md
@@ -4,7 +4,7 @@ Minimal Next.js web boilerplate with:
|
||||
|
||||
- Next.js 16 Pages Router
|
||||
- React 19
|
||||
- Prisma 7 + PostgreSQL
|
||||
- Drizzle + PostgreSQL
|
||||
- `@t3-oss/env-nextjs` runtime env parsing
|
||||
- `oxfmt` and `oxlint`
|
||||
- `vinext` for local dev and Cloudflare-targeted builds
|
||||
@@ -14,7 +14,7 @@ Minimal Next.js web boilerplate with:
|
||||
|
||||
- Node.js 24+
|
||||
- `pnpm` 10+
|
||||
- PostgreSQL access if you plan to run Prisma queries
|
||||
- PostgreSQL access if you plan to run Drizzle queries
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -30,7 +30,7 @@ Create a local env file:
|
||||
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
|
||||
pnpm db:push
|
||||
@@ -54,14 +54,14 @@ pnpm dev
|
||||
- `pnpm lint`: run Oxfmt checks and Oxlint
|
||||
- `pnpm format:write`: format supported files with Oxfmt
|
||||
- `pnpm typecheck`: run TypeScript checks
|
||||
- `pnpm db:push`: push the Prisma schema to your database
|
||||
- `pnpm db:studio`: open Prisma Studio
|
||||
- `pnpm db:push`: push the Drizzle schema to your database
|
||||
- `pnpm db:studio`: open Drizzle Studio
|
||||
|
||||
## Project layout
|
||||
|
||||
- `src/env.ts`: runtime env validation and defaults
|
||||
- `src/server/db.ts`: Prisma singleton using the Postgres adapter
|
||||
- `prisma/schema.prisma`: sample `Post` model and Prisma client generator
|
||||
- `src/server/db.ts`: Drizzle client using postgres-js
|
||||
- `src/server/schema.ts`: sample `Post` table schema
|
||||
- `wrangler.jsonc`: Cloudflare Workers runtime configuration
|
||||
|
||||
## Cloudflare
|
||||
|
||||
11
drizzle.config.ts
Normal file
11
drizzle.config.ts
Normal 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 ?? "",
|
||||
},
|
||||
});
|
||||
12
package.json
12
package.json
@@ -18,18 +18,16 @@
|
||||
"format:check": "oxfmt --check .",
|
||||
"format:write": "oxfmt .",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"db:push": "prisma db push",
|
||||
"db:studio": "prisma studio",
|
||||
"postinstall": "prisma generate"
|
||||
"db:push": "drizzle-kit push",
|
||||
"db:studio": "drizzle-kit studio"
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/adapter-pg": "7.5.0",
|
||||
"@prisma/client": "7.5.0",
|
||||
"@t3-oss/env-nextjs": "^0.13.10",
|
||||
"babel-plugin-react-compiler": "^1.0.0",
|
||||
"drizzle-orm": "^0.45.1",
|
||||
"dotenv": "^17.3.1",
|
||||
"next": "^16.2.0",
|
||||
"pg": "^8.20.0",
|
||||
"postgres": "^3.4.7",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"zod": "^4.3.6"
|
||||
@@ -40,9 +38,9 @@
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^6.0.1",
|
||||
"drizzle-kit": "^0.31.8",
|
||||
"oxfmt": "^0.41.0",
|
||||
"oxlint": "^1.56.0",
|
||||
"prisma": "^7.5.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vinext": "0.0.32",
|
||||
"vite": "^8.0.1",
|
||||
|
||||
1559
pnpm-lock.yaml
generated
1559
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,5 @@
|
||||
onlyBuiltDependencies:
|
||||
- "@prisma/engines"
|
||||
- esbuild
|
||||
- prisma
|
||||
- sharp
|
||||
- workerd
|
||||
- wrangler
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
@@ -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
8
src/pages/api/posts.ts
Normal 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 });
|
||||
}
|
||||
@@ -13,11 +13,11 @@ export default function HomePage() {
|
||||
<>
|
||||
<Head>
|
||||
<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>
|
||||
<main className={"page-shell"}>
|
||||
<section className={"hero-card"}>
|
||||
<p className={"eyebrow"}>Next.js + Prisma + Cloudflare</p>
|
||||
<p className={"eyebrow"}>Next.js + Drizzle + Cloudflare</p>
|
||||
<h1>Vinext Boilerplate</h1>
|
||||
<p className={"lead"}>
|
||||
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"}>
|
||||
<span className={"chip"}>Next 16</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"}>wrangler</span>
|
||||
<span className={"chip"}>oxfmt + oxlint</span>
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
import { PrismaClient } from "@/db";
|
||||
import { PrismaPg } from "@prisma/adapter-pg";
|
||||
import { drizzle } from "drizzle-orm/postgres-js";
|
||||
import postgres from "postgres";
|
||||
import { env } from "~/env";
|
||||
import * as schema from "~/server/schema";
|
||||
|
||||
const adapter = new PrismaPg({
|
||||
connectionString: env.DATABASE_URL,
|
||||
const createDb = () => {
|
||||
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 {
|
||||
prisma: ReturnType<typeof createPrismaClient> | undefined;
|
||||
return drizzle(client, { schema });
|
||||
};
|
||||
|
||||
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") {
|
||||
globalForPrisma.prisma = db;
|
||||
globalForDb.db = db;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { desc } from "drizzle-orm";
|
||||
import { db } from "~/server/db";
|
||||
import { posts } from "~/server/schema";
|
||||
|
||||
export const listRecentPosts = async (limit: number = 5) =>
|
||||
db.post.findMany({
|
||||
take: limit,
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
});
|
||||
db.select().from(posts).orderBy(desc(posts.createdAt)).limit(limit);
|
||||
|
||||
22
src/server/schema.ts
Normal file
22
src/server/schema.ts
Normal 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),
|
||||
}),
|
||||
);
|
||||
@@ -23,8 +23,7 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"~/*": ["./src/*"],
|
||||
"@/db": ["./generated/prisma/client"]
|
||||
"~/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
|
||||
Reference in New Issue
Block a user