build things.

Every project here solves a real business problem. Each phase adds new tools to your stack. You never rebuild the same thing — you always move forward.

Three phases. Two projects each. By the end you'll have 6 deployed apps built with REST, GraphQL, and TypeScript.

Ground Rules

01No tutorials. Docs only (MDN, React docs, Express docs, Mongoose docs).
02Commit after every meaningful change. Write real commit messages.
03Write a README for every project. Explain the decisions you made and why.
04Deploy everything. Localhost is not a portfolio.
05Ugly first, functional first. Polish comes last.
06When stuck, Google the specific problem. Not a tutorial for the whole app.
Phase 1

REST + JavaScript

Build with what you know from FSO Parts 0–7. React, Express, MongoDB, REST APIs, JWT, testing. No new tools. The goal is to get comfortable making every decision yourself.

React Vite Node.js Express MongoDB Mongoose REST APIs JWT bcrypt React Router Redux Toolkit React Query useReducer useContext Custom Hooks Vitest SuperTest Playwright React Testing Library Material UI styled-components ESLint
01

Freelancer Invoice System

A freelance graphic designer is tired of making invoices in Google Docs. She has 15 recurring clients. She wants to create invoices, track which ones are paid, send reminders for overdue ones, and see how much money she's owed at a glance.

Build a full invoicing system. This is real software that real freelancers pay for (FreshBooks, Wave, Invoice Ninja). You'll deal with financial calculations, PDF generation, status workflows, and dashboard analytics.

What to Build

  • Client management: name, email, company, address, phone. Full CRUD.
  • Invoice builder: select a client, add line items (description, quantity, unit price), tax rate, discount, notes. Auto-calculate subtotal, tax, total.
  • Invoice statuses with a workflow: Draft → Sent → Viewed → Paid → Overdue. Status changes logged with timestamps.
  • Auto-incrementing invoice numbers (INV-001, INV-002), configurable prefix
  • Recurring invoices: mark as recurring (weekly/monthly), auto-generate new drafts
  • Dashboard: revenue this month/quarter/year, outstanding amount, overdue amount, invoices by status, revenue over time chart
  • PDF generation: generate a clean PDF of any invoice server-side (pdfkit or puppeteer)
  • Due date tracking: invoices auto-become "overdue" after the due date
  • Payment recording: mark paid with date and payment method

Phases

1Design schemas: User, Client, Invoice, LineItem, Payment. Where do line items live? How do you store status history?
2Backend: Client CRUD, Invoice CRUD with status transitions, line item calculations server-side (never trust the frontend with money), PDF endpoint, dashboard aggregation with MongoDB pipeline.
3Frontend: client list, invoice builder (dynamic add/remove line items), invoice detail, dashboard with charts, PDF download.
4Tests: SuperTest for invoice total calculations and edge cases, Playwright for full creation flow. Deploy.

Decisions You'll Make

How to handle money (floating point is dangerous — store cents as integers?). How to build the dynamic line item form (add/remove rows, recalculate live). How to implement the status workflow (what transitions are valid? can Paid go back to Draft?). How to generate PDFs server-side. How to build aggregation queries for revenue by month grouped by status.

React Express MongoDB Mongoose JWT React Router React Query useReducer Custom Hooks Vitest SuperTest Playwright
02

Inventory & POS System

A clothing shop with 200+ SKUs tracks inventory in a spreadsheet. They reorder stock manually when they notice items are low. They want real-time stock tracking, low-stock alerts, sales logging, and reports showing what's selling and what's dead stock.

Two main views: POS screen for cashiers and back-office dashboard for the owner. Harder than the invoice system — multi-role access, product variants with individual stock counts, atomic stock operations under concurrency, and a POS interface that needs to be fast enough for daily use.

What to Build

  • Roles: Owner/Admin (full access) and Cashier (POS only)
  • Products: name, SKU, category, cost price, selling price, stock count, reorder level, supplier, variants (size/color with individual stock)
  • POS interface: search products, add to cart, adjust qty, apply discounts, calculate total, process sale, generate receipt view
  • Stock operations: auto-deduct on sale, manual adjustments with reason (damaged, returned, restocked), full audit trail
  • Purchase orders: create orders to suppliers, mark received to auto-update stock
  • Low stock alerts: configurable threshold per product, dashboard highlights items below level
  • Reports: revenue by period, top sellers, revenue by category, profit margins, sales by cashier
  • Audit trail: every stock change (sale, adjustment, restock) logged with timestamp and user

Phases

1Schema design: Product (with variants), Sale (with line items), StockAdjustment, PurchaseOrder, User. Think hard about variants — "Blue T-Shirt Size M" needs its own stock count but shares a product page.
2Backend: Product CRUD, sale processing (deduct stock atomically — what if stock hits 0 mid-transaction?), stock adjustment logging, purchase order workflow, report aggregation endpoints.
3POS frontend: fast, keyboard-friendly. Search by name or SKU, cart management, checkout. Needs to be snappy.
4Admin frontend: product management, inventory overview, alerts, purchase orders, reports with charts. Deploy.

Decisions You'll Make

How to model variants (separate docs? embedded array? Variant collection referencing Product?). How to prevent stock going negative during concurrent sales (transactions? optimistic concurrency?). How to structure POS cart state (useReducer: ADD_ITEM, REMOVE_ITEM, SET_QTY, APPLY_DISCOUNT, CLEAR). How to build fast product search for cashiers. How to calculate profit margins using aggregation.

React Express MongoDB Mongoose JWT React Router React Query useReducer useContext Redux Toolkit Custom Hooks Vitest SuperTest
Phase 2

GraphQL

Complete FSO Part 8 (GraphQL). Then build these two projects using Apollo Server + Apollo Client instead of REST. You'll feel the difference immediately — nested data that required 5 REST calls now comes in one query.

Apollo Server Apollo Client GraphQL React Node.js MongoDB Mongoose JWT React Router React Query
03

Appointment Booking Platform

A dental clinic with 3 dentists manages appointments through phone calls and a paper logbook. They want patients to book online, staff to manage the schedule, and no two patients to book the same dentist at the same time.

Two interfaces: public booking page and admin dashboard. GraphQL shines here — the booking page needs provider info, available slots, and service details in a single query instead of three REST calls. The core algorithmic challenge is time slot computation and double-booking prevention.

What to Build

  • Two roles: Staff/Admin (manages everything) and Patient (books appointments)
  • Service management: name, duration, price, which providers can perform it
  • Provider schedules: working hours per day of week, vacation/blocked times
  • Booking flow: select service → select provider (or "any available") → see open slots → pick one → enter info → confirm
  • Slot calculation: given service duration + provider schedule, compute open windows excluding existing bookings and blocks
  • Race condition handling: two simultaneous bookings for the same slot — only one succeeds
  • Admin calendar: daily/weekly view of all providers' appointments, color-coded by status
  • Statuses: Scheduled, Confirmed, In Progress, Completed, Cancelled, No-Show
  • Cancellation rules: patients can cancel 24+ hrs before, staff anytime
  • Simulated notifications: log reminder and confirmation "emails" with timestamps

Decisions You'll Make

How to design your GraphQL schema (types, queries, mutations) vs. how you would have done it with REST. How to handle slot calculation — is it a custom query that accepts a date and provider and returns available slots? How to prevent double-booking with atomic mutations. How to structure resolvers for nested data (a booking has a provider, a service, and a patient). How to handle auth in GraphQL context vs. Express middleware.

GraphQL Apollo Server Apollo Client React Node.js MongoDB Mongoose JWT React Router Custom Hooks Vitest
04

Multi-Tenant Client Portal

A digital marketing agency manages 20 clients. Each client needs to see their project status, deliverables, and communicate with the team. Currently everything is scattered across email, Drive, and Slack. The agency wants one portal where each client logs in and sees only their stuff.

Multi-tenancy is the architectural challenge. Client A must never see Client B's data. GraphQL makes data isolation more interesting — you enforce tenant scoping in every resolver, not just every route. Harder than the booking platform because of three distinct roles, organizational hierarchy, and the deliverable review workflow.

What to Build

  • Three roles: Agency Admin (sees everything), Agency Staff (assigned to specific clients), Client User (sees only their org's data)
  • Organization setup: agency creates client orgs, invites client users
  • Projects per client: phases/milestones, status, deadlines, assigned staff
  • Deliverables: link URLs per project. Status workflow: In Progress → Submitted → Approved / Revision Requested. Revision history with notes.
  • Threaded messaging per project and per deliverable. No cross-client visibility.
  • Client dashboard: active projects, deliverables awaiting review, recent messages, deadlines
  • Agency dashboard: all clients overview, projects by status, overdue items, staff workload
  • Activity log: timestamped feed per project
  • Data isolation: every resolver scoped to user's organization. Non-negotiable.

Decisions You'll Make

How to enforce tenant isolation in GraphQL resolvers (middleware? wrapper function? check in every resolver?). How to design the schema so a single query can fetch a project with its deliverables, messages, and assigned staff. How to model the deliverable review workflow as mutations (submitForReview, approve, requestRevision). Whether to use GraphQL subscriptions for real-time message updates or polling.

GraphQL Apollo Server Apollo Client React Node.js MongoDB Mongoose JWT React Router useContext Custom Hooks Vitest Playwright Material UI
Phase 3

TypeScript

Complete FSO Part 9 (TypeScript). Then build these two projects in TypeScript from the start — backend and frontend. You pick REST or GraphQL for each. These are the hardest projects and the ones you talk about in interviews.

TypeScript React Node.js Express or Apollo MongoDB Mongoose REST or GraphQL JWT React Router React Query Redux Toolkit
05

Applicant Tracking System

An HR department receives 500+ applications per month across 15 open positions. They're drowning in spreadsheets and email. They need a system where recruiters post jobs, applicants apply through a public form, and the hiring team moves candidates through stages with notes and ratings.

A real SaaS product category. Greenhouse, Lever, and Workable make millions from this. Public career page plus internal hiring dashboard. TypeScript catches bugs in your configurable pipeline logic, complex evaluation models, and role-based permissions before they reach production.

What to Build

  • Roles: Admin, Hiring Manager (their postings), Recruiter (reviews candidates)
  • Job postings: title, department, location, type, salary range, description, requirements, status (Draft/Open/Closed)
  • Public careers page: open positions, filter by department/location, apply button
  • Application form: name, email, resume URL, cover letter, custom questions per job
  • Configurable hiring pipeline per job: Applied → Phone Screen → Technical → Interview → Offer → Hired/Rejected. Drag candidates between stages.
  • Candidate profile: application info, interviewer notes, 1-5 rating per stage, full status timeline
  • Collaborative evaluation: multiple team members rate and comment on the same candidate
  • Email templates: rejection, interview invite, offer. "Send" logs the event.
  • Dashboard: open positions, candidates per stage (funnel chart), time-to-hire, source tracking
  • Search across all applicants by name, skills, or notes

Decisions You'll Make

How to type the configurable pipeline (each job can have different stages — how do you define a type for a pipeline that varies per job?). How to type evaluations (different evaluators, stages, all linked to one candidate). How to use discriminated unions for application states. Whether to use Zod for runtime validation alongside TypeScript's compile-time checks. How to type your routes or resolvers end-to-end so a backend change breaks the frontend at compile time, not production.

TypeScript React Express or Apollo MongoDB Mongoose JWT React Router React Query Redux Toolkit Custom Hooks Vitest SuperTest Playwright Material UI
06

Property Management System

A landlord owns 12 residential properties across Metro Manila with a total of 40+ units. She tracks rent collection in a notebook, sends payment reminders via text, and has no idea which units are profitable after maintenance costs. She needs a system that manages tenants, leases, rent collection, maintenance requests, and financial reporting across all her properties.

The hardest project. Multiple properties, multiple units per property, tenants with lease terms, recurring rent generation, payment tracking, maintenance workflows, and financial analytics per property and per unit. TypeScript is essential — the relationships between properties, units, leases, tenants, payments, and maintenance requests are deep enough that untyped code will break constantly. This is your portfolio centerpiece.

What to Build

  • Roles: Landlord/Admin (full access), Property Manager (assigned properties only), Tenant (their unit only)
  • Properties: name, address, type (apartment/condo/house), number of units, photos, documents
  • Units within properties: unit number, floor, bedrooms, bathrooms, area (sqm), monthly rate, status (Vacant/Occupied/Under Maintenance)
  • Tenant management: personal info, emergency contact, move-in date, lease terms, documents
  • Lease management: unit, tenant, start date, end date, monthly rate, security deposit, terms. Status: Active, Expiring Soon, Expired, Terminated. Auto-flag leases expiring within 30 days.
  • Rent generation: auto-generate monthly rent invoices for all active leases. Track status: Pending, Paid, Partial, Overdue. Support partial payments.
  • Payment recording: amount, date, method (cash/bank transfer/GCash), reference number, receipt generation
  • Maintenance requests: tenant submits request (description, category, urgency, photos). Landlord assigns priority and status: Open → In Progress → Completed. Cost tracking per request.
  • Tenant portal: see lease details, payment history, submit maintenance requests, view announcements
  • Dashboard: total revenue vs. expenses per property, occupancy rate, overdue payments, upcoming lease expirations, pending maintenance, net income per unit
  • Financial reports: income statement per property, per unit. Filter by date range. Export-ready views.

Phases

1Data model with TypeScript interfaces: User (with roles), Property, Unit, Tenant, Lease, RentInvoice, Payment, MaintenanceRequest. Define types first. The relationship chain is deep: Property → Unit → Lease → Tenant, and Property → Unit → MaintenanceRequest. Think about how rent invoices are generated and linked.
2Backend: Property/Unit CRUD, tenant onboarding, lease creation with validation (can't lease an occupied unit), rent invoice auto-generation, payment processing with partial payment support, maintenance request workflow, financial aggregation endpoints. Fully typed.
3Landlord frontend: property overview, unit grid per property, lease management, rent collection tracker, maintenance queue, financial dashboard with charts.
4Tenant portal: lease info, payment history, submit/track maintenance requests.
5Tests: rent calculation edge cases (partial payments, mid-month move-ins), lease validation (overlapping leases on same unit), financial report accuracy. Deploy.

Decisions You'll Make

How to model the Property → Unit → Lease → Tenant chain (all separate collections with references? how deep do you populate?). How to handle rent generation (cron-like job on server start? generate on dashboard load? pre-generate for the month?). How to prevent leasing an already-occupied unit (validation + atomic check). How to calculate net income per unit (rent payments minus maintenance costs — aggregation pipeline). How to handle partial payments (a tenant pays 8,000 of 12,000 — how is that modeled?). How to type the financial calculations so rounding errors are impossible. How to share types between the landlord frontend and tenant portal.

TypeScript React Express or Apollo MongoDB Mongoose JWT bcrypt React Router React Query Redux Toolkit useReducer useContext Custom Hooks Vitest SuperTest Playwright React Testing Library Material UI ESLint