AniCue
Tracks over 106,000 anime and manga titles and tells people the moment a new episode or chapter lands, through push, Discord, or their own calendar.
- 106,000+
- Titles tracked
- 3
- Delivery channels
- 24h
- Per-title cache window

Status
Live · paying subscribers
Platforms
Android (React Native) + web (Next.js)
Role
Sole designer, engineer, and operator
Stack
106,000+ anime and manga titles tracked
Idempotent notification retry queue with a dead-letter table for terminal failures
Scheduled release checking across AniList, Consumet, and MangaPlus
Two-tier caching: a daily bulk-discovery cron plus a 24-hour per-title cache
Delivery over FCM push, Discord webhooks, and a live .ics calendar feed
The problem
Release information for anime and manga is scattered across official schedules, streaming platforms, and community scanlation groups. Following a dozen series means checking a dozen places, and the schedules move.
AniCue centralises that into one tracker: follow a title once, then get told when something drops, through whichever channel you already pay attention to.
Architecture
The client is React Native; the backend is Supabase: PostgreSQL with Deno Edge Functions. The data flow is strictly one-directional and strictly enforced: component → Zustand store → service → Supabase or external API. Components never call Supabase directly.
That constraint sounds bureaucratic until the app has four stores, nine services, and three upstream APIs that each fail differently. Keeping the boundary rigid is what makes a failure diagnosable. There is exactly one layer where a given class of bug can live.
Delivery reliability
A release tracker that misses a release is worthless, so notification delivery is the part of this system that got the most design attention.
In-app writes and FCM push sends both enqueue to an idempotent retry queue on failure. Idempotency comes from a dedupe key shaped as channel:user:release:event, which makes duplicate retry rows for the same delivery event structurally impossible rather than merely unlikely. The queue tracks attempt counts against a maximum, schedules the next attempt, and records the last error and error code for diagnosis.
Anything that exhausts its attempts lands in a dead-letter table with its full payload and error diagnostics, so it can be inspected and replayed by hand instead of vanishing. A dedicated Edge Function drains the queue.
Caching against third-party limits
Upstream APIs have rate limits, and a catalogue of this size will hit them. AniCue runs two separate caching strategies rather than one compromise.
A daily cron-driven Edge Function refreshes bulk discovery data (trending, currently airing, and upcoming), deduplicated by AniList ID via a unique constraint on (external_id, external_source), so a title appearing in several categories merges instead of duplicating. Individual title lookups from search use a separate 24-hour cache.
Splitting them means browsing traffic and search traffic scale independently, and neither can exhaust the other's budget.
Commercially
AniCue is monetised through RevenueCat subscriptions and AdMob, and has paying subscribers. Sentry and PostHog cover errors and product analytics.
Owning billing, refunds, and support changes how you design a system: every unreliable notification is a support ticket, and every support ticket has a name attached to it. The retry queue exists because of that, not because of an architecture diagram.
