Knowledge > Runbooks > Content Ops > Migrate Blog Posts to Supabase
Migrate Blog Posts from blog-posts.ts to Supabase articles Table
Move the 9 audited blog articles from the static TypeScript data file (src/lib/blog-posts.ts) into the production articles table in Supabase. This unblocks Email #2 (which links to the theological-traditions article) and enables database-driven article management going forward.
Prerequisites
- All 9 articles in
src/lib/blog-posts.tshave been audited and fixed for accuracy - Supabase SQL access via Supabase dashboard or
mcp__plugin_supabase_supabase__execute_sqlMCP tool - Migration script:
churchwiseai-web/scripts/migrate-blog-posts.ts
Context
Current state:
- Articles exist in
src/lib/blog-posts.ts(static TypeScript objects) - Blog pages query
src/lib/articles.ts, which reads from Supabasearticlestable - Result: Articles from
blog-posts.tsare not visible. Email #2 links to a 404.
After migration:
- Articles will be in Supabase
articlestable src/lib/articles.tswill return them automatically- All links (Email #2, etc.) will work
- Future articles can be managed via database or admin interface
Steps
1. Create the articles Table Schema (if it doesn't exist)
Run this SQL in the Supabase SQL editor or via MCP tool:
CREATE TABLE IF NOT EXISTS public.articles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
slug TEXT NOT NULL UNIQUE,
title TEXT NOT NULL,
excerpt TEXT,
content TEXT,
status TEXT NOT NULL DEFAULT 'published' CHECK (status IN ('draft', 'published', 'archived', 'scheduled')),
published_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
read_time INTEGER DEFAULT 5,
-- Author as JSONB
author JSONB DEFAULT NULL,
-- Category as JSONB
category JSONB DEFAULT NULL,
-- Tags as JSONB array
tags JSONB DEFAULT NULL,
-- Featured image
featured_image_url TEXT,
featured_image_alt TEXT,
-- SEO metadata as JSONB
seo JSONB DEFAULT NULL,
-- Indexes for common queries
CONSTRAINT articles_content_not_empty CHECK (content IS NOT NULL AND content <> '')
);
-- Create indexes for common queries
CREATE INDEX IF NOT EXISTS idx_articles_slug ON articles(slug) WHERE status IN ('published', 'archived');
CREATE INDEX IF NOT EXISTS idx_articles_published ON articles(published_at DESC) WHERE status IN ('published', 'archived');
CREATE INDEX IF NOT EXISTS idx_articles_status ON articles(status);
Verify:
SELECT column_name, data_type FROM information_schema.columns
WHERE table_name = 'articles'
ORDER BY ordinal_position;
2. Run the Migration Script
From C:\dev\churchwiseai-web:
pnpm tsx scripts/migrate-blog-posts.ts
The script will:
- Read all articles from
src/lib/blog-posts.ts - Transform each into Supabase format (JSONB for author, category, tags, seo)
- Insert into
articlestable viaSUPABASE_SERVICE_ROLE_KEY - Log success/failure for each article
- Output a summary report
Expected output:
✓ Migrating 9 blog posts to Supabase articles table...
✓ Article: anthropic-ai-safety-and-christian-conscience
✓ Article: five-questions-ai-provider-safety
✓ Article: theological-traditions-and-ai
... (6 more articles)
✓ Migration complete! 9 articles inserted.
✓ Verify: SELECT COUNT(*) FROM articles WHERE status = 'published';
3. Verify the Migration in Supabase
In the Supabase dashboard SQL editor:
-- Count articles
SELECT COUNT(*) FROM articles WHERE status = 'published';
-- Check specific article
SELECT slug, title, published_at, read_time FROM articles WHERE slug = 'theological-traditions-and-ai';
-- List all article titles
SELECT slug, title, published_at FROM articles WHERE status IN ('published', 'archived') ORDER BY published_at DESC;
Expected results:
- Count: 9
theological-traditions-and-aishould be published and visible- All 9 articles should have correct titles and dates
4. Test Email #2 Link
In your local dev environment or deployed staging:
GET /blog/theological-traditions-and-ai
Expected result: Article loads with full content, no 404.
5. Verify Blog Page Functions
Test in a browser:
-
Index page:
GET /blog- Should display all 9 articles
- Newest first
- Excerpt, title, read time visible
-
Article detail:
GET /blog/theological-traditions-and-ai- Full content loads
- Author, category, tags visible
- Featured image displays (if present)
-
Search: Search for "theological"
- Article appears in results
-
Category filter: Filter by "AI Ethics" or "Theology"
- Relevant articles appear
6. Optional: Add Featured Images Post-Migration
The articles table supports featured_image_url and featured_image_alt. If you want to add featured images:
- Upload images to Supabase Storage (or host externally)
- Update articles with image URLs:
UPDATE articles
SET featured_image_url = 'https://your-cdn.com/image.jpg',
featured_image_alt = 'Image description'
WHERE slug = 'article-slug';
Rollback
If migration fails or you need to reverse:
-- Delete all migrated articles
DELETE FROM articles WHERE status IN ('published', 'archived');
-- OR drop the entire table if needed
DROP TABLE IF EXISTS articles;
Then restore articles from blog-posts.ts and diagnose the issue.
Next Steps
- Update Remotion graphics pipeline — Now that articles are in Supabase, blog graphics (ScriptureCard, StatCard components) can be generated and stored as media assets
- Archive old blog-posts.ts — Once verified, move
src/lib/blog-posts.tsto adocs/archive/folder - Build article admin dashboard — UI to create/edit/delete articles without code
- SEO optimization — Add Open Graph images, structured data, meta tags per article
Related Documents
knowledge/runbooks/technical-ops/supabase-migration.md— General Supabase migration processknowledge/runbooks/content-ops/— Other content management runbookschurchwiseai-web/src/lib/articles.ts— How articles are queriedchurchwiseai-web/src/lib/blog-posts.ts— Source data being migrated