Skip to content
← All projects

2025

CoworkOS

CoworkOS is a coworking space management platform, used privately by the space that commissioned it. It replaced a manual, spreadsheet-based way of running the space with registration, rooms, monthly plans, subscriptions, bookings and admin workflows, on a React client and three Node.js services.

My role

Freelance. The owner came to me with the problem, we worked out the solution together, and I built the whole platform: the React client, the three Node.js services and the Docker Compose setup. I maintain it while the space uses it day to day.

Context

The space was run on Excel. Members, bookings and payments were tracked by hand, and there were no monthly subscriptions at all. The owner came to me, and we worked out together what the space actually needed. CoworkOS is private to that space and in real daily use. It is a monorepo with a React client and three services: auth for identity and security, user for profiles and admin user operations, and booking for rooms, bookings, plans and subscriptions.

Problem

With spreadsheets, the owner had no reliable way to know who was a member, what they had paid, or whether a room was free. There was no recurring revenue model either, because the space had no monthly subscriptions. The owner needed one platform to run the space: members registering and choosing a plan, rooms booked without clashes, and payments confirmed. That means enforcing each plan's limits, preventing overlapping bookings and expiring subscriptions on time, so the platform does it in code instead of someone checking a sheet.

What I built

Registration and security

Registration takes four steps: email, email verification, profile, and plan. Login uses cookie-based JWT with CSRF protection and optional two-factor authentication (TOTP). It also covers password reset and change, and role-based route protection for members, admins and super admins. Members now sign up and choose a plan themselves, where before the owner entered everything by hand.

Rooms, plans and bookings

Admins manage rooms with images and plans with per-room-type rules: daily, weekly and monthly quotas, maximum duration and allowed hours. A booking moves through pending, confirmed, no-show, cancelled and completed. Bookings need an active subscription, an active room, a plan that allows the room type, and no overlap with another booking, so double bookings are impossible by construction instead of caught by eye.

Subscriptions and admin workflows

This is what the space did not have before: monthly subscriptions. A subscription starts as pending payment. An admin confirms the payment to activate it, or rejects it. Admins also manage users, bookings, spaces and plans from one place. Scheduled jobs expire subscriptions, time out unpaid ones and clean up old cancelled bookings, work the owner used to do manually.

Services and events

Each service has its own PostgreSQL database through Prisma, and they stay in sync through RabbitMQ events. Choosing a plan creates a pending subscription, and an activated subscription activates the account. Redis caches data and deduplicates events.

Architecture

A React 19 and TypeScript client (Vite, Tailwind CSS, TanStack Query, React Router) talks over HTTP to three Express services: auth, user and booking. Services talk to each other through RabbitMQ events on one exchange. Each service has its own PostgreSQL database through Prisma, and Redis caches data and deduplicates events by ID. Requests are validated with Zod. Docker Compose orchestrates it all.

Key decisions and trade-offs

  • Build the backend as microservices that talk through events, instead of one monolith.

    Trade-off: The three parts have different jobs and different risk. Auth handles identity and security, so I wanted it isolated, with its own data, where a bug in booking logic cannot reach credentials or sessions. Booking is the busiest and most business-critical part, with plan rules, overlap checks and scheduled jobs, so it needs to change and scale on its own without touching login or profiles. Each service owns its data and links to the others only by user ID, so a change in one does not break the others, and a failure in one does not take the whole platform down. The cost is real: data syncs through events, so it is eventually consistent, every consumer has to deduplicate events, and there are more moving parts to run. For a space of this size a monolith would have been simpler to operate. I accepted that cost for the isolation and the room to grow, and Docker Compose keeps the operational overhead manageable.

  • Merge the old pass service into the booking service.

    Trade-off: Plans, quotas and bookings now live together, so a booking checks its plan in one place. Splitting them had put one business rule across two services. The cost was updating events and docs that still used the old service name.

  • Replace the spreadsheet workflow with enforced rules, and have an admin confirm each payment instead of automating payment.

    Trade-off: The space already took payments its own way, so I kept an admin in the loop to confirm or reject them and put the rules (plan limits, no overlaps, expiry) in code. That fit how the owner already worked and kept the first version small. The cost is that payment confirmation is still a manual step.

  • Cache with Redis and move slow work onto RabbitMQ queues to keep API responses fast under concurrent load.

    Trade-off: Cached reads and queued work keep responses fast under load. The cost is extra moving parts to run (Redis and RabbitMQ), cached data that can briefly lag behind the database, and the need to deduplicate events.

Outcome

CoworkOS is in real use by the coworking space that commissioned it, and I maintain it. The space moved from Excel to one platform where members register and subscribe themselves, rooms cannot be double booked, plan limits are enforced automatically and subscriptions expire on time. It gave the owner something the space did not have before: monthly subscriptions. It sustains 200+ concurrent sessions at sub-100ms API response times.

Stack

  • React 19
  • TypeScript
  • Vite
  • Tailwind CSS
  • React Query
  • Node.js
  • Express
  • PostgreSQL
  • Prisma
  • Redis
  • RabbitMQ
  • Docker