---
title: "How Midday Is Built"
description: "Repository structure, Bun + Turborepo build system, dependencies, testing, and deployment"
image: "https://tractiongrowth.com/og/open-source/midday-ai-finance/how-it-is-built/image.png"
---

# How Midday Is Built

# How Midday Is Built

Midday is built as a Bun monorepo managed by Turborepo, with separate applications for the dashboard, API, worker, website, and desktop client sharing packages for data, auth, and product logic. This page explains the repository layout, tooling, dependencies, and deployment approach that ship the product.

<Callout title="For Product Managers">
  This page answers "how does the engineering team ship Midday?" — the tools, processes, and infrastructure that turn source code into a running fintech product. Understanding this helps you estimate customization effort and deployment complexity.
</Callout>

## Repository Structure

Midday is a **Bun monorepo** with **Turborepo** for build orchestration. 5 apps and 27 packages live in a single repository.

```
midday/
├── apps/
│   ├── dashboard/        ← Next.js 16 — main product UI (port 3001)
│   ├── api/              ← Hono + tRPC — API server (port 3003)
│   ├── worker/           ← BullMQ — background job processor
│   ├── website/          ← Next.js — marketing site (port 3000)
│   └── desktop/          ← Tauri 2 — native macOS app
├── packages/
│   ├── db/               ← Drizzle ORM schema + 36 migrations
│   ├── supabase/         ← Supabase client, types, helpers
│   ├── ui/               ← Radix + Tailwind component library
│   ├── banking/          ← Plaid + GoCardless + Teller + EnableBanking
│   ├── invoice/          ← PDF generation, templates, calculations
│   ├── accounting/       ← Xero + QuickBooks + Fortnox sync
│   ├── inbox/            ← Gmail + Outlook email sync
│   ├── documents/        ← OCR, classification, embeddings
│   ├── insights/         ← AI financial analysis
│   ├── email/            ← 18 React Email templates
│   ├── notifications/    ← Resend delivery
│   ├── trpc/             ← tRPC client/server setup
│   ├── cache/            ← Redis caching layer
│   ├── jobs/             ← Trigger.dev task definitions
│   ├── job-client/       ← BullMQ queue client
│   ├── events/           ← OpenPanel analytics
│   ├── categories/       ← Transaction categorization + tax rates
│   ├── customers/        ← Customer enrichment (Exa AI)
│   ├── import/           ← Transaction CSV import
│   ├── plans/            ← Subscription plan definitions (Polar)
│   ├── app-store/        ← Integration apps (Slack, WhatsApp, etc.)
│   ├── location/         ← Countries, currencies, timezones
│   ├── encryption/       ← Encryption utilities (jose)
│   ├── health/           ← Health check probes
│   ├── logger/           ← Pino structured logging
│   ├── utils/            ← Shared utilities
│   ├── desktop-client/   ← Tauri client utilities
│   └── tsconfig/         ← Shared TypeScript config
├── turbo.json            ← Turborepo task configuration
└── package.json          ← Root config (Bun 1.3.10)
```

### The 5 Applications

| App           | Technology                  | Purpose                                                        |
| ------------- | --------------------------- | -------------------------------------------------------------- |
| **dashboard** | Next.js 16.1.6 (React 19)   | Main product — transactions, invoicing, time tracking, AI chat |
| **api**       | Hono 4.11.4 + tRPC 11.10.0  | API server — tRPC for dashboard, REST for desktop/third-party  |
| **worker**    | BullMQ + Hono               | Background processor — bank sync, documents, inbox, invoices   |
| **website**   | Next.js 16.1.6              | Marketing site with MDX docs and AI chat                       |
| **desktop**   | Tauri 2 + Vite 7 + React 19 | Native macOS app with deep links and file system               |

<Callout type="info" title="Why Bun?">
  Midday uses **Bun** instead of Node.js as the JavaScript runtime. Bun offers significantly faster startup times (important for serverless/Railway), native TypeScript execution (no transpilation step), a built-in test runner (replacing Jest/Vitest), and faster package installation. The Dockerfiles use `oven/bun:1.3.10` as the base image.
</Callout>

## Build System

### Turborepo Pipeline

```
bun install                     ← installs all dependencies (Bun workspaces)
    │
    ▼
turbo build                     ← orchestrates parallel builds
    │
    ├──► @midday/db              (Drizzle schema → types)
    ├──► @midday/ui              (component library → bundled)
    ├──► @midday/trpc            (tRPC config → types)
    │
    ├──► dashboard               (Next.js build → standalone output)
    ├──► api                     (Bun bundle → dist/)
    ├──► worker                  (Bun bundle → dist/ + workbench UI)
    └──► website                 (Next.js build → standalone output)
```

### Code Quality

| Tool                 | Purpose                                           |
| -------------------- | ------------------------------------------------- |
| **Biome 2.4.4**      | Linting + formatting (replaces ESLint + Prettier) |
| **TypeScript 5.9.3** | Type checking across all packages                 |
| **Bun test**         | Built-in test runner (51 test files)              |

## Dependency Map

### Dashboard Dependencies

| Package            | Version | Purpose                       |
| ------------------ | ------- | ----------------------------- |
| Next.js            | 16.1.6  | Full-stack React framework    |
| React              | 19.2.3  | UI framework                  |
| Zustand            | 5.0.11  | Minimal global state          |
| TanStack Query     | 5.90.21 | Server-state caching          |
| TanStack Table     | 8.21.3  | Data tables                   |
| React Hook Form    | 7.66.1  | Form management               |
| nuqs               | 2.8.8   | URL search params state       |
| Recharts           | 3.6.0   | Charts and data visualization |
| Framer Motion      | 12.34.0 | Animations                    |
| @dnd-kit           | latest  | Drag and drop                 |
| Geist              | latest  | Font family                   |
| @sentry/nextjs     | 10.x    | Error tracking                |
| ai + @ai-sdk/react | 5.0.87  | AI chat UI                    |

### API Dependencies

| Package                    | Version | Purpose                 |
| -------------------------- | ------- | ----------------------- |
| Hono                       | 4.11.4  | HTTP framework          |
| tRPC                       | 11.10.0 | Type-safe RPC           |
| jose                       | latest  | JWT verification (JWKS) |
| hono-rate-limiter          | 0.4.2   | Rate limiting           |
| @scalar/hono-api-reference | latest  | API documentation       |
| ai + @ai-sdk/openai        | 5.0.87  | AI assistant tools      |
| Stripe                     | 20.1.0  | Payment processing      |
| Polar                      | 0.45.1  | Subscription billing    |
| @modelcontextprotocol/sdk  | 1.0.0   | MCP integration         |

### Infrastructure

| Service               | Purpose                                      |
| --------------------- | -------------------------------------------- |
| Supabase (PostgreSQL) | Primary database + auth + storage + realtime |
| Redis (Upstash)       | BullMQ queues + API cache                    |
| Cloudflare R2         | Institution logos and static assets          |
| Typesense             | Full-text search                             |

## Deployment

### Production Architecture

| Service           | Platform    | Method                             |
| ----------------- | ----------- | ---------------------------------- |
| **Dashboard**     | Railway     | Docker (standalone Next.js on Bun) |
| **API**           | Railway     | Docker (Hono on Bun)               |
| **Worker**        | Railway     | Docker (BullMQ on Bun)             |
| **Website**       | Vercel      | Serverless Next.js                 |
| **Jobs**          | Trigger.dev | Managed task execution             |
| **Email preview** | Vercel      | React Email dev server             |

### Docker Images

All three Railway services use `oven/bun:1.3.10` as the base:

```
# Example: apps/dashboard/Dockerfile
FROM oven/bun:1.3.10 AS base
  → turbo prune @midday/dashboard
  → bun install
  → bun run build
  → Standalone Next.js output
  → PORT from Railway, GIT_COMMIT_SHA for Sentry
```

### CI/CD Pipeline

Three GitHub Actions workflows:

| Workflow           | Trigger          | Steps                                                                         |
| ------------------ | ---------------- | ----------------------------------------------------------------------------- |
| **production.yml** | Push to `main`   | Lint → Build → Typecheck → Test → Deploy (Railway/Vercel/Trigger.dev)         |
| **staging.yml**    | Push to non-main | Same validation → Deploy to staging environments                              |
| **desktop.yml**    | Manual dispatch  | Tauri build (macOS aarch64 + x86\_64) → Code sign → Notarize → GitHub Release |

**Smart deploys:** `turbo build --affected` detects which services changed and only deploys those.

### Key Environment Variables

| Category       | Variables                                                   |
| -------------- | ----------------------------------------------------------- |
| **Supabase**   | `SUPABASE_URL`, `SUPABASE_SERVICE_KEY`, `SUPABASE_ANON_KEY` |
| **Database**   | `DATABASE_URL` + read replicas (FRA/SJC/IAD)                |
| **Banking**    | `PLAID_*`, `GOCARDLESS_*`, `TELLER_*`, `ENABLE_BANKING_*`   |
| **Payments**   | `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET`                |
| **AI**         | `OPENAI_API_KEY`, `GOOGLE_GENERATIVE_AI_API_KEY`            |
| **Email**      | `RESEND_API_KEY`                                            |
| **Search**     | `TYPESENSE_*`                                               |
| **Storage**    | `R2_*` credentials for Cloudflare R2                        |
| **Monitoring** | `SENTRY_*`, `OPENPANEL_*`                                   |
| **Jobs**       | `TRIGGER_SECRET_KEY`, `REDIS_QUEUE_URL`                     |

## Background Job Systems

Midday uses **two** background job systems:

### Trigger.dev (Scheduled Tasks)

Trigger.dev handles jobs that need scheduling, retries, and observability:

* Bank sync polling
* Invoice recurring scheduler
* Document processing pipeline
* Inbox email sync
* Notification delivery
* Team onboarding sequences

### BullMQ (Queue-Based Jobs)

BullMQ handles high-throughput queued work:

* Transaction import batches
* Inbox provider sync
* Notification queuing
* Document processing queue

The Worker app (`apps/worker/`) includes a **Workbench** admin dashboard (accessible at `/admin`) for monitoring queue health.

## Testing

| Scope        | Tool            | Files                                     |
| ------------ | --------------- | ----------------------------------------- |
| **Unit**     | Bun test runner | 51 `.test.ts` files                       |
| **API**      | Bun test runner | tRPC router tests, REST router tests      |
| **Packages** | Bun test runner | banking, invoice, import, documents, etc. |

Tests use Bun's native test runner — no Jest, Vitest, or Playwright dependencies needed.

## What's Next

<Cards>
  <Card title="What Is Midday?" description="What Midday does, who it's for, and why it exists." href="/open-source/midday-ai-finance" />

  <Card title="Midday Architecture Overview" description="System design, data model, and background-job architecture." href="/open-source/midday-ai-finance/architecture" />

  <Card title="Midday Workflows and Data Flows" description="Bank sync, invoicing, receipt matching, and AI assistant." href="/open-source/midday-ai-finance/workflows" />
</Cards>

