PAS7 Studio
Illustration of Bun.js, Elysia, Drizzle, and PostgreSQL
Technology27 Aug 2026·3 min read·Updated 27 Aug 2026

Bun.js + Elysia + Drizzle: a type-safe REST API with PostgreSQL

A practical Bun.js API using Elysia for routing, Drizzle ORM for typed SQL, PostgreSQL for persistence, and Zod for validation.

TypeScript developersFull-stack engineersAPI and microservice teams

Elysia handles HTTP routes, Drizzle describes PostgreSQL tables with TypeScript, and Bun runs the whole service without a separate transpile step.

Database schema and application types come from one source.
Invalid payloads are rejected before SQL executes.
Migrations are explicit and repeatable in CI and production.
Xin

Why this stack

Each library has one clear responsibility, so the service stays easy to test and evolve.

Elysia

A Bun-native HTTP framework with routes, middleware, and schemas.

Drizzle

A SQL-first ORM: tables are TypeScript, while queries remain close to SQL.

PostgreSQL

A reliable production store for indexes, transactions, and future growth.

Zod

Validates input at the API boundary before it reaches the service layer.

The request is validated before SQL runs, and the response returns through a typed contract.

Section stack screenshot

Step 1: create the project

Bun installs dependencies and executes TypeScript without a separate bundler.

01

Initialize and install

BASH
mkdir tasks-api && cd tasks-api
bun init
bun add elysia drizzle-orm postgres zod
bun add -d drizzle-kit
02

Configure the environment

ENV
DATABASE_URL=postgres://app:app@localhost:5432/tasks
PORT=3000
DB_POOL_SIZE=10

Bun loads .env automatically. Keep secrets outside source control.

03

Configure migrations

TS
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({ schema: "./src/db/schema.ts", out: "./drizzle", dialect: "postgresql", dbCredentials: { url: process.env.DATABASE_URL! } });

Run bun run db:generate and bun run db:migrate; commit the generated migration.

Step 2: define the table and migration

The table becomes the source of truth for insert and select types. Do not maintain a second hand-written Task type.

TS
// src/db/schema.ts
import { boolean, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
export const tasks = pgTable("tasks", { id: uuid("id").defaultRandom().primaryKey(), title: text("title").notNull(), done: boolean("done").notNull().default(false), createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull() });
export type Task = typeof tasks.$inferSelect;
export type NewTask = typeof tasks.$inferInsert;

Generate and apply the migration with bun run db:generate and bun run db:migrate.

Step 3: add typed Elysia routes

Elysia validates the request at the edge. The handler can then focus on the database operation.

TS
import { Elysia, t } from "elysia";
import { desc, eq } from "drizzle-orm";
import { z } from "zod";
import { db } from "./db/client";
import { tasks } from "./db/schema";
const createTask = z.object({ title: z.string().trim().min(1).max(120) });
export const app = new Elysia()
  .get("/health", () => ({ status: "ok", runtime: "bun" }))
  .get("/tasks", () => db.select().from(tasks).orderBy(desc(tasks.createdAt)))
  .post("/tasks", async ({ body, set }) => { const input = createTask.parse(body); const [task] = await db.insert(tasks).values(input).returning(); set.status = 201; return task; }, { body: t.Object({ title: t.String({ minLength: 1, maxLength: 120 }) }) })
  .patch("/tasks/:id", async ({ params, body }) => { const [task] = await db.update(tasks).set({ done: body.done }).where(eq(tasks.id, params.id)).returning(); return task ?? new Response("Not found", { status: 404 }); }, { params: t.Object({ id: t.String() }), body: t.Object({ done: t.Boolean() }) })
  .listen(Number(Bun.env.PORT ?? 3000));

Database client and Docker Compose

Create one connection pool per long-lived Bun process. For local development, Compose gives the team the same PostgreSQL version.

TS
// src/db/client.ts
import postgres from "postgres";
import { drizzle } from "drizzle-orm/postgres-js";
const sql = postgres(Bun.env.DATABASE_URL!, { max: Number(Bun.env.DB_POOL_SIZE ?? 10), prepare: false });
export const db = drizzle(sql);
YAML
services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: app
      POSTGRES_DB: tasks
    ports: ["5432:5432"]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d tasks"]

Transactions, errors, and production rules

A fast runtime does not replace explicit data boundaries.

Use transactions for related writes

Wrap multi-table changes in db.transaction(async (tx) => { ... }).

Return 404 for missing resources

Check returning() rather than returning undefined with status 200.

Run migrations separately

Apply migrations as a deploy step; do not let every server instance mutate the schema.

Keep the pool bounded

Use a pooler or a smaller limit in serverless environments.

Test handlers without opening a port

Elysia exposes handle, so a smoke test can call the app directly.

TS
import { expect, test } from "bun:test";
import { app } from "./index";
test("rejects an empty title", async () => {
  const response = await app.handle(new Request("http://localhost/tasks", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ title: "  " }) }));
  expect(response.status).toBe(422);
});

Run bun test against a dedicated test database.

Run and smoke-test locally

01

Start PostgreSQL

BASH
docker compose up -d
bun run db:migrate
02

Start Bun

BASH
bun run src/index.ts
curl http://localhost:3000/health
03

Use the frozen lockfile

BASH
bun install --frozen-lockfile
bun run db:migrate
bun test
FAQ

FAQ

Can Drizzle run with Bun?

Yes. Drizzle works with Bun and PostgreSQL through the postgres driver or another supported driver. Pin and test the versions used by your deployment.

Is Node.js required for Elysia?

No. Bun provides the runtime, package manager, and TypeScript execution. Node.js can still be installed for other projects.

Reviewed: 27 Aug 2026Applies to: Bun 1.3+Applies to: Elysia 1.xApplies to: Drizzle ORM 0.44+Applies to: PostgreSQL 16+Tested with: Bun runtimeTested with: ElysiaTested with: drizzle-kitTested with: PostgreSQL with Docker

Conclusion

Describe the task — first 15 minutes of consultation are free.

Related Articles

AI Assistant Development Cost in 2026: RAG Chatbots, CRM Integrations, Guardrails, and Support
ai-assistants

AI Assistant Development Cost in 2026: RAG Chatbots, CRM Integrations, Guardrails, and Support

A practical buyer guide to AI assistant development cost in 2026: prototypes, RAG chatbots, knowledge-base assistants, CRM and website integrations, guardrails, evaluations, monitoring, and support.

AI for landing page development: where it speeds up launches and where it hurts conversion
blogs

AI for landing page development: where it speeds up launches and where it hurts conversion

A practical research piece on using AI for landing page development: v0, Webflow AI, Builder.io, Framer-like builders, UX generation, copy, SEO, personalization, A/B testing, template risk, accessibility, security and technical debt.

AI SEO / GEO in 2026: Your Next Customers Aren’t Humans — They’re Agents
growth

AI SEO / GEO in 2026: Your Next Customers Aren’t Humans — They’re Agents

Search is shifting from clicks to answers. Bots and AI agents crawl, cite, recommend, and increasingly buy. Learn what AI SEO / GEO means, why classic SEO is no longer enough, and how PAS7 Studio helps brands win visibility in the agentic web.

The most powerful Apple chip yet? M5 Pro and M5 Max are breaking records
blogs

The most powerful Apple chip yet? M5 Pro and M5 Max are breaking records

A data-backed March 2026 analysis of Apple M5 Pro and M5 Max. We break down why these chips can credibly be called Apple's most powerful pro laptop silicon, how they compare with M4 Pro, M4 Max, M1 Pro, M1 Max, and how they stack up against Intel and AMD laptop rivals.

Professional development for your business

We create modern web solutions and bots for businesses. Learn how we can help you achieve your goals.