The Problem
Freelancers must juggle invoicing, time‑tracking, receipt reconciliation, file storage and financial reporting across many SaaS tools. Switching between Stripe, Plaid, Supabase, email, and a custom UI creates context‑switch cost and data inconsistency.
What This Does
midday is a monorepo that bundles the back‑end API, a Next.js dashboard, a Tauri desktop client and supporting libraries into a single code base.
- The API lives under
apps/api/src/– e.g.apps/api/src/ai/agents/main.tswires AI‑driven assistants, whileapps/api/src/rest/routers/*expose REST endpoints for invoices, bank accounts, and file vaults. - The dashboard UI is in
apps/dashboard/and uses React, Tailwind, and the shared UI primitives frompackages/ui/. - Core business logic (invoice templates, DB queries, notification helpers) resides in
packages/and is imported by both API and UI layers.
How It Is Wired
Execution starts at the API entry point apps/api/src/index.ts, which creates a Bun server, registers middleware (apps/api/src/rest/middleware/*) and mounts the tRPC router defined in apps/api/src/trpc/routers/_app.ts.
- Request flow – A typical request (e.g.
GET /api/invoices) hits the router inapps/api/src/rest/routers/invoices.ts, which calls service functions inapps/api/src/services/supabase.ts. The Supabase client (createClient) is defined inapps/api/src/services/supabase.ts(3 functions, called from 8 places). This client performs the only database write/read observed (packages/supabase/src/queries/index.ts).
- AI agents – Calls to
apps/api/src/ai/agents/*are triggered from the same API entry point via themain.tsagent dispatcher. Each agent ultimately uses utility functions such asgetUserContext(apps/api/src/ai/utils/get-user-context.ts) andtool-helpersto fetch data from Supabase or external APIs.
- File I/O – The only filesystem interaction is in
apps/dashboard/src/components/bank-connect-button.tsx(reads/writes a file for the “Connect Bank” flow).
- Cryptographic work – A single secret‑generation helper lives in
packages/ui/src/utils/cn.ts(used by 25 files).
- High‑impact hubs –
packages/ui/src/utils/indexis imported by 50 modules (instability 0.04), making it a low‑risk but critical utility surface. Conversely,apps/api/src/trpc/routers/_appimports 29 other modules (instability 0.97) and is a change‑sensitive hub.
- Call concentration – The function
cnis called from 25 files;createClientfrom 11 places;setfrom 10 places. These are the narrowest blast‑radius points that, if altered, affect many downstream components.
No circular import cycles were detected, and the internal call graph contains 109 resolved edges, indicating a relatively flat dependency structure.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/midday.git
cd midday
# Install dependencies (Bun is the package manager used)
bun install
# Build and run the API container
docker build -f apps/api/Dockerfile -t midday-api .
docker run -p 3000:3000 midday-api
# Start the dashboard locally
cd apps/dashboard
bun dev # uses next.config.ts and tailwind.config.ts
- Env files –
apps/api/.env-templatelists required variables (Supabase keys, OpenAI/Gemini tokens, Resend API). Copy to.envbefore starting. - Database – The API expects a Supabase instance; the schema is defined in
apps/api/src/schemas/*.ts. - Desktop client – Build with Tauri:
cd apps/desktop && bun tauri dev(requires Rust toolchain, seeapps/desktop/src-tauri/Cargo.toml).
If any of these files are missing (e.g., a concrete docker-compose.yml), the steps above reflect the only documented build paths.
Real‑World Use
A freelancer runs the API on Fly.io, the dashboard on Vercel, and connects their bank via Plaid. When a new receipt lands in the inbox, the API webhook (apps/api/src/rest/routers/webhooks/*) stores the file, triggers apps/api/src/ai/agents/transactions.ts to classify it, and updates the Supabase transactions table via packages/supabase/src/queries/index.ts. The dashboard instantly reflects the new entry thanks to Supabase realtime.
Code Health & Issues
| Severity | Issue | Location |
|---|---|---|
| High | Pin third‑party GitHub Actions to a commit SHA | .github/workflows/* |
| High | CI does not run the test suite | .github/workflows/* |
| Medium | Least‑privilege GITHUB_TOKEN permissions missing | .github/workflows/beta-dashboard.yaml |
| Medium | No Dependabot/Renovate configuration | Repository root |
| Medium | Docker base image not pinned by digest | apps/api/Dockerfile |
| Medium | No dependency‑vulnerability scan in CI | .github/workflows/* |
| Medium | Large binary (notification-center.gif) stored in repo | apps/website/public/images/ |
| Medium | Dockerfile runs as root | apps/api/Dockerfile |
| Low | Missing convention files (.editorconfig, .gitattributes) | Repository root |
These findings come directly from the static analysis pipeline; no additional subjective issues are listed.
The Bottom Line
midday delivers a full‑stack freelance‑business platform with a clear separation of API, UI and shared libraries, and its code base is reasonably modular aside from a few high‑instability hubs. Production readiness is hampered by CI gaps, unpinned dependencies and container hardening omissions. Teams comfortable managing Supabase, Bun and Docker can adopt it, but should address the highlighted health items before scaling.