Prerequisites

Before you start, make sure you:

1

Install Drizzle and the libSQL SDK

Finish by updating package.json to include three new scripts:

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

Retrieve database credentials

Get the database URL:

turso db show --url <database-name>

Get the database authentication token:

turso db tokens create <database-name>

Assign credentials to the environment variables inside .env.

TURSO_DATABASE_URL=
TURSO_AUTH_TOKEN=
3

Create a Drizzle schema

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

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

Configure Drizzle Kit

Create the file drizzle.config.ts in the root of your project with the following:

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;

We’re using dotenv above, but if you’re using something like Next.js, Remix, Astro, or Vite, you can use their built-in environment variables manager to source these values.

5

Connect Drizzle with libSQL

6

Database migrations

Drizzle can generate and apply database migrations with drizzle-kit.

Whenever you make changes to the schema, run db:generate:

npm run db:generate

Now apply these changes to the database with db:migrate:

npm run db:migrate
7

Query

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

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

Connect Drizzle Studio

Vector Embeddings

You can extend Drizzle to support Turso’s native vector — learn more.

1

Define custom vector type

Inside db/schema.ts, add the following:

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

Create a table with a vector column

Now where you define the schema, invoke float32Array to create a column that stores vectors:

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

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

Create a vector index

You will need to use raw SQL to create the index:

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

Insert vector data

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

Query vector data

Calculate vector distance:

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

Perform efficient nearest neighbor search:

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

Remember to create appropriate indexes for efficient vector operations and adjust vector dimensions as needed for your use case.