The idea
In Bulgaria people regularly look up whose name day it is, which days are off work, what the church commemorates and when school breaks fall. That information is scattered across sites that often disagree, and much of it moves every year: feasts tied to Orthodox Easter, compensating days off when a holiday lands on a weekend, and vacations set by an annual ministry order. I wanted one fast, accurate reference that computes these dates from rules instead of retyping them every year, and answers each question on its own indexable page.
Implementation
- Next.js 16 (App Router) front end with React 19, TypeScript, HeroUI v3 and Tailwind CSS v4. About 2,800 pages are prerendered at build time and refreshed through tag-based on-demand revalidation rather than timers.
- Express 5 REST API in TypeScript with Zod validation and per-route rate limits, on Neon serverless PostgreSQL through Drizzle ORM (22 tables, versioned migrations).
- A typed date-rule engine (fixed dates, offsets from Orthodox Easter, weekday-relative rules and ranges) that generates occurrences five years ahead on a cron schedule.
- Typo-tolerant search built on PostgreSQL trigram similarity (pg_trgm), with Latin-to-Cyrillic transliteration.
- Optional accounts with JWT authentication: an httpOnly cookie on the web, bearer tokens on mobile, and one-time email codes for verification and password resets.
- Public and personal iCalendar feeds that can be subscribed to from Google, Apple and Outlook calendars.
- Docker images built in GitHub Actions and deployed to my own VPS behind Caddy, with a health check and automatic rollback.
- An Android app in Expo and React Native that matches name days against the phone's contacts on the device, with local notifications and three home-screen widgets.
Challenges
- Movable dates: Orthodox Easter is calculated with the Julian (Meeus) method, and every feast that depends on it is expressed as a rule, so nothing has to be retyped each year.
- Compensating days off: when a public holiday falls on a weekend, the Labour Code moves the day off to the next working day. The implementation is checked against the officially announced days for 2021–2026.
- School vacations are published late, so future dates are predicted from a back-tested rule and labelled as forecasts until the ministry confirms them.
- The scale-to-zero database kept waking up: calendar apps polling the iCal feeds caused about 70% of wake-ups. Feeds are now cached in API memory and pages revalidate only when the data actually changes.
- Builds prerender against the live API, so CI waits until the API reports the new commit before building the site, and a deploy lock prevents overlapping releases.
Result
bgcal.eu is live and deploys automatically on every push to main. Its sitemap lists 3,320 URLs, including 366 day pages, 1,813 name pages and 834 Orthodox feast pages, and five public calendar feeds can be subscribed to from any calendar app. Accounts let visitors save names, filter categories and rearrange their home page. The Android app has its signing setup and Play Store assets ready but is not published yet.
Gallery
Version history
Weeks after the first build, I rewrote the front end in Next.js and TypeScript for server rendering and SEO, and moved the API from raw SQL to Drizzle ORM after trying Prisma side by side. It ran on HeroUI v2 until the 2026 redesign.
- Next.js App Router with TypeScript
- Drizzle ORM chosen over Prisma after a side-by-side trial
- Date-rule engine with cron-based recalculation
- Upgraded to Next.js 16 in December 2025
A client-rendered React app built with Vite and HeroUI v2, backed by an Express API querying Neon PostgreSQL with the pg driver. Replaced within weeks because client-side rendering was a poor fit for a reference site that depends on search traffic.
- React single-page app with React Router
- Express API with raw SQL on Neon
Key Features
- Today dashboard with name days, church commemorations, sunrise and sunset, and moon phase
- Year calendar colour-coded by category
- A page for every day of the year and every name, with meaning, origin and national statistics
- Orthodox calendar with a day-by-day fasting calendar
- School vacations by grade, with forecasts for future school years
- iCalendar subscriptions and an optional personalised account
- Dark and light themes
Data Sources
- Holy Synod of the Bulgarian Orthodox Church calendar
- Labour Code, art. 154(2), for compensating days off
- Ministry of Education vacation orders
- National Statistical Institute data on names in Bulgaria
