Services Architecture

Background workers, Inngest events, and external integrations.

change-stream-worker

A long-running Node service that monitors PostgreSQL for changes via LISTEN/NOTIFY. When rows in FolioPlant or Folio are inserted, updated, or deleted, it sends events to Inngest for processing (e.g. search indexing).

  • Located in apps/change-stream-worker
  • Deployed on Railway
  • Requires database triggers: enable-database-notifications.sql
  • Health check at /health

Flow: PostgreSQL LISTEN → Change Stream Worker → Inngest Event → Background Job

render-worker

Express service that renders signage to PDF or ZIP. The dashboard sends a sign render request via Inngest; an Inngest function calls the render-worker HTTP endpoint. Output is uploaded to Vercel Blob.

  • Located in apps/render-worker
  • Deployed on Railway
  • Endpoint: POST /render (jobId, printSignUrl, templateType, etc.)
  • Health check at /health
  • Dashboard configures the worker URL via environment variables

Inngest Events

Event types defined in packages/events/src/inngest.ts:

  • ticket/create — Support ticket created
  • survey/complete — User survey completed
  • invite/create — Invitation created
  • onboarding/complete — User onboarding completed
  • identification/suggestion/plantFound — Plant suggestion found
  • user/create — User created
  • identification/create — Identification created
  • plant/create — Plant created
  • folioplant/insert, folioplant/update, folioplant/delete — From change-stream
  • folio/insert, folio/update, folio/delete — From change-stream
  • sign/render/requested — Sign render job

External Services

  • Algolia — Plant and folio search
  • Vercel Blob — Media storage, signage assets, rendered PDFs
  • Resend — Transactional email
  • Stripe — Payments, subscriptions
  • Clerk — Authentication
  • Sentry — Error monitoring
  • Plant.id / Brave / Trefle — Plant identification, search

plantfolio_mobile

Flutter mobile app in apps/plantfolio_mobile. Consumes shared backend via API/client logic.

← Back to overview