Neon Postgres와 함께 사용하기
이 튜토리얼은 Neon Postgres 데이터베이스에서 Drizzle ORM을 사용하는 방법을 설명합니다. Neon 계정이 없다면 여기에서 가입하세요.
- Drizzle ORM과 Drizzle kit을 설치해야 합니다. 다음 명령어로 설치할 수 있습니다:
npm i drizzle-orm
npm i -D drizzle-kit
- Neon serverless driver도 설치해야 합니다.
npm i @neondatabase/serverless
- 환경 변수 관리를 위해
dotenv패키지를 설치해야 합니다.
npm i dotenv
Neon과 Drizzle ORM 설정
새 Neon 프로젝트 생성
Neon Console에 로그인하여 Projects 섹션으로 이동합니다. 프로젝트를 선택하거나 New Project 버튼을 클릭하여 새 프로젝트를 생성합니다.
Neon 프로젝트에는 neondb라는 이름의 바로 사용 가능한 Postgres 데이터베이스가 제공됩니다. 이 튜토리얼에서는 이 데이터베이스를 사용합니다.
연결 문자열 변수 설정
프로젝트 콘솔의 Connection Details 섹션으로 이동하여 데이터베이스 연결 문자열을 확인합니다. 다음과 유사한 형태입니다:
postgres://username:password@ep-cool-darkness-123456.us-east-2.aws.neon.tech/neondb.env 또는 .env.local 파일에 DATABASE_URL 환경 변수를 추가합니다. 이 변수를 사용하여 Neon 데이터베이스에 연결합니다.
DATABASE_URL=NEON_DATABASE_CONNECTION_STRINGDrizzle ORM을 데이터베이스에 연결
db.ts 파일을 생성하고 데이터베이스 설정을 구성합니다:
import { drizzle } from "drizzle-orm/neon-http";
import { neon } from "@neondatabase/serverless";
import { config } from "dotenv";
config({ path: ".env" }); // or .env.local
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle({ client: sql });테이블 생성
schema.ts 파일을 생성하고 테이블을 정의합니다:
import { integer, pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
export const usersTable = pgTable('users_table', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
age: integer('age').notNull(),
email: text('email').notNull().unique(),
});
export const postsTable = pgTable('posts_table', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
content: text('content').notNull(),
userId: integer('user_id')
.notNull()
.references(() => usersTable.id, { onDelete: 'cascade' }),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at')
.notNull()
.$onUpdate(() => new Date()),
});
export type InsertUser = typeof usersTable.$inferInsert;
export type SelectUser = typeof usersTable.$inferSelect;
export type InsertPost = typeof postsTable.$inferInsert;
export type SelectPost = typeof postsTable.$inferSelect;Drizzle 설정 파일 구성
Drizzle config - Drizzle Kit에서 사용하는 설정 파일로, 데이터베이스 연결, 마이그레이션 폴더 및 스키마 파일에 대한 모든 정보를 포함합니다.
프로젝트 루트에 drizzle.config.ts 파일을 생성하고 다음 내용을 추가합니다:
import { config } from 'dotenv';
import { defineConfig } from "drizzle-kit";
config({ path: '.env' });
export default defineConfig({
schema: "./src/schema.ts",
out: "./migrations",
dialect: "postgresql",
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});데이터베이스에 변경사항 적용
drizzle-kit generate 명령어로 마이그레이션을 생성한 다음 drizzle-kit migrate 명령어로 실행할 수 있습니다.
마이그레이션 생성:
npx drizzle-kit generate생성된 마이그레이션은 drizzle.config.ts에 지정된 대로 drizzle/migrations 디렉토리에 저장됩니다. 이 디렉토리에는 데이터베이스 스키마를 업데이트하는 데 필요한 SQL 파일과 다양한 마이그레이션 단계에서 스키마의 스냅샷을 저장하는 meta 폴더가 포함됩니다.
생성된 마이그레이션 예시:
CREATE TABLE IF NOT EXISTS "posts_table" (
"id" serial PRIMARY KEY NOT NULL,
"title" text NOT NULL,
"content" text NOT NULL,
"user_id" integer NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "users_table" (
"id" serial PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"age" integer NOT NULL,
"email" text NOT NULL,
CONSTRAINT "users_table_email_unique" UNIQUE("email")
);
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "posts_table" ADD CONSTRAINT "posts_table_user_id_users_table_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users_table"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;마이그레이션 실행:
npx drizzle-kit migrate또는 Drizzle kit push 명령어를 사용하여 변경사항을 데이터베이스에 직접 푸시할 수 있습니다:
npx drizzle-kit push기본 파일 구조
이것은 프로젝트의 기본 파일 구조입니다. src/db 디렉토리에는 db.ts의 연결 설정, schema.ts의 스키마 정의, 그리고 migrations 디렉토리에 저장된 마이그레이션을 적용하는 migrate.ts 파일 등 데이터베이스 관련 파일들이 있습니다.
📦 <project root>
├ 📂 src
│ ├ 📜 db.ts
│ └ 📜 schema.ts
├ 📂 migrations
│ ├ 📂 meta
│ │ ├ 📜 _journal.json
│ │ └ 📜 0000_snapshot.json
│ └ 📜 0000_dry_richard_fisk.sql
├ 📜 .env
├ 📜 drizzle.config.ts
├ 📜 package.json
└ 📜 tsconfig.json쿼리 예제
예를 들어, src/queries 폴더를 생성하고 각 작업(insert, select, update, delete)별로 파일을 분리할 수 있습니다.
데이터 삽입
insert 쿼리에 대한 자세한 내용은 문서를 참조하세요.
import { db } from '../db';
import { InsertPost, InsertUser, postsTable, usersTable } from '../schema';
export async function createUser(data: InsertUser) {
await db.insert(usersTable).values(data);
}
export async function createPost(data: InsertPost) {
await db.insert(postsTable).values(data);
}데이터 조회
select 쿼리에 대한 자세한 내용은 문서를 참조하세요.
getColumns는 drizzle-orm@1.0.0-beta.2부터 사용 가능합니다(여기에서 자세히 보기)
1.0 이전 버전(예: 0.45.1)을 사용하는 경우 getTableColumns를 사용하세요
import { asc, between, count, eq, getColumns, sql } from 'drizzle-orm';
import { db } from '../db';
import { SelectUser, usersTable, postsTable } from '../schema';
export async function getUserById(id: SelectUser['id']): Promise<
Array<{
id: number;
name: string;
age: number;
email: string;
}>
> {
return db.select().from(usersTable).where(eq(usersTable.id, id));
}
export async function getUsersWithPostsCount(
page = 1,
pageSize = 5,
): Promise<
Array<{
postsCount: number;
id: number;
name: string;
age: number;
email: string;
}>
> {
return db
.select({
...getColumns(usersTable),
postsCount: count(postsTable.id),
})
.from(usersTable)
.leftJoin(postsTable, eq(usersTable.id, postsTable.userId))
.groupBy(usersTable.id)
.orderBy(asc(usersTable.id))
.limit(pageSize)
.offset((page - 1) * pageSize);
}
export async function getPostsForLast24Hours(
page = 1,
pageSize = 5,
): Promise<
Array<{
id: number;
title: string;
}>
> {
return db
.select({
id: postsTable.id,
title: postsTable.title,
})
.from(postsTable)
.where(between(postsTable.createdAt, sql`now() - interval '1 day'`, sql`now()`))
.orderBy(asc(postsTable.title), asc(postsTable.id))
.limit(pageSize)
.offset((page - 1) * pageSize);
}또는 관계형 쿼리 문법을 사용할 수도 있습니다.
데이터 업데이트
update 쿼리에 대한 자세한 내용은 문서를 참조하세요.
import { eq } from 'drizzle-orm';
import { db } from '../db';
import { SelectPost, postsTable } from '../schema';
export async function updatePost(id: SelectPost['id'], data: Partial<Omit<SelectPost, 'id'>>) {
await db.update(postsTable).set(data).where(eq(postsTable.id, id));
}데이터 삭제
delete 쿼리에 대한 자세한 내용은 문서를 참조하세요.
import { db } from '../db';
import { eq } from 'drizzle-orm';
import { SelectUser, usersTable } from '../schema';
export async function deleteUser(id: SelectUser['id']) {
await db.delete(usersTable).where(eq(usersTable.id, id));
}