Skip to content

Navigation Menu

Sign in
Sign up

Repository files navigation

FormOS πŸ—”

A full-stack form builder with a retro operating-system soul β€” build forms on a Windows-95-flavored desktop, publish them, share a link + QR, and watch responses roll in.

Built for the ChaiCode "build a similar project" challenge on the exact restricted stack: Turborepo Β· Next.js Β· tRPC Β· Drizzle ORM Β· PostgreSQL Β· Scalar (OpenAPI).

All business logic is implemented as tRPC procedures β€” there are no REST endpoints for forms, submissions, views, or analytics. The only non-tRPC routes are the single tRPC HTTP handler and the OpenAPI/docs endpoints.


✨ Features

  • Form editor / canvas β€” create a form, add fields dynamically, edit labels, help text, placeholders, required state, and options, reorder fields (▲さんかく/β–Ό), duplicate, delete, pick a window theme, Save draft and Publish. Includes a live interactive preview.
  • 8 field types β€” short text, long text, email, rating (stars), dropdown, multiple choice, checkbox, and date.
  • Public form β€” /forms/[slug] renders the published schema, validates required fields (client and server), stores the response as JSON, and tracks views. Fully anonymous.
  • Sharing β€” one-click public link + a QR code for any published form.
  • Analytics dashboard β€” total views, total submissions, completion rate, average completion time, a paginated response table, and per-field breakdowns for rating / dropdown / multiple-choice / checkbox fields.
  • Retro OS UI β€” hand-built window chrome, beveled controls, group boxes, a taskbar with a live clock, retro scrollbars, and playful microcopy. Fully responsive down to mobile.
  • Scalar API docs β€” /docs renders an OpenAPI 3.1 spec that is generated by introspecting the live tRPC router (so it can't drift from the procedures).
  • Loading / empty / error states everywhere.

🧱 Tech stack

Layer Choice
Monorepo Turborepo + pnpm workspaces
Frontend Next.js 15 (App Router) + React 19 + TypeScript
API tRPC v11 (procedures only β€” no REST business API)
Validation Zod (shared client + server)
ORM Drizzle ORM (postgres-js driver)
Database PostgreSQL 16
API docs Scalar over an introspected OpenAPI 3.1 document
Styling Hand-built retro CSS design system + Tailwind for responsive layout
Local DB Docker Compose

πŸ“ Project structure

formos/
β”œβ”€ apps/
β”‚ └─ web/ # Next.js app (UI + the only HTTP routes)
β”‚ └─ src/
β”‚ β”œβ”€ app/
β”‚ β”‚ β”œβ”€ page.tsx # Dashboard (retro desktop)
β”‚ β”‚ β”œβ”€ editor/[id]/ # Form editor
β”‚ β”‚ β”œβ”€ forms/[slug]/ # Public form (anonymous)
β”‚ β”‚ β”œβ”€ analytics/[id]/ # Analytics dashboard
β”‚ β”‚ β”œβ”€ docs/route.ts # Scalar docs viewer
β”‚ β”‚ └─ api/
β”‚ β”‚ β”œβ”€ trpc/[trpc]/route.ts # THE single tRPC endpoint
β”‚ β”‚ └─ openapi.json/route.ts # OpenAPI spec (for Scalar)
β”‚ β”œβ”€ components/ # dashboard, editor, runner, analytics...
β”‚ β”œβ”€ lib/trpc/ # tRPC React client + provider
β”‚ └─ server/api.ts # server-side tRPC caller (SSR)
β”œβ”€ packages/
β”‚ β”œβ”€ api/ # tRPC routers, context, Zod inputs, OpenAPI generator
β”‚ β”œβ”€ db/ # Drizzle schema, client, migrations, seed, field model
β”‚ β”œβ”€ ui/ # Retro OS component kit + retro.css
β”‚ └─ config/ # Shared tsconfig presets
β”œβ”€ docker-compose.yml # Local PostgreSQL
β”œβ”€ turbo.json
└─ pnpm-workspace.yaml

Request flow

React components ──(@trpc/react-query)──▢ POST/GET /api/trpc/<proc>
 β”‚
 packages/api appRouter (Zod-validated)
 β”‚
 packages/db Drizzle ──▢ PostgreSQL

/api/openapi.json introspects the same appRouter to produce the spec Scalar renders at /docs.


πŸš€ Quick start

Prerequisites: Node β‰₯ 20, pnpm β‰₯ 10, and Docker (for local PostgreSQL).

# 1. Install dependencies
pnpm install
# 2. Configure environment
cp .env.example .env
# (DB defaults match docker-compose.yml. Set ADMIN_TOKEN to any passcode β€”
# you'll enter it once to unlock the builder/analytics.)
# 3. Start PostgreSQL
docker compose up -d
# 4. Apply the schema + load demo data
pnpm db:migrate # create the tables
pnpm db:seed # 1 demo user, 3 forms, ~78 views, ~53 submissions
# 5. Run the app
pnpm dev

Open http://localhost:3000 πŸŽ‰

First run without seeding? No problem β€” a demo operator and your first form are created on demand.


πŸ“œ Scripts

Command What it does
pnpm dev Run the web app in dev mode
pnpm build Production build of every package
pnpm typecheck Type-check all packages
pnpm lint ESLint across the repo
pnpm db:generate Generate a new SQL migration from the Drizzle schema
pnpm db:migrate Apply migrations to the database
pnpm db:push Push the schema directly (handy in dev)
pnpm db:seed Reset + seed demo data
pnpm db:studio Open Drizzle Studio

πŸ—ƒοΈ Data model

  • demo_users β€” simplified auth: one demo operator owns the forms.
  • forms β€” title, description, unique slug, status (draft | published), accent, and an ordered fields array stored as JSONB.
  • form_views β€” one row per public view, with an anonymous session_id.
  • form_submissions β€” answers as JSONB keyed by field id, plus duration_ms (paired to a view via session_id for completion-time analytics).

The field model (packages/db/src/fields.ts) is the single source of truth β€” the same Zod schemas validate tRPC inputs, type the JSONB column, and drive the editor/renderer.


πŸ“– API & docs

Every operation is a tRPC procedure under form.*, submission.*, view.*, analytics.*. Browse them at /docs (Scalar). The spec at /api/openapi.json documents how each procedure is reached over tRPC's HTTP transport:

  • Queries β†’ GET /api/trpc/<procedure>?input=<url-encoded JSON>
  • Mutations β†’ POST /api/trpc/<procedure> with the input as the JSON body

No duplicate REST API is generated β€” the doc is built by reading the real router + Zod inputs.


βœ… Status & honest notes

What's been verified in this repo:

  • pnpm typecheck β€” βœ… passes (all 5 packages)
  • pnpm lint β€” βœ… passes
  • pnpm build β€” βœ… next build succeeds
  • pnpm db:generate β€” βœ… initial migration committed in packages/db/migrations
  • Runtime smoke test (no DB needed): /, /docs, and /api/openapi.json all return 200, and the OpenAPI generator emits all 13 procedures.

Notes:

  • Live PostgreSQL is exercised via the steps above on your machine (Docker). The repo ships the migration + seed so a clean machine reaches a working app with migrate β†’ seed β†’ dev.
  • Auth is simplified per the brief: a single demo operator owns the forms, and the builder + analytics are gated by a shared admin passcode (ADMIN_TOKEN, sent as the x-admin-token header; enter it once on the unlock screen). Public form-filling at /forms/[slug] stays anonymous. Swap in real per-user auth later if needed.
  • The Scalar viewer at /docs loads its bundle from a CDN at runtime; the OpenAPI JSON itself is served by the app.

πŸš€ Live demo

Hosted on Vercel (Next.js) + Supabase (PostgreSQL via the IPv4 transaction pooler). The builder & analytics are behind an admin passcode (ADMIN_TOKEN); public form-filling is open.

Built for the ChaiCode #ChaiForms challenge β€” a form builder on the restricted stack, built end-to-end (including the demo video) with Claude Opus 4.8.

🚒 Deploy

apps/web is a standard Next.js app and deploys cleanly to any Node host (e.g. Vercel) with a managed PostgreSQL DATABASE_URL, NEXT_PUBLIC_APP_URL, and ADMIN_TOKEN set. For a pnpm monorepo, set the Vercel Root Directory to apps/web and deploy from Git (Vercel includes the workspace automatically).

License

MIT β€” add a LICENSE file if you publish it.

About

Retro OS full-stack form builder with Next.js, tRPC, Drizzle, PostgreSQL, public forms, QR sharing, analytics, and Scalar docs.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /