본문 바로가기
WIKI 기술 지식 베이스

Drizzle + Turso

원문 보기 위키 갱신

Drizzle을 Turso와 함께 쓰도록 설정하는 방법이에요. 설치부터 스키마 정의, 마이그레이션, 쿼리, 벡터 임베딩까지 순서대로 따라오세요.

출처: 문서

본문

Drizzle은 Turso 연동에 @libsql/client를 사용해요. 그리고 로컬/임베디드 용도로는 @tursodatabase/database 베타 지원도 있어요.

Prerequisites

시작하기 전에 다음을 확인하세요:

Install Drizzle and the libSQL SDK

npm i drizzle-orm @libsql/client dotenv
npm i -D drizzle-kit
pnpm add drizzle-orm @libsql/client dotenv
pnpm add -D drizzle-kit
yarn add drizzle-orm @libsql/client dotenv
yarn add -D drizzle-kit

마지막으로 package.json에 세 개의 새 scripts를 추가하면 준비 끝이에요:

{
  "scripts": {
    "db:generate": "drizzle-kit generate",
    "db:migrate": "drizzle-kit migrate",
    "db:studio": "drizzle-kit studio"
  }
}

Retrieve database credentials

데이터베이스 자격 증명(URL과 인증 토큰)을 받아서 환경 변수로 저장해 두세요.

Create a Drizzle schema

import { sql } from "drizzle-orm";
import { text, sqliteTable } from "drizzle-orm/sqlite-core";

export const fooTable = sqliteTable("foo", {
  bar: text("bar").notNull().default("Hey!"),
});

Configure Drizzle Kit

프로젝트 루트에 drizzle.config.ts 파일을 다음 내용으로 만드세요:

require("dotenv").config();

import type { Config } from "drizzle-kit";

export default {
  schema: "./db/schema.ts",
  out: "./migrations",
  dialect: "turso",
  dbCredentials: {
    url: process.env.TURSO_DATABASE_URL!,
    authToken: process.env.TURSO_AUTH_TOKEN,
  },
} satisfies Config;

참고로, 위에서는 dotenv를 썼지만 Next.js, Remix, Astro, Vite 같은 도구를 쓴다면 내장 환경 변수 관리자로 이 값들을 읽어 올 수 있어요.

Connect Drizzle with libSQL

Node.js / Serverless

import { drizzle } from "drizzle-orm/libsql";
import { createClient } from "@libsql/client";

const turso = createClient({
  url: process.env.TURSO_DATABASE_URL!,
  authToken: process.env.TURSO_AUTH_TOKEN,
});

export const db = drizzle(turso);

Edge Runtimes

import { drizzle } from "drizzle-orm/libsql";
import { createClient } from "@libsql/client/web";

const turso = createClient({
  url: process.env.TURSO_DATABASE_URL!,
  authToken: process.env.TURSO_AUTH_TOKEN,
});

export const db = drizzle(turso);

Database migrations

Drizzle은 drizzle-kit으로 데이터베이스 마이그레이션을 생성하고 적용할 수 있어요.

스키마를 변경할 때마다 db:generate를 실행하세요:

npm run db:generate

이제 db:migrate로 데이터베이스에 변경을 적용하세요:

npm run db:migrate

Query

import { db } from "./db";
import { fooTable } from "./schema";

const result = await db.select().from(fooTable).all();

Connect Drizzle Studio

npm run db:studio
pnpm run db:studio

Vector Embeddings

Drizzle을 확장해서 Turso의 네이티브 벡터를 지원할 수 있어요 — 자세히 보기.

Define custom vector type

db/schema.ts 안에 다음을 추가하세요:

import { sql } from "drizzle-orm";
import { customType } from "drizzle-orm/sqlite-core";

const float32Array = customType<{
  data: number[];
  config: { dimensions: number };
  configRequired: true;
  driverData: Buffer;
}>({
  dataType(config) {
    return `F32_BLOB(${config.dimensions})`;
  },
  fromDriver(value: Buffer) {
    return Array.from(new Float32Array(value.buffer));
  },
  toDriver(value: number[]) {
    return sql`vector32(${JSON.stringify(value)})`;
  },
});

Create a table with a vector column

이제 스키마를 정의하는 곳에서 float32Array를 호출해서 벡터를 저장하는 컬럼을 만들어요:

import { sqliteTable, integer } from "drizzle-orm/sqlite-core";

export const vectorTable = sqliteTable("vector_table", {
  id: integer("id").primaryKey(),
  vector: float32Array("vector", { dimensions: 3 }),
});

Create a vector index

인덱스를 만들 때는 raw SQL을 사용해야 해요:

import { drizzle } from "drizzle-orm/libsql";
import { createClient } from "@libsql/client";

const client = createClient({
  url: process.env.TURSO_DATABASE_URL!,
  authToken: process.env.TURSO_AUTH_TOKEN,
});

const db = drizzle(client);

await db.run(sql`
  CREATE INDEX IF NOT EXISTS vector_index
  ON vector_table(vector)
  USING vector_cosine(3)
`);

Insert vector data

await db
  .insert(vectorTable)
  .values([{ vector: sql`vector32(${JSON.stringify([1.1, 2.2, 3.3])})` }]);

Query vector data

벡터 거리를 계산해요:

const res = await db
  .select({
    distance: sql<number>`vector_distance_cos(${vectorTable.vector}, vector32(${JSON.stringify([2.2, 3.3, 4.4])}))`,
  })
  .from(vectorTable);

console.log(res);

효율적인 최근접 이웃 검색도 가능해요:

const topK = await db
  .select({
    id: sql`id`,
    distance: sql`distance`,
  })
  .from(
    sql`vector_top_k('vector_index', vector32(${JSON.stringify([2.2, 3.3, 4.4])}), 5)`,
  )
  .leftJoin(vectorTable, sql`${vectorTable}.id = id`);

console.log(topK);

효율적인 벡터 연산을 위해 적절한 인덱스를 만들고, 용도에 맞게 벡터 차원을 조정하는 것도 잊지 마세요.

더 알아보기 (Learn more)