Next.js, Drizzle ORM, PostgreSQL and Vercel Tutorial
Learn how to integrate Drizzle ORM with Next.js, PostgreSQL and Vercel.
Step 1: Create a Next.js app
npx create-next-app@latest
Step 2: Cleanup
/app/page.tsx
export default function Home() {
return <div>Home</div>;
}
/app/global.css
@tailwind base;
@tailwind components;
@tailwind utilities;
Step 3: Deploy to Vercel
npx vercel
Step 4: Create a Postgres Database In Vercel
- Go to Vercel Dashboard
- Go to project
- Go to storage
- Create a postgres database
In local terminal, run:
vercel env pull .env.local
Step 5: Install @vercel/postgres, Drizzle, and Drizzle Kit
npm i drizzle-orm
npm i @vercel/postgres
npm i -D drizzle-kit
Step 6: Create Drizzle Config
/drizzle.config.ts
import "@/lib/config";
import { defineConfig } from "drizzle-kit";
export default defineConfig({
schema: "./lib/schema.ts",
out: "./drizzle",
driver: "pg",
dbCredentials: {
connectionString: process.env.POSTGRES_URL! + "?sslmode=require",
},
verbose: true,
strict: true,
});
Step 7: Create the Schema
/lib/schema.ts
import {
pgTable,
serial,
text,
timestamp,
uniqueIndex,
} from "drizzle-orm/pg-core";
export const users = pgTable(
"users",
{
id: serial("id").primaryKey(),
name: text("name").notNull(),
email: text("email").notNull(),
image: text("image").notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
},
(users) => {
return {
uniqueIdx: uniqueIndex("unique_idx").on(users.email),
};
}
);
Step 8: Create configuration file for loading environment variables
/lib/config.ts
import { loadEnvConfig } from "@next/env";
const projectDir = process.cwd();
loadEnvConfig(projectDir);
Step 9: Create DB connection and helpers
/lib/db.ts
import "@/lib/config";
import { drizzle } from "drizzle-orm/vercel-postgres";
import { sql } from "@vercel/postgres";
import { users } from "./schema";
import * as schema from "./schema";
export const db = drizzle(sql, { schema });
export const getUsers = async () => {
const selectResult = await db.select().from(users);
console.log("Results", selectResult);
return selectResult;
};
export type NewUser = typeof users.$inferInsert;
export const insertUser = async (user: NewUser) => {
return db.insert(users).values(user).returning();
};
export const getUsers2 = async () => {
const result = await db.query.users.findMany();
return result;
};
Step 10: Generate the sql migration file
npx drizzle-kit generate:pg
You may see an error like this:
Transforming const to the configured target environment ("es5") is not supported yet
The work around is to change the target from es5 to es6 in tsconfig.json
.
{
"compilerOptions": {
"target": "es6"
}
}
After running the generate command, you should see a drizzle
folder created.
Step 11: Create the migrate script
/scripts/migrate.ts
import { migrate } from "drizzle-orm/vercel-postgres/migrator";
import { db } from "@/lib/db";
async function main() {
await migrate(db, { migrationsFolder: "./drizzle" });
}
main();
Step 12: Run the migrate script
npx tsx scripts/migrate.ts
You should see some files generated in the drizzle
folder.
Step 13: Create and run a seed script
/scripts/seed.ts
import { NewUser, insertUser } from "@/lib/db";
async function main() {
const newUser: NewUser = {
email: "foo@example.com",
image: "some image url",
name: "foo",
};
const res = await insertUser(newUser);
console.log("insert user success", res);
process.exit();
}
main();
Run the seed script.
npx tsx scripts/seed.ts
Step 14: Update Home Page to display data
import { getUsers, getUsers2 } from "@/lib/db";
export default async function Home() {
const data = await getUsers();
const data2 = await getUsers2();
return (
<div>
<div>sql-like: {JSON.stringify(data)}</div>
<div>relational: {JSON.stringify(data2)}</div>
</div>
);
}
Step 15: Update the schema
Add a field to the users schema in /lib/schema.ts
. For example: password: text("password"),
Step 16: Try the push command
The push command syncs the drizzle schema definition schema.ts
with the database.
The push command is useful for rapid prototyping.
The generate + migrate commands are useful if you want to keep track of database migrations.
npx drizzle-kit push:pg
Step 17: Add commands to package.json
Add the commands to package.json
for future use.
{
"scripts": {
"generate": "drizzle-kit generate:pg",
"migrate": "npx tsx scripts/migrate.ts",
"seed": "npx tsx scripts/seed.ts",
"push": "drizzle-kit push:pg"
}
}
Conclusion
Drizzle has two main ways to update database schema:
- Generate and migrate - for projects that require migration files.
- Push - for prototyping or if project doesn't require migration files.
Drizzle has two main ways to query data:
- SQL-like - for SQL-like query builder.
- Relational - for ORM-like syntax for fetching data that always outputs 1 SQL query.