Build a Full-Stack App with Next.js 15, Drizzle ORM, and PostgreSQL
A complete walkthrough of building a modern full-stack application with the latest Next.js features and type-safe database access.
February 25, 20262 min read
# Build a Full-Stack App with Next.js 15, Drizzle ORM, and PostgreSQL
## What We're Building
In this tutorial, you'll build a task management app with:
- Next.js 15 App Router with Server Components
- Drizzle ORM for type-safe database access
- PostgreSQL for data persistence
- Server Actions for mutations
- Tailwind CSS for styling
## Prerequisites
- Node.js 18+
- PostgreSQL installed locally or a cloud instance
- Basic TypeScript knowledge
## Step 1: Project Setup
Create a new Next.js project:
```bash
npx create-next-app@latest task-app --typescript --tailwind --app
cd task-app
```
Install Drizzle:
```bash
npm install drizzle-orm postgres
npm install -D drizzle-kit
```
## Step 2: Database Schema
Create your schema with full type safety:
```typescript
// src/db/schema.ts
import { pgTable, text, timestamp, boolean, uuid } from "drizzle-orm/pg-core";
export const tasks = pgTable("tasks", {
id: uuid("id").defaultRandom().primaryKey(),
title: text("title").notNull(),
completed: boolean("completed").notNull().default(false),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
```
## Step 3: Database Connection
```typescript
// src/db/index.ts
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import * as schema from "./schema";
const connection = postgres(process.env.DATABASE_URL!);
export const db = drizzle(connection, { schema });
```
## Step 4: Server Actions
```typescript
// src/app/actions.ts
"use server";
import { db } from "@/db";
import { tasks } from "@/db/schema";
import { eq } from "drizzle-orm";
import { revalidatePath } from "next/cache";
export async function addTask(formData: FormData) {
const title = formData.get("title") as string;
await db.insert(tasks).values({ title });
revalidatePath("/");
}
export async function toggleTask(id: string) {
const task = await db.query.tasks.findFirst({ where: eq(tasks.id, id) });
if (task) {
await db.update(tasks).set({ completed: !task.completed }).where(eq(tasks.id, id));
revalidatePath("/");
}
}
```
## Step 5: Build the UI
Use Server Components to fetch data and Client Components for interactivity. The full pattern leverages React's streaming capabilities for instant page loads.
## What You've Learned
- Setting up Drizzle ORM with PostgreSQL
- Creating type-safe database schemas
- Using Server Actions for mutations
- Building with the Next.js 15 App Router
## Next Steps
Deploy your app and add it to your Velso.dev portfolio to showcase your full-stack skills.
next.jsdrizzlepostgresqltutorialfull-stack
Share
Ready to build your developer portfolio?
Use Velso.dev to showcase your skills, learn with AI-powered courses, and connect with clients — all in one place.
Related Articles
TutorialsFebruary 25, 2026
Add Dark Mode to Any React App in 10 Minutes
A quick, practical guide to implementing a dark mode toggle using CSS variables and React context.
2 min read