개발을 시작할 때 많은 것을 고민한다. 그 중 새로운 기술을 접하게 되었을 때 가장 고민되는것 같다. 항상 새로운 기술들은 나오고, 그 전에 사용하던 기술들은 항상 다 써보지도 못하고 적응할 때 쯤에 새로운 기술이 나오는 것 같다는 생각이 들어서 아닐까?
드리즐 홈페이지 https://orm.drizzle.team/
깃 허브 https://github.com/drizzle-team/drizzle-orm
드리즐(Drizzle)은 SQL 데이터베이스와 상호작용하기 위한 ORM(Object-Relational Mapping)라이브러리이다.
뭐 그냥 type-orm, prisma 와 같이 typescript 생태계에서 새로운 라이브러리라고 들어 시도해보고 싶었다.
최근 프로젝트를 하나 진행하게 되었는데, 나는 항상 프로젝트를 할 때 마다 새로운 라이브러리 도입이나, 기술들을 적용해보고 싶어서 이상한것들을 찾아보고 적용해본다.
그게 이번엔 drizzle-orm이 된 것 같다. 그 외에도 최근에 typescript 생태계를 시작하게 되었기 때문에, class-validator밖에 사용을 못 해봤는데, zod를 사용해서 validator를 사용할 것 같다.
// Access your data
await db
.select()
.from(countries)
.leftJoin(cities, eq(cities.countryId, countries.id))
.where(eq(countries.id, 10))
드리즐의 메뉴얼 첫 페이지에 있는 Select 코드이다.
// manage your schema
export const countries = pgTable('countries', {
id: serial('id').primaryKey(),
name: varchar('name', { length: 256 }),
});
export const cities = pgTable('cities', {
id: serial('id').primaryKey(),
name: varchar('name', { length: 256 }),
countryId: integer('country_id').references(() => countries.id),
});
pgTable이라는 함수를 사용해서 테이블의 이름을 정하고, 각 데이터의 객체를 어떻게 정의할 지 작성한다.
const result = await db.query.users.findMany({
with: {
posts: true
},
});
그렇다고 해서 findMany 와 같이 데이터를 불러오는 함수들도 제공해준다.
간단하게 nestjs의 프로젝트를 하나 만들고, yarn add로 드리즐 라이브러리를 불러온다.
https://orm.drizzle.team/docs/get-started-postgresql#node-postgres
node-postgres를 기반으로 코드를 작성해본다.
yarn add drizzle-orm pgyarn add -D drizzle-kit @types/pg
client, pool 두 가지의 방식으로 연결해 접근하는 방법이 있다.
나는 pool 방식으로 연결해볼 생각이다.
import { pgTable, serial, text, varchar } from "drizzle-orm/pg-core";
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
const pool = new Pool({
connectionString: "postgres://user:password@host:port/db",
});
// or
const pool = new Pool({
host: "127.0.0.1",
port: 5432,
user: "postgres",
password: "password",
database: "db_name",
});
const db = drizzle(pool);
나는 지금까지 spring 프레임워크에만 익숙하게 개발하다가 최근에 nestjs 쪽으로 넘어오게 됐는데 아직 익숙하지 않은게 너무 많다...
특히 이 drizzle의 구조 자체가 도대체 왜 이렇게 만들어 진거지.. 라는 생각을 많이 했다..
그래도 nestjs를 위해 간단하게 구조화 시켜놓은 라이브러리를 발견했다.
https://www.npmjs.com/package/@knaadh/nestjs-drizzle-pg
📦 <project root>
└ 📜 drizzle.config.ts
└ 📂 src
└ 📂 db
└ 📜 schema.ts
└ 📜 drizzle.module.ts
import { DrizzlePGModule } from "@knaadh/nestjs-drizzle-pg"
import { Module } from "@nestjs/common"
import * as schema from "./schema"
export const PG_CONNECTION = "PG_CONNECTION" // ignore that it is not separate file
@Module({
imports: [
DrizzlePGModule.registerAsync({
tag: "DB",
useFactory() {
return {
pg: {
connection: "pool",
config: {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
port: parseInt(process.env.DB_PORT),
},
},
config: { schema: { ...schema } },
}
},
}),
],
})
export class DrizzleModule {}
#DB
PORT=3000
DB_HOST=localhost
DB_USER=postgres
DB_PASSWORD=postgres
DB_PORT=5432
DB_NAME=postgres
import * as schema from "@drizzle/schema"
import { Product, ProductTags, Tag } from "@drizzle/schema"
import { Inject, Injectable } from "@nestjs/common"
import { getProductlistSchema } from "@product/common/product-zod.dto"
import { AddProductReq } from "@product/common/product.dto"
import { eq, getTableColumns, sql } from "drizzle-orm"
import { NodePgDatabase } from "drizzle-orm/node-postgres"
@Injectable()
export class ProductService {
constructor(@Inject("DB") private db: NodePgDatabase<typeof schema>) {}
async getProductList() {
const result = await this.db
.select({
product: getTableColumns(Product),
tags: sql<string[]>`array_agg(${Tag.name})`,
})
.from(Product)
.leftJoin(ProductTags, eq(ProductTags.productId, Product.id))
.leftJoin(Tag, eq(ProductTags.tagId, Tag.id))
.groupBy(Product.id)
const res = getProductlistSchema.parse(result)
return res
}
async addProductData(payload: AddProductReq) {
const [data] = await this.db
.insert(schema.Product)
.values({ ...payload })
.returning()
return data
}
}