Skip to content

@indiepub/db

Drizzle ORM schema and migrations for Cloudflare D1. This package defines the database structure shared by all IndiePub packages.

Schema overview

entries

The core table — stores all post types in a single flat structure.

ColumnTypeNotes
idtext PKnanoid
slugtext uniqueURL-safe identifier
urltextcanonical URL
typetextpost-type-discovery result
statustextdraft | published | deleted
visibilitytextpublic | unlisted | private
nametexttitle (articles, bookmarks)
contenttextbody text (HTML allowed)
summarytextexcerpt
phototextimage URL
in_reply_totextURL being replied to
like_oftextURL being liked
bookmark_oftextURL being bookmarked
repost_oftextURL being reposted
locationtextJSON: {name, latitude, longitude}
rsvptextyes | no | maybe | interested
syndicate_totextJSON array of target names
author_idtext
published_atintegertimestamp
created_atintegertimestamp
updated_atintegertimestamp

tags and entry_tags

Many-to-many tag relationship. Tags have name and slug columns.

webmentions

Incoming and outgoing webmentions.

ColumnNotes
source_urlURL of the page mentioning us
target_urlURL on our site being mentioned
typelike | reply | repost | mention
author_name, author_url, author_photoparsed from mf2
statuspending | verified | rejected

syndications

Tracks POSSE copies for each entry.

ColumnNotes
entry_idFK to entries
targetbluesky | mastodon | standard.site
target_urlURL of the syndicated copy
at_uriATProto URI (Bluesky/standard.site)
statussuccess | failed

subscribers

Email subscribers for the newsletter.

ColumnNotes
emailunique
statusactive | unsubscribed
tokenHMAC token for unsubscribe links

ap_followers

ActivityPub followers — populated by the inbox when someone follows your actor.

ColumnNotes
actor_urlunique; follower’s Actor URL
inbox_urlfollower’s inbox for delivery
follow_activity_idID of the Follow activity

attachments

Media files attached to entries (images, video). Stores R2 URLs and optional metadata.

ColumnNotes
entry_idFK to entries
urlR2 media URL
content_typeMIME type
display_orderordering within an entry
width, heightoptional dimensions
sizeoptional file size in bytes
altoptional alt text

Social profile links for an author — displayed as icons in the theme.

ColumnNotes
author_idFK to authors
labeldisplay name (e.g. “GitHub”)
urlprofile URL
iconicon identifier
display_orderordering

logs

Structured request log entries written by the integration logger.

ColumnNotes
leveldebug | info | warn | error
categorymicropub | syndication | webmention | email | admin | system
messagehuman-readable summary
dataJSON blob with additional context

site_settings

Key/value store for runtime site configuration managed via the admin panel.

KeyDescription
debug_mode"true" enables verbose logging
favicon_urlR2 URL of the site favicon
og_colorhex accent colour for OG images
og_templateOG image layout template name
theme_palettepalette preset name (e.g. "slate", "midnight")

redirects

DB-driven URL redirects managed via /admin/redirects. The middleware checks this table when a route returns 404.

ColumnNotes
from_pathunique; normalised path (no trailing slash)
to_pathdestination path or full URL
status301 or 302

Migrations

Migrations are Drizzle-generated SQL files in packages/db/migrations/. Never write migration files by hand — always run pnpm drizzle-kit generate inside packages/db to produce them from schema changes.

In your theme, apply pending migrations with:

Terminal window
# Local D1
pnpm db:migrate
# Remote (production)
pnpm db:migrate:remote

These scripts run wrangler d1 migrations apply DB --local/--remote and apply all unapplied migrations in order.

Using the schema

import { entries, tags, entryTags } from '@indiepub/db/schema';
import { eq } from 'drizzle-orm';
const result = await db.select().from(entries).where(eq(entries.status, 'published'));

Drizzle types are internal to each package — always use the types from @indiepub/types at package boundaries.