Applications · entity catalog

demeter app

Authored subsystem deep-dive for demeter, layered on the code-linked entity catalog — what each system is, why it exists, and how it fits.

authored deep-dive
3entities2layers3deep-dives

On this page

The apps/demeter/ area: the three deployable client/server applications of Demeter, a home-gardening platform — a Fastify REST API plus an Expo mobile app and a Vite web dashboard that both consume it.

What this area is#

Demeter is a home-gardening product: users model their gardens and beds, track plantings through their lifecycle, log harvests and journal observations, manage garden tasks, wire up IoT soil/environment sensors, watch the weather, and lean on a gardening assistant. The apps/demeter/ directory holds the three deployable applications that surface that product — it is the app tier, not the domain logic. The reusable domain packages live separately under libs/demeter/* (for example @demeter/core, which owns the Drizzle schema and Zod shapes, alongside feature libraries like analytics, automation, biodynamic, community, and more).

The three apps are exactly the projects git ls-files reports under this tree: @demeter/api (the backend, apps/demeter/api), @demeter/mobile (the React Native client, apps/demeter/mobile), and @demeter/web (the browser dashboard, apps/demeter/web). All three carry the scope:demeter / type:app Nx tags; the API additionally tags layer:service, mobile tags platform:mobile, and web tags platform:web (see each project.json).

The relationship between them is a classic one-backend / two-frontend split. @demeter/api is the single source of truth: it exposes a versioned REST surface under /v1/* (the route groups registered in apps/demeter/api/src/routes/index.ts are gardens, plants, plantings, tasks, harvests, sensors, weather, observations, ai, and community, plus unauthenticated /health* probes). Both @demeter/mobile and @demeter/web are pure clients of that surface — each ships its own fetch-based API client (apps/demeter/mobile/src/api/client.ts and apps/demeter/web/src/api/client.ts) and a parallel set of React Query hooks per domain (src/api/hooks/use*.ts in both). They do not share code directly; they share the wire surface the API defines.

How it fits the wider system#

@demeter/api is the only node here that reaches into the rest of the monorepo: its package.json depends on @demeter/core (the Drizzle schema — demeterGardens, demeterGardenBeds, demeterGardenMembers, etc., imported by the stores) plus the platform libraries @oshun/database, @oshun/errors, and @oshun/logging. It persists to PostgreSQL via Drizzle, caches/rate-limits via Redis, and integrates the public Open-Meteo weather service. The two client apps depend on nothing in the workspace at the app boundary — they talk HTTP to the API and store tokens locally (Expo SecureStore on mobile, localStorage on web), so the boundary between this area and the rest of the platform is the REST contract plus @demeter/core/@oshun/* on the server side. The apps/demeter/docker-compose.yml and the API's Dockerfile describe how the backend runs against its Postgres/Redis dependencies.

Entity catalog (3)#

The 3 tracked Nx projects in demeter, each a code-linked entity node — package, type, source path, declared targets, and its internal dependency graph (depends-on / used-by, resolved from the package manifests, §6/§8), read from the project graph. Grouped by architectural layer; walk the dependency links to travel the system. 3 of these carry an authored deep-dive (what / why / how it fits); the rest are generated scaffolds awaiting one.

service (1)#

app

@demeter/api

#

Demeter Home Gardening API — Fastify-based REST API

The Fastify backend (apps/demeter/api), described by its own README as the "Demeter Home Gardening API — Fastify-based REST API." Its architecture is a real ordered plugin stack assembled in src/app.ts's buildServer(): request-context, security headers, CORS, error handler, Swagger/OpenAPI, JWT auth, a PostgreSQL+Drizzle database plugin, Redis, and rate limiting, with all route groups registered last. Persistence is genuinely relational — the src/db stores (e.g. garden-store.ts) issue Drizzle queries against the @demeter/core tables, map between the public API shape and the schema (garden location to lat/long/elevation/aspect columns, bed metres to centimetres, soil type to a jsonb soil_composition), guard UUIDs, strip NUL bytes, and use soft-delete (deletedAt). Six checked-in migrations live under drizzle/ (0000_demeter_initial through 0005_community_tables). External integrations are dependency-injectable seams: src/weather/weather-provider.ts ships a real Open-Meteo client (mapping WMO codes and wind degrees, frost-risk thresholds) that tests override with a deterministic provider, and src/auth/oauth-verifiers.ts is similarly injectable. The /v1/ai/* routes (src/routes/ai.ts) are honestly heuristic, not an LLM pass-through — and the file says so: plant identification is a trait-matching scoring algorithm over a built-in profile table, diagnosis is a symptom-disease correlation engine, plus planting-plan generation, companion-aware layout optimization, harvest-timing prediction, and a keyword-scored chat knowledge base. Testing is extensive and layered: unit specs in src/__tests__/, a full test/integration/ suite, and a test/performance/ suite with its own harness and benchmarks.

buildtestlinttypecheckdevstart
layer: servicescope: demeterowner: @GreyChimp

unclassified (2)#

app

@demeter/mobile

#

Demeter - Home Gardening Platform Mobile App

The Expo / React Native client (apps/demeter/mobile) — "Demeter - Home Gardening Platform Mobile App." App.tsx is a real entry point that wires SafeAreaProvider, React Query, and the navigation container, restores the session from stored tokens, and wires the API client's 401 handler to force logout. The app is broad and implemented, not scaffolded: src/screens/ contains full screen sets for gardens, plants, journal, tasks, sensors, weather, ai (plant identification/diagnosis, assistant, recommendations feed), community, settings, and auth, navigated through src/navigation/AppNavigator.tsx. Server access goes through src/api/client.ts — a fetch client with Expo SecureStore token storage, deduplicated 401 refresh, exponential-backoff retries, and per-domain React Query hooks under src/api/hooks/. The standout is the offline layer in src/services/offline/: SyncEngine.ts is a real priority queue (CRITICAL→LOW enum), persisted to AsyncStorage, with deduplication by key, batched processing, per-item exponential-backoff retry, status listeners, and auto-sync — accompanied by ActionQueue, ConflictResolver, OfflineStorage, PlantCache, WeatherCache, SensorSync, and OfflinePhotoManager. There is also a src/services/widgets/ set (Siri Shortcuts, Google Assistant, Quick Actions, Watch, Widget data, Notification actions) and Jest test suites co-located with the screens and services.

testlinttypecheckrun-androidrun-iosstart
scope: demeterowner: @GreyChimp
app

@demeter/web

#

The Vite + React browser dashboard (apps/demeter/web). src/App.tsx initializes the auth store and theme then mounts a RouterProvider; src/router.tsx defines a large lazy-loaded route tree — eager auth pages plus protected sections for dashboard, gardens (including a planner and a GardenPlanner3DPage), plants (companion matrix, planting calendar, pest/disease browser, comparison), tasks (calendar, recurring, scheduler, templates, bulk, export), harvests, sensors/IoT (visualization, automation rules, scenes, alerts, device pairing, real-time monitor, history), weather, journal, analytics (yield, cost, resource, environmental, comparison, report builder, export, printable), community, settings, and profile. It talks to the same backend via src/api/client.ts (base URL from VITE_API_URL, falling back to /api, with localStorage tokens and 401 auto-refresh) and per-domain hooks under src/api/hooks/. The component library is real (src/components/ charts, forms, layout, table, ui) and styling is Tailwind. One honest caveat lives in src/stores/expensesStore.ts: it is a Zustand store backed by localStorage with seed data, explicitly labelled in its own docstring as a stand-in for CostAnalysisPage "before the production expense API is available" — a documented local placeholder, not a claimed server integration. Quality is covered by src/__tests__/ (vitest) and an e2e/ Playwright suite spanning auth, gardens, plants, tasks, sensors, community, journal, navigation, and plant-id flows.

buildtestlinttypecheckservee2epreview
scope: demeterowner: @GreyChimp