Skip to main content

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.ts have been audited and fixed for accuracy
  • Supabase SQL access via Supabase dashboard or mcp__plugin_supabase_supabase__execute_sql MCP 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 Supabase articles table
  • Result: Articles from blog-posts.ts are not visible. Email #2 links to a 404.

After migration:

  • Articles will be in Supabase articles table
  • src/lib/articles.ts will 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:

  1. Read all articles from src/lib/blog-posts.ts
  2. Transform each into Supabase format (JSONB for author, category, tags, seo)
  3. Insert into articles table via SUPABASE_SERVICE_ROLE_KEY
  4. Log success/failure for each article
  5. 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-ai should be published and visible
  • All 9 articles should have correct titles and dates

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:

  1. Index page: GET /blog

    • Should display all 9 articles
    • Newest first
    • Excerpt, title, read time visible
  2. Article detail: GET /blog/theological-traditions-and-ai

    • Full content loads
    • Author, category, tags visible
    • Featured image displays (if present)
  3. Search: Search for "theological"

    • Article appears in results
  4. 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:

  1. Upload images to Supabase Storage (or host externally)
  2. 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​

  1. Update Remotion graphics pipeline — Now that articles are in Supabase, blog graphics (ScriptureCard, StatCard components) can be generated and stored as media assets
  2. Archive old blog-posts.ts — Once verified, move src/lib/blog-posts.ts to a docs/archive/ folder
  3. Build article admin dashboard — UI to create/edit/delete articles without code
  4. SEO optimization — Add Open Graph images, structured data, meta tags per article
  • knowledge/runbooks/technical-ops/supabase-migration.md — General Supabase migration process
  • knowledge/runbooks/content-ops/ — Other content management runbooks
  • churchwiseai-web/src/lib/articles.ts — How articles are queried
  • churchwiseai-web/src/lib/blog-posts.ts — Source data being migrated