Better Blog

Drizzle

Provider backed by Drizzle ORM

Drizzle

Better Blog can be used with Drizzle ORM and a PostgreSQL database.

Environment Variables

POSTGRES_URL=

Installation

pnpm add drizzle-orm postgres
pnpm add -D drizzle-kit

Schema

Update your schema to match the one below:

import {
  timestamp,
  pgTable,
  text,
  primaryKey,
  integer,
  uuid,
  index,
  uniqueIndex
} from "drizzle-orm/pg-core"
import postgres from "postgres"
import { drizzle } from "drizzle-orm/postgres-js"
import { relations } from "drizzle-orm"

const connectionString = process.env.POSTGRES_URL || 'postgres://postgres:postgres@localhost:5432/kysely_experiment_1'
const pool = postgres(connectionString, { max: 1 })
 
export const db = drizzle(pool)

// Post table
export const posts = pgTable("Post", {
  id: uuid('id').primaryKey().defaultRandom(),
  authorId: text('authorId'),
  defaultLocale: text('defaultLocale').notNull().default('en'),
  title: text('title').notNull(),
  slug: text('slug').notNull().unique(),
  excerpt: text('excerpt').notNull(),
  content: text('content').notNull(),
  image: text('image'),
  version: integer('version').notNull().default(1),
  status: text('status').notNull().default('DRAFT'),
  createdAt: timestamp('createdAt', { withTimezone: true }).notNull().defaultNow(),
  updatedAt: timestamp('updatedAt', { withTimezone: true }).notNull().defaultNow()
}, (table) => [
  index('post_author_id_idx').on(table.authorId),
  index('post_status_updated_at_idx').on(table.status, table.updatedAt)
])

// Tag table
export const tags = pgTable("Tag", {
  id: uuid('id').primaryKey().defaultRandom(),
  defaultLocale: text('defaultLocale').notNull().default('en'),
  name: text('name').notNull(),
  slug: text('slug').notNull().unique(),
  createdAt: timestamp('createdAt', { withTimezone: true }).notNull().defaultNow(),
  updatedAt: timestamp('updatedAt', { withTimezone: true }).notNull().defaultNow()
})

// PostI18n table
export const postI18n = pgTable("PostI18n", {
  id: uuid('id').primaryKey().defaultRandom(),
  postId: uuid('postId').notNull().references(() => posts.id, { onDelete: 'cascade' }),
  locale: text('locale').notNull(),
  title: text('title').notNull(),
  slug: text('slug').notNull(),
  excerpt: text('excerpt').notNull(),
  content: text('content').notNull()
}, (table) => [
  index('post_i18n_locale_idx').on(table.locale),
  uniqueIndex('post_i18n_post_id_locale_unique').on(table.postId, table.locale),
  uniqueIndex('post_i18n_locale_slug_unique').on(table.locale, table.slug)
])

// TagI18n table
export const tagI18n = pgTable("TagI18n", {
  id: uuid('id').primaryKey().defaultRandom(),
  tagId: uuid('tagId').notNull().references(() => tags.id, { onDelete: 'cascade' }),
  locale: text('locale').notNull(),
  name: text('name').notNull(),
  slug: text('slug').notNull()
}, (table) => [
  index('tag_i18n_locale_idx').on(table.locale),
  uniqueIndex('tag_i18n_tag_id_locale_unique').on(table.tagId, table.locale),
  uniqueIndex('tag_i18n_locale_slug_unique').on(table.locale, table.slug)
])

// PostTag junction table
export const postTags = pgTable("PostTag", {
  postId: uuid('postId').notNull().references(() => posts.id, { onDelete: 'cascade' }),
  tagId: uuid('tagId').notNull().references(() => tags.id, { onDelete: 'cascade' })
}, (table) => [
  primaryKey({ columns: [table.postId, table.tagId] }),
  index('post_tag_tag_id_idx').on(table.tagId)
])

// Relations
export const postsRelations = relations(posts, ({ many }) => ({
  i18n: many(postI18n),
  tags: many(postTags)
}))

export const tagsRelations = relations(tags, ({ many }) => ({
  i18n: many(tagI18n),
  posts: many(postTags)
}))

export const postI18nRelations = relations(postI18n, ({ one }) => ({
  post: one(posts, {
    fields: [postI18n.postId],
    references: [posts.id]
  })
}))

export const tagI18nRelations = relations(tagI18n, ({ one }) => ({
  tag: one(tags, {
    fields: [tagI18n.tagId],
    references: [tags.id]
  })
}))

export const postTagsRelations = relations(postTags, ({ one }) => ({
  post: one(posts, {
    fields: [postTags.postId],
    references: [posts.id]
  }),
  tag: one(tags, {
    fields: [postTags.tagId],
    references: [tags.id]
  })
}))

Example usage

// lib/blog-data-provider.ts

import { createDrizzleProvider } from "better-blog/providers/drizzle"
import { db } from "./drizzle"
import { sql } from "drizzle-orm"

export async function createBlogDataProvider() {
  return createDrizzleProvider({
    drizzle: db,
    sql,
  })
}

Full documentation on how to manage migrations with Drizzle can be found at the Drizzle Kit Migrations page.