Skip to content
All products
Live · paying subscribers

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
AniCue home page showing a trending anime hero, a live catalogue count of 106,492 tracked titles, and a latest-news row.

Status

Live · paying subscribers

Platforms

Android (React Native) + web (Next.js)

Role

Sole designer, engineer, and operator

Stack

React NativeNext.jsSupabasePostgreSQLDeno Edge FunctionsZustandTamaguiFCMRevenueCat
01At a glance
01

106,000+ anime and manga titles tracked

02

Idempotent notification retry queue with a dead-letter table for terminal failures

03

Scheduled release checking across AniList, Consumet, and MangaPlus

04

Two-tier caching: a daily bulk-discovery cron plus a 24-hour per-title cache

05

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.

Delivery path
UpstreamAniList · ConsumetMangaPlusRelease checkEdge Functioncron-scheduledDeliverin-app writeFCM push sendDeliverednotification shownRetry queuededupe: channel:user:release:eventattempts n / max · next_attempt_atlast_error · error_codeDead letterfull payload + diagnosticsinspected and replayed by handsuccessfailureretry after backoffattempts exhausted
Both delivery channels enqueue to the same idempotent retry queue on failure. The dedupe key makes a duplicate row for one delivery event structurally impossible; anything that exhausts its attempts lands in the dead-letter table with its payload intact, rather than disappearing.

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.

Next project

RoastPit

AI debate arena