Drizzle <> Nile
- Drizzle๋ฅผ ์ฌ์ฉํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ๊ธฐ๋ณธ ์ฌํญ
- Nile Database - ์น์ฌ์ดํธ
- Drizzle PostgreSQL ๋๋ผ์ด๋ฒ - ๋ฌธ์
**๊ณต์ ์น์ฌ์ดํธ**์ ๋ฐ๋ฅด๋ฉด, Nile์ ๋ฉํฐ ํ ๋ํธ ์ฑ์ ์ํด ์ฌ์ค๊ณ๋ PostgreSQL์ ๋๋ค.
๊ณต์ Nile + Drizzle ๋น ๋ฅธ ์์ ๋ฐ ๋ง์ด๊ทธ๋ ์ด์ ๋ฌธ์๋ฅผ ํ์ธํ์ธ์.
Nile์ Drizzle์ ๋ชจ๋ Postgres ๋๋ผ์ด๋ฒ์ ํจ๊ป ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ์๋์์๋ node-postgres ์ฌ์ฉ ์์ ๋ฅผ ๋ณด์ฌ๋๋ฆฝ๋๋ค.
1๋จ๊ณ - ํจํค์ง ์ค์น
npm i drizzle-orm postgres
npm i -D drizzle-kit
2๋จ๊ณ - ๋๋ผ์ด๋ฒ ์ด๊ธฐํ ๋ฐ ์ฟผ๋ฆฌ ์คํ
// 'pg' ํจํค์ง๋ฅผ ์ค์นํด์ผ ํฉ๋๋ค
import { drizzle } from 'drizzle-orm/node-postgres'
const db = drizzle(process.env.NILEDB_URL);
const response = await db.select().from(...);๊ธฐ์กด ๋๋ผ์ด๋ฒ๋ฅผ ์ ๊ณตํด์ผ ํ๋ ๊ฒฝ์ฐ:
// 'pg' ํจํค์ง๋ฅผ ์ค์นํด์ผ ํฉ๋๋ค
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
const db = drizzle({ client: pool });
const response = await db.select().from(...);๊ฐ์ ํ ๋ํธ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฐ๊ฒฐ
Nile์ ๊ฐ์ ํ
๋ํธ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ ๊ณตํฉ๋๋ค. ํ
๋ํธ ์ปจํ
์คํธ๋ฅผ ์ค์ ํ๋ฉด Nile์ ํด๋น ํน์ ํ
๋ํธ์ ๊ฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก ์ฟผ๋ฆฌ๋ฅผ ์ ๋ฌํ๊ณ ๋ชจ๋ ์ฟผ๋ฆฌ๊ฐ ํด๋น ํ
๋ํธ์ ์ ์ฉ๋ฉ๋๋ค (์ฆ, select * from table์ ์ด ํ
๋ํธ์ ๋ ์ฝ๋๋ง ๋ฐํํฉ๋๋ค).
ํ ๋ํธ ์ปจํ ์คํธ๋ฅผ ์ค์ ํ๊ธฐ ์ํด, ํธ๋์ญ์ ์ ์คํํ๊ธฐ ์ ์ ์ ์ ํ ํ ๋ํธ ์ปจํ ์คํธ๋ฅผ ์ค์ ํ๋ ํธ๋์ญ์ ์ผ๋ก ๊ฐ ์ฟผ๋ฆฌ๋ฅผ ๋ํํฉ๋๋ค.
ํ ๋ํธ ID๋ ๊ฐ๋จํ ๋ํผ์ ์ธ์๋ก ์ ๋ฌํ ์ ์์ต๋๋ค:
import { drizzle } from 'drizzle-orm/node-postgres';
import { todosTable, tenants } from "./db/schema";
import { sql } from 'drizzle-orm';
import 'dotenv/config';
const db = drizzle(process.env.NILEDB_URL);
function tenantDB<T>(tenantId: string, cb: (tx: any) => T | Promise<T>): Promise<T> {
return db.transaction(async (tx) => {
if (tenantId) {
await tx.execute(sql`set local nile.tenant_id = '${sql.raw(tenantId)}'`);
}
return cb(tx);
}) as Promise<T>;
}
// ์น ์ฑ์์๋ ์์ฒญ ๊ฒฝ๋ก ๋งค๊ฐ๋ณ์๋ ํค๋์์ ๊ฐ์ ธ์ฌ ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค
const tenantId = '01943e56-16df-754f-a7b6-6234c368b400'
const response = await tenantDB(tenantId, async (tx) => {
// ์ฌ๊ธฐ์ "where" ์ ์ด ํ์ํ์ง ์์ต๋๋ค
return await tx.select().from(todosTable);
});
console.log(response);์ด๋ฅผ ์ง์ํ๋ ์น ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, AsyncLocalStorage๋ฅผ ์ค์ ํ๊ณ ๋ฏธ๋ค์จ์ด๋ฅผ ์ฌ์ฉํ์ฌ ํ ๋ํธ ID๋ก ์ฑ์ธ ์ ์์ต๋๋ค. ์ด ๊ฒฝ์ฐ Drizzle ํด๋ผ์ด์ธํธ ์ค์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
import { drizzle } from 'drizzle-orm/node-postgres';
import dotenv from "dotenv/config";
import { sql } from "drizzle-orm";
import { AsyncLocalStorage } from "async_hooks";
export const db = drizzle(process.env.NILEDB_URL);
export const tenantContext = new AsyncLocalStorage<string | undefined>();
export function tenantDB<T>(cb: (tx: any) => T | Promise<T>): Promise<T> {
return db.transaction(async (tx) => {
const tenantId = tenantContext.getStore();
console.log("executing query with tenant: " + tenantId);
// ํ
๋ํธ ID๊ฐ ์์ผ๋ฉด ํธ๋์ญ์
์ปจํ
์คํธ์ ์ค์ ํฉ๋๋ค
if (tenantId) {
await tx.execute(sql`set local nile.tenant_id = '${sql.raw(tenantId)}'`);
}
return cb(tx);
}) as Promise<T>;
}๊ทธ๋ฐ ๋ค์, AsyncLocalStorage๋ฅผ ์ฑ์ฐ๊ณ ์์ฒญ์ ์ฒ๋ฆฌํ ๋ tenantDB ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋๋ก ๋ฏธ๋ค์จ์ด๋ฅผ ๊ตฌ์ฑํฉ๋๋ค:
// ํ
๋ํธ ์ปจํ
์คํธ๋ฅผ ์ค์ ํ๋ ๋ฏธ๋ค์จ์ด
app.use("/api/tenants/:tenantId/*", async (c, next) => {
const tenantId = c.req.param("tenantId");
console.log("setting context to tenant: " + tenantId);
return tenantContext.run(tenantId, () => next());
});
// ๋ผ์ฐํธ ํธ๋ค๋ฌ
app.get("/api/tenants/:tenantId/todos", async (c) => {
const todos = await tenantDB(c, async (tx) => {
return await tx
.select({
id: todoSchema.id,
tenant_id: todoSchema.tenantId,
title: todoSchema.title,
estimate: todoSchema.estimate,
})
.from(todoSchema);
});
return c.json(todos);
});