Dropaly is a mobile-first productivity application for capturing unstructured thoughts and turning them into actionable tasks, lists, and notes.
The project is currently in an architecture-first phase. The product scope is still evolving, but the repository already demonstrates the intended technical foundation: a TypeScript monorepo, a modular Fastify API, shared contracts, shared clients, web and mobile apps, authentication, PostgreSQL persistence, and an AI chat integration.
This is a work in progress.
Implemented foundations include:
- Monorepo structure with Turborepo and pnpm workspaces.
- Node.js/Fastify API with module-based route registration.
- Authentication with Better Auth and PostgreSQL persistence.
- Drizzle schema for auth and todos.
- OpenAPI contract generated from Fastify/Zod routes.
- Typed API client generated from the OpenAPI contract.
- Shared TanStack Query factories used by web and mobile clients.
- React web app with TanStack Router.
- Expo mobile app with Expo Router.
- AI chat endpoint using the Vercel AI SDK and a Google model adapter.
- Standardized API error handling with tests.
Still in progress:
- Product-specific capture, inbox, notes, lists, and project workflows.
- Production-ready billing configuration.
- Full migration history and deployment pipeline.
- UI/UX polish beyond the current architecture validation screens.
The long-term goal is to help users empty their mind quickly, then structure the result afterwards.
Core concepts:
- Capture: text or voice input that can produce structured suggestions.
- Cards: AI-generated proposals to validate before creating content.
- Inbox: triage area for tasks, notes, and lists without enough context.
- Tasks: actionable items with optional date, time, project, and priority.
- Short notes: plain text notes that should not become tasks.
- Lists: simple lists with todo/done items.
- Projects: lightweight containers for tasks, lists, notes, and sub-projects.
The backend is intentionally organized to keep framework code, application logic, and persistence concerns separate.
apps/api/src/app.tscreates the Fastify app, registers plugins, configures OpenAPI, and mounts modules under/api.- API modules live in
apps/api/src/modules/*. - The todo module follows a simple
routes -> service -> repositorysplit. - Fastify plugins inject shared dependencies such as
dbandauth. auth-contextresolves the current actor from Better Auth sessions.auth-guardsexposes route guards such asrequireAuthandrequireRole.error-handlerstandardizes validation, auth, HTTP, database, and unexpected errors.- Zod schemas are used for request/response validation and OpenAPI generation.
- Drizzle schemas live in
packages/dband are consumed by the API and auth packages.
The frontend apps consume the API through shared packages:
packages/api-contractstores the generated OpenAPI artifact.packages/api-clientprovides a typedopenapi-fetchclient.packages/api-queryprovides reusable TanStack Query options.apps/webandapps/mobileshare the same API contract and query layer.
- TypeScript
- Node.js
- Fastify
- PostgreSQL
- Drizzle ORM
- Better Auth
- OpenAPI
- openapi-fetch
- TanStack Query
- TanStack Router
- React
- Expo / React Native
- Vercel AI SDK
- Turborepo
- pnpm
- oxlint / oxfmt
- Vitest
dropaly/
|-- apps/
| |-- api/ # Fastify API host
| |-- web/ # React web app with TanStack Router
| `-- mobile/ # Expo mobile app with Expo Router
|-- packages/
| |-- api-client/ # Typed OpenAPI client
| |-- api-contract/ # Generated OpenAPI artifact
| |-- api-query/ # Shared TanStack Query factories
| |-- auth/ # Better Auth server configuration
| |-- config/ # Shared lint/format/TypeScript config
| |-- db/ # Drizzle schema and database tooling
| |-- ui/ # Shared design tokens/utilities
| |-- ui-mobile/ # Mobile UI primitives
| `-- ui-web/ # Web UI primitives
|-- docs/ # Project and agent documentation
|-- pnpm-workspace.yaml
|-- turbo.json
`-- README.md
- Node.js
^22.22.3 - pnpm
^11.1.3 - Docker, for the local PostgreSQL database
The expected versions are also declared in package.json and .tool-versions.
Create local env files from the provided examples:
cp apps/api/.env.example apps/api/.env
cp apps/web/.env.example apps/web/.env
cp apps/mobile/.env.example apps/mobile/.envFor Expo on a physical device, EXPO_PUBLIC_API_URL must point to the LAN URL of your machine, for example http://192.168.1.101:3000. The API must listen on SERVER_HOST=0.0.0.0 to be reachable from the device.
Install dependencies:
pnpm installStart the local database:
pnpm db:startApply the current schema during local prototyping:
pnpm db:pushStart all apps in development mode:
pnpm devDefault local URLs:
- API:
http://localhost:3000 - API docs:
http://localhost:3000/docs - Web app:
http://localhost:3001 - Expo dev server:
http://localhost:8081
pnpm dev: start all applications in development mode.pnpm dev:api: start only the Fastify API.pnpm dev:web: start only the web app.pnpm dev:mobile: start only the Expo app.pnpm typecheck: run TypeScript checks across the workspace.pnpm lint: run oxlint across the workspace.pnpm format:check: check formatting with oxfmt.pnpm test: run tests across packages that define tests.pnpm check: run typecheck, lint, format check, and tests.pnpm openapi:generate: regenerate the OpenAPI contract and typed API schema.pnpm db:start: start the local PostgreSQL container.pnpm db:stop: stop the local PostgreSQL container.pnpm db:push: push the current Drizzle schema to the database.pnpm db:generate: generate SQL migrations from Drizzle schema changes.pnpm db:migrate: apply generated Drizzle migrations.pnpm db:studio: open Drizzle Studio.
API routes define their request and response schemas with Zod. The OpenAPI artifact and typed client schema are generated from the API source.
After changing API routes or schemas, run:
pnpm openapi:generateCommit both generated files when they change:
packages/api-contract/openapi.jsonpackages/api-client/src/schema.d.ts
During early prototyping, pnpm db:push is useful because it applies the current schema directly to the local database. For a public project and future production-like workflow, prefer generated migrations.
Recommended initial migration workflow:
pnpm db:start
pnpm db:generate
pnpm db:migrateThen inspect and commit the generated migration files under packages/db/src/migrations/.
Recommended workflow for future schema changes:
- Modify Drizzle schema files in
packages/db/src/schema/*. - Run
pnpm db:generateto create a migration. - Review the generated SQL before applying it.
- Run
pnpm db:migratelocally. - Update affected API schemas, repositories, tests, and OpenAPI artifacts if needed.
- Commit the schema change and migration together.
Use db:push only for local experiments that are not meant to be preserved. Once migrations exist, avoid mixing persistent schema changes made by db:push with migration-based changes.
The repository uses:
oxlintfor linting.oxfmtfor formatting.tsc --noEmitfor type checking.vitestfor tests.lefthookfor local Git hooks.- GitHub Actions for CI.
Run the full local check with:
pnpm checkNo license has been selected yet.