A directory of evangelical churches, built with Cloudflare Workers, Hono, and Turso.
This application provides a comprehensive directory of evangelical churches organized by county. The application features an interactive map, detailed church information, and data export capabilities.
- Interactive Map - Find churches near you with Google Maps integration
- County Organization - Browse churches organized by counties
- Church Details - View gathering times, contact info, and affiliations
- Data Export - Download church data in JSON, YAML, CSV, or XLSX formats
- Multi-language Support - Track churches serving in different languages
- Admin Dashboard - Manage churches, affiliations, and users
- AI-Powered Data Extraction - Automatically extract church information from websites
- Image Management - Upload and reorder multiple church images via Cloudflare Images
- Save and Continue - Efficiently review churches with one-click navigation to next church
- Runtime: Cloudflare Workers (edge computing)
- Framework: Hono (lightweight web framework)
- Database: Turso (SQLite at the edge)
- ORM: Drizzle ORM
- Authentication: Better-auth (self-hosted, Google OAuth)
- Styling: Tailwind CSS (via CDN)
- Package Manager: pnpm
- AI Integration: OpenRouter API with Google Gemini
- Image Storage: Cloudflare Images
For detailed setup instructions including all third-party services, see SETUP.md.
- Node.js 18+
- pnpm 10.11.0+
- Cloudflare account
- Turso database account
- Google OAuth credentials (for authentication)
Create a .dev.vars
file in the root directory:
TURSO_DATABASE_URL=your_database_url
TURSO_AUTH_TOKEN=your_auth_token
GOOGLE_MAPS_API_KEY=your_maps_api_key
CLOUDFLARE_ACCOUNT_ID=your_cloudflare_account_id
CLOUDFLARE_ACCOUNT_HASH=your_cloudflare_account_hash
CLOUDFLARE_IMAGES_API_TOKEN=your_cloudflare_images_token
OPENROUTER_API_KEY=your_openrouter_api_key
# Authentication (Better-Auth) - Required
BETTER_AUTH_SECRET=your-secret-key-here-min-32-chars-long
BETTER_AUTH_URL=http://localhost:8787
GOOGLE_CLIENT_ID=your-google-client-id-here
GOOGLE_CLIENT_SECRET=your-google-client-secret-here
# Install dependencies
pnpm install
# Start development server
pnpm dev
# The app will be available at http://localhost:8787
# Development
pnpm dev # Start development server
# Database
pnpm db:generate # Generate Drizzle migrations
pnpm db:push # Push schema changes to database
pnpm db:studio # Open Drizzle Studio
# Deployment
pnpm deploy # Deploy to Cloudflare Workers
├── src/
│ ├── components/ # React-like JSX components
│ ├── db/ # Database schema and configuration
│ ├── middleware/ # Authentication middleware
│ ├── styles/ # Tailwind class collections
│ ├── utils/ # Utility functions
│ └── index.tsx # Main application routes
├── drizzle/ # Database migrations
├── public/ # Static assets
└── wrangler.toml # Cloudflare Workers configuration
The application uses the following main tables:
churches
- Church information including location, contacts, and statuscounties
- Geographic countiesaffiliations
- Church networks/denominationschurch_affiliations
- Many-to-many relationshipchurch_gatherings
- Gathering times and detailschurch_images
- Church photos and imagespages
- Static pages (FAQ, About, etc.)settings
- Site configuration
- Listed - Publicly visible churches
- Ready to list - Pending review
- Assess - Under evaluation
- Needs data - Incomplete information
- Unlisted - Not publicly visible
- Heretical - Excluded from public data
- Closed - No longer active
GET /
- Home page with county listGET /counties/:path
- Churches in a specific countyGET /churches/:path
- Individual church detailsGET /map
- Interactive mapGET /networks
- Church affiliationsGET /data
- Data export page
GET /churches.json
- JSON formatGET /churches.yaml
- YAML formatGET /churches.csv
- CSV formatGET /churches.xlsx
- Excel format
GET /admin
- DashboardGET /admin/churches
- Manage churchesGET /admin/affiliations
- Manage affiliationsGET /admin/counties
- Manage countiesGET /admin/users
- Manage usersGET /admin/settings
- Site settings and configurationGET /admin/pages
- Manage static pagesPOST /admin/churches/:id/extract
- Extract church data from website
When editing churches, administrators can use the "Save and continue" button to:
- Save the current church
- Automatically navigate to the next church needing review (oldest update date)
- Continue reviewing churches in sequence without returning to the list
- Drag-and-drop image reordering
- Automatic address formatting and normalization
- Gathering time validation and normalization
- AI extraction for rapid data collection from church websites
The application includes an AI-powered feature to automatically extract church information from websites:
- Enter a church website URL in the edit form
- Click "Extract Info from Website"
- The system fetches and converts the webpage to text
- Google Gemini 2.5 Flash Lite (via OpenRouter) analyzes the content
- Extracted data is automatically filled into the form
- Phone numbers (formatted consistently)
- Email addresses
- Physical addresses (with proper capitalization)
- Gathering times with:
- Normalized time formats (e.g., "9 AM" not "9am")
- Day of week for multi-day schedules
- Brief notes (2-4 words max like "Traditional" or "Bible study")
- Social media links (Facebook, Instagram, YouTube, Spotify)
- Statement of Faith URL
- Sign up for a free OpenRouter account
- Generate an API key
- Add to
.dev.vars
and production secrets
The extraction uses Google Gemini 2.5 Flash Lite which has a 1M+ token context window, allowing it to process even very large church websites.
The application includes JSON-LD structured data on individual church pages (/churches/:path
) to improve search engine understanding and visibility. This includes:
- Church schema with address, contact information, and geo coordinates
- Event schemas for recurring church gatherings
- Organization affiliations
- Social media links
- Statement of Faith as a related CreativeWork
The domain and region in structured data are configurable via the site settings.
The application uses better-auth for self-hosted authentication with Google OAuth.
-
Create Google OAuth Application
- Go to Google Cloud Console
- Create a new project or select existing
- Enable Google+ API
- Create OAuth 2.0 credentials
- Add authorized redirect URI:
http://localhost:8787/auth/callback/google
-
Configure Environment Variables
# Better-Auth configuration (required) BETTER_AUTH_SECRET=your-secret-key-here-min-32-chars-long BETTER_AUTH_URL=http://localhost:8787 GOOGLE_CLIENT_ID=your-google-client-id-here GOOGLE_CLIENT_SECRET=your-google-client-secret-here
-
Set Up Database Schema
# Create better-auth tables pnpm better-auth:schema
-
Production Setup
# Set production secrets wrangler secret put BETTER_AUTH_SECRET wrangler secret put BETTER_AUTH_URL wrangler secret put GOOGLE_CLIENT_ID wrangler secret put GOOGLE_CLIENT_SECRET
# Set production secrets
wrangler secret put TURSO_DATABASE_URL
wrangler secret put TURSO_AUTH_TOKEN
wrangler secret put GOOGLE_MAPS_API_KEY
wrangler secret put CLOUDFLARE_ACCOUNT_ID
wrangler secret put CLOUDFLARE_ACCOUNT_HASH
wrangler secret put CLOUDFLARE_IMAGES_API_TOKEN
wrangler secret put OPENROUTER_API_KEY
wrangler secret put BETTER_AUTH_SECRET
wrangler secret put BETTER_AUTH_URL
wrangler secret put GOOGLE_CLIENT_ID
wrangler secret put GOOGLE_CLIENT_SECRET
# Deploy
pnpm deploy
Please read CLAUDE.md for detailed information about the codebase structure and development patterns.
This project is licensed under the MIT License - see the LICENSE file for details.
For questions or support, please open an issue on GitHub.