|
| 1 | +.. _ref_guide_gel_drizzle: |
| 2 | + |
| 3 | +====================== |
| 4 | +Drizzle ORM in Next.js |
| 5 | +====================== |
| 6 | + |
| 7 | +|Gel| integrates seamlessly with Drizzle ORM, providing a type-safe and intuitive way to interact with your database in TypeScript applications. |
| 8 | + |
| 9 | +Enable Drizzle in your Gel project |
| 10 | +================================== |
| 11 | + |
| 12 | +.. edb:split-section:: |
| 13 | +
|
| 14 | + To integrate Drizzle with your Gel project, you'll need to install the necessary dependencies: |
| 15 | + |
| 16 | + .. code-block:: bash |
| 17 | +
|
| 18 | + $ npm install drizzle-orm |
| 19 | + $ npm install -D drizzle-kit |
| 20 | +
|
| 21 | +
|
| 22 | +.. edb:split-section:: |
| 23 | +
|
| 24 | + Next, create a Drizzle configuration file in your project root to tell Drizzle how to work with your Gel database: |
| 25 | + |
| 26 | + .. code-block:: typescript |
| 27 | + :caption: drizzle.config.ts |
| 28 | +
|
| 29 | + import { defineConfig } from 'drizzle-kit'; |
| 30 | +
|
| 31 | + export default defineConfig({ |
| 32 | + dialect: 'gel', |
| 33 | + }); |
| 34 | +
|
| 35 | +
|
| 36 | +Sync your Gel schema with Drizzle |
| 37 | +================================= |
| 38 | + |
| 39 | +.. edb:split-section:: |
| 40 | +
|
| 41 | + Before using Drizzle with your Gel database, you'll need to let Drizzle introspect your schema. This step generates TypeScript files that Drizzle can use to interact with your database. |
| 42 | + |
| 43 | + .. code-block:: bash |
| 44 | +
|
| 45 | + $ npx drizzle-kit pull |
| 46 | +
|
| 47 | +
|
| 48 | +.. edb:split-section:: |
| 49 | +
|
| 50 | + This command will create a schema file based on your Gel database. The file will typically look something like this: |
| 51 | + |
| 52 | + .. code-block:: typescript |
| 53 | + :caption: drizzle/schema.ts |
| 54 | + :class: collapsible |
| 55 | +
|
| 56 | + import { gelTable, uniqueIndex, uuid, smallint, text, timestamp, relations } from "drizzle-orm/gel-core" |
| 57 | + import { sql } from "drizzle-orm" |
| 58 | +
|
| 59 | + export const books = gelTable("Book", { |
| 60 | + id: uuid().default(sql`uuid_generate_v4()`).primaryKey().notNull(), |
| 61 | + title: text().notNull(), |
| 62 | + author: text(), |
| 63 | + year: smallint(), |
| 64 | + genre: text(), |
| 65 | + read_date: timestamp(), |
| 66 | + }, (table) => [ |
| 67 | + uniqueIndex("books_pkey").using("btree", table.id.asc().nullsLast().op("uuid_ops")), |
| 68 | + ]); |
| 69 | +
|
| 70 | + export const notes = gelTable("Note", { |
| 71 | + id: uuid().default(sql`uuid_generate_v4()`).primaryKey().notNull(), |
| 72 | + text: text().notNull(), |
| 73 | + created_at: timestamp().default(sql`datetime_current()`), |
| 74 | + book_id: uuid().notNull(), |
| 75 | + }, (table) => [ |
| 76 | + uniqueIndex("notes_pkey").using("btree", table.id.asc().nullsLast().op("uuid_ops")), |
| 77 | + ]); |
| 78 | +
|
| 79 | +
|
| 80 | +Keep Drizzle in sync with Gel |
| 81 | +============================= |
| 82 | + |
| 83 | +.. edb:split-section:: |
| 84 | +
|
| 85 | + To keep your Drizzle schema in sync with your Gel schema, add a hook to your ``gel.toml`` file. This hook will automatically run ``drizzle-kit pull`` after each migration: |
| 86 | + |
| 87 | + .. code-block:: toml |
| 88 | + :caption: gel.toml |
| 89 | +
|
| 90 | + [hooks] |
| 91 | + after_migration_apply = [ |
| 92 | + "npx drizzle-kit pull" |
| 93 | + ] |
| 94 | +
|
| 95 | +
|
| 96 | +
|
| 97 | +With this hook in place, your Drizzle schema will automatically update whenever you apply Gel migrations. |
| 98 | + |
| 99 | + |
| 100 | +Create a database client |
| 101 | +======================== |
| 102 | + |
| 103 | +.. edb:split-section:: |
| 104 | +
|
| 105 | + Now, let's create a database client that you can use throughout your application: |
| 106 | + |
| 107 | + .. code-block:: typescript |
| 108 | + :caption: src/db/index.ts |
| 109 | +
|
| 110 | + import { drizzle } from 'drizzle-orm/gel'; |
| 111 | + import { createClient } from 'gel-js'; |
| 112 | + import * as schema from '@/drizzle/schema'; |
| 113 | + import * as relations from '@/drizzle/relations'; |
| 114 | +
|
| 115 | + // Import our schema |
| 116 | + import * as schema from './schema'; |
| 117 | +
|
| 118 | + // Initialize Gel client |
| 119 | + const gelClient = createClient(); |
| 120 | +
|
| 121 | + // Create Drizzle instance |
| 122 | + export const db = drizzle({ |
| 123 | + client: gelClient, schema: { |
| 124 | + ...schema, |
| 125 | + ...relations |
| 126 | + }, |
| 127 | + }); |
| 128 | +
|
| 129 | + // Helper types for use in our application |
| 130 | + export type Book = typeof schema.book.$inferSelect; |
| 131 | + export type NewBook = typeof schema.book.$inferInsert; |
| 132 | +
|
| 133 | + export type Note = typeof schema.note.$inferSelect; |
| 134 | + export type NewNote = typeof schema.note.$inferInsert; |
| 135 | +
|
| 136 | +
|
| 137 | +Perform database operations with Drizzle |
| 138 | +======================================== |
| 139 | + |
| 140 | +.. edb:split-section:: |
| 141 | +
|
| 142 | + Drizzle provides a clean, type-safe API for database operations. Here are some examples of common operations: |
| 143 | + |
| 144 | + **Selecting data:** |
| 145 | + |
| 146 | + .. code-block:: typescript |
| 147 | +
|
| 148 | + // Get all books with their notes |
| 149 | + const allBooks = await db.query.book.findMany({ |
| 150 | + with: { |
| 151 | + notes: true, |
| 152 | + }, |
| 153 | + }); |
| 154 | +
|
| 155 | + // Get a specific book |
| 156 | + const book = await db.query.book.findFirst({ |
| 157 | + where: eq(books.id, id), |
| 158 | + with: { notes: true }, |
| 159 | + }); |
| 160 | +
|
| 161 | +
|
| 162 | +.. edb:split-section:: |
| 163 | +
|
| 164 | + **Inserting data:** |
| 165 | + |
| 166 | + .. code-block:: typescript |
| 167 | +
|
| 168 | + // Insert a new book |
| 169 | + const newBook = await db.insert(book).values({ |
| 170 | + title: 'The Great Gatsby', |
| 171 | + author: 'F. Scott Fitzgerald', |
| 172 | + year: 1925, |
| 173 | + genre: 'Novel', |
| 174 | + }).returning(); |
| 175 | +
|
| 176 | + // Insert a note for a book |
| 177 | + const newNote = await db.insert(note).values({ |
| 178 | + text: 'A classic novel about the American Dream', |
| 179 | + book_id: bookId, |
| 180 | + }).returning(); |
| 181 | +
|
| 182 | +
|
| 183 | +.. edb:split-section:: |
| 184 | +
|
| 185 | + **Updating data:** |
| 186 | + |
| 187 | + .. code-block:: typescript |
| 188 | +
|
| 189 | + // Update a book |
| 190 | + const updatedBook = await db.update(book) |
| 191 | + .set({ |
| 192 | + title: 'Updated Title', |
| 193 | + author: 'Updated Author', |
| 194 | + }) |
| 195 | + .where(eq(books.id, bookId)) |
| 196 | + .returning(); |
| 197 | +
|
| 198 | +
|
| 199 | +.. edb:split-section:: |
| 200 | +
|
| 201 | + **Deleting data:** |
| 202 | + |
| 203 | + .. code-block:: typescript |
| 204 | +
|
| 205 | + // Delete a note |
| 206 | + await db.delete(notes).where(eq(notes.id, noteId)); |
| 207 | +
|
| 208 | +
|
| 209 | +Using Drizzle with Next.js |
| 210 | +========================== |
| 211 | + |
| 212 | +.. edb:split-section:: |
| 213 | +
|
| 214 | + In a Next.js application, you can use your Drizzle client in API routes and server components. Here's an example of an API route that gets all books: |
| 215 | + |
| 216 | + .. code-block:: typescript |
| 217 | + :caption: src/app/api/books/route.ts |
| 218 | +
|
| 219 | + import { NextResponse } from 'next/server'; |
| 220 | + import { db } from '@/db'; |
| 221 | +
|
| 222 | + export async function GET() { |
| 223 | + try { |
| 224 | + const allBooks = await db.query.book.findMany({ |
| 225 | + with: { notes: true }, |
| 226 | + }); |
| 227 | +
|
| 228 | + return NextResponse.json(allBooks); |
| 229 | + } catch (error) { |
| 230 | + console.error('Error fetching books:', error); |
| 231 | + return NextResponse.json( |
| 232 | + { error: 'Failed to fetch books' }, |
| 233 | + { status: 500 } |
| 234 | + ); |
| 235 | + } |
| 236 | + } |
| 237 | +
|
| 238 | +
|
| 239 | +.. edb:split-section:: |
| 240 | +
|
| 241 | + And here's an example of using Drizzle in a server component: |
| 242 | + |
| 243 | + .. code-block:: typescript |
| 244 | + :caption: src/app/books/page.tsx |
| 245 | +
|
| 246 | + import { db } from '@/db'; |
| 247 | + import BookCard from '@/components/BookCard'; |
| 248 | +
|
| 249 | + export default async function BooksPage() { |
| 250 | + const books = await db.query.book.findMany({ |
| 251 | + with: { notes: true }, |
| 252 | + }); |
| 253 | +
|
| 254 | + return ( |
| 255 | + <div> |
| 256 | + {books.map((book) => ( |
| 257 | + <BookCard key={book.id} book={book} /> |
| 258 | + ))} |
| 259 | + </div> |
| 260 | + ); |
| 261 | + } |
| 262 | +
|
| 263 | +
|
| 264 | +Keep going! |
| 265 | +=========== |
| 266 | + |
| 267 | +You are now ready to use Gel with Drizzle in your applications. This integration gives you the best of both worlds: Gel's powerful features and Drizzle's type-safe, intuitive API. |
| 268 | + |
| 269 | +For a complete example of using Gel with Drizzle in a Next.js application, check out our `Book Notes app example <https://github.com/geldata/gel-examples/tree/main/drizzle-book-notes-app>`_. |
| 270 | + |
| 271 | +You can also find a detailed tutorial on building a Book Notes app with Gel, Drizzle, and Next.js in our :ref:`documentation <ref_guide_gel_drizzle_booknotes>`. |
0 commit comments