PAS7 Studio
Ілюстрація стеку Bun.js, Elysia, Drizzle та PostgreSQL
Технології27 серп. 2026 р.·4 хв читання·Оновлено 27 серп. 2026 р.

Bun.js + Elysia + Drizzle: типобезпечний REST API з PostgreSQL

Практичний приклад API на Bun.js: Elysia для маршрутів, Drizzle ORM для типобезпечних SQL-запитів, PostgreSQL для даних і Zod для валідації.

TypeScript-розробникиFull-stack інженериАвтори API та мікросервісів

Elysia обробляє HTTP-маршрути, Drizzle описує таблиці TypeScript-кодом, а PostgreSQL зберігає дані. Bun запускає все одним процесом і не потребує окремого transpile-кроку.

Схема бази та типи відповіді виводяться з одного джерела.
Валідація відбувається на межі API, до виконання SQL-запиту.
Міграції залишаються явними й повторюваними для CI та production.
Xin

Чому саме цей стек

Кожна бібліотека має одну чітку роль, тому код легко замінювати або тестувати окремо.

Elysia

Bun-native HTTP-фреймворк із зручними route-маршрутами, middleware та схемами.

Drizzle

SQL-first ORM: таблиці описуються TypeScript-кодом, а запити залишаються близькими до SQL.

PostgreSQL

Надійне сховище для production, індексів, транзакцій і майбутнього росту домену.

Zod

Явно перевіряє вхідні дані й не дозволяє помилковому payload дійти до сервісного шару.

Запит проходить валідацію до SQL-рівня, а відповідь повертається клієнту з типізованим контрактом.

Скріншот секції stack

Крок 1: створюємо проєкт і підключаємо залежності

Bun сам встановить пакети й запускатиме TypeScript-файли без окремого bundler-а.

01

Ініціалізація

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

Змінні середовища

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

Bun автоматично читає .env під час запуску. Секрети не потрібно вбудовувати у вихідний код.

03

Конфігурація міграцій

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! },
});

Додайте scripts: "db:generate": "drizzle-kit generate" та "db:migrate": "drizzle-kit migrate".

Крок 2: описуємо таблицю та міграцію

Таблиця є джерелом типів для insert і select. Не створюйте паралельний ручний тип Task, якщо його можна вивести з Drizzle.

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;

Запустіть bun run db:generate, а потім bun run db:migrate. Міграцію комітьте в репозиторій: production має застосовувати відомий SQL, а не генерувати його навмання.

Крок 3: додаємо типобезпечні маршрути Elysia

Elysia може перевіряти схеми на вході. Для невеликого прикладу використаємо Zod через t, але в реальному сервісі варто винести handler-и в окремий service layer.

TS
// src/index.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) });

const app = new Elysia()
  .get("/health", () => ({ status: "ok", runtime: "bun" }))
  .get("/tasks", async () =>
    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));

console.log(`API running at http://localhost:${app.server?.port}`);

Повний database client і Docker Compose

З'єднання створюємо один раз на процес. Для локальної розробки Compose дає команді однакову версію PostgreSQL, а healthcheck не дозволяє міграціям стартувати раніше за базу.

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);

Для serverless-провайдера обирайте pooler або HTTP-драйвер, а для довгоживучого Bun-сервера звичайний pool із лімітом з'єднань зазвичай простіший.

YAML
# compose.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"]
      interval: 2s
      timeout: 3s
      retries: 10

Транзакції, помилки та production-поради

Швидкий runtime не компенсує нечіткі межі даних. Ось три правила, які збережуть API передбачуваним.

Тестуємо handler без магії

Elysia дозволяє викликати застосунок через handle, тому smoke-тест не потребує відкритого порту. Для інтеграційного тесту підставте тестову базу через DATABASE_URL.

TS
// src/index.test.ts
import { describe, expect, test } from "bun:test";
import { app } from "./index";

describe("tasks API", () => {
  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);
  });
});

Експортуйте app окремо від listen, щоб тест не запускав справжній listener. Запуск: bun test.

Перевірка локально та в CI

01

Запустіть PostgreSQL та API

BASH
docker run --name tasks-db -e POSTGRES_PASSWORD=app -e POSTGRES_USER=app -e POSTGRES_DB=tasks -p 5432:5432 -d postgres:16
bun run db:migrate
bun run src/index.ts
02

Створіть задачу

BASH
curl -X POST http://localhost:3000/tasks -H "content-type: application/json" -d '{"title":"Перевірити Bun API"}'
curl http://localhost:3000/tasks
03

Залиште короткий pipeline

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

Для тестів піднімайте окрему базу або використовуйте Testcontainers; не запускайте тести проти production PostgreSQL.

FAQ

Часті запитання

Чи можна використовувати Drizzle з Bun?

Так. Drizzle працює з Bun, а для PostgreSQL можна використовувати пакет `postgres` або сумісний драйвер. Перевіряйте версії драйвера у своєму deployment-середовищі.

Чи потрібен Node.js для Elysia?

Для запуску API на Bun — ні. Bun має власний runtime, package manager і TypeScript execution. Node.js може залишатися встановленим для інших проєктів.

Перевірено: 27 серп. 2026 р.Актуально для: Bun 1.3+Актуально для: Elysia 1.xАктуально для: Drizzle ORM 0.44+Актуально для: PostgreSQL 16+Перевірено з: Bun runtimeПеревірено з: ElysiaПеревірено з: drizzle-kitПеревірено з: PostgreSQL через Docker

Висновок

Опишіть задачу — перші 15 хвилин консультації безкоштовні.

Пов'язані статті

Скільки коштує розробка AI асистента у 2026: RAG чатбот, база знань, CRM, Telegram та підтримка
ai-assistants

Скільки коштує розробка AI асистента у 2026: RAG чатбот, база знань, CRM, Telegram та підтримка

Практичний гід для бізнесу: від чого залежить ціна розробки AI асистента у 2026 році, що входить у RAG чатбот, інтеграції з CRM, Telegram, guardrails, оцінювання, моніторинг і супровід.

AI може зробити більше. Не краще: що насправді кажуть дослідження про розробку ігор
blogs

AI може зробити більше. Не краще: що насправді кажуть дослідження про розробку ігор

Generative AI входить у production ігор, але докази складніші за хайп. Розбираємо adoption, ставлення гравців, ризики якості та практичний workflow.

AI для розробки лендінгів: де він реально прискорює запуск, а де псує конверсію
blogs

AI для розробки лендінгів: де він реально прискорює запуск, а де псує конверсію

Дослідження про використання AI у розробці лендінгів: v0, Webflow AI, Builder.io, Framer-подібні AI builders, генерація UX, copy, SEO, персоналізація, A/B тести, ризики шаблонності, безпеки, доступності та технічного боргу.

AI SEO / GEO у 2026: ваші наступні клієнти — не люди, а агенти
growth

AI SEO / GEO у 2026: ваші наступні клієнти — не люди, а агенти

Пошук зміщується від кліків до відповідей. Боти та AI-агенти сканують, цитують, рекомендують і дедалі частіше купують. Дізнайтесь, що таке AI SEO / GEO, чому класичного SEO вже недостатньо, і як PAS7 Studio допомагає брендам перемагати у «агентному» вебі.

Професійна розробка для вашого бізнесу

Створюємо сучасні веб-рішення та боти для бізнесу. Дізнайтеся, як ми можемо допомогти вам досягти цілей.