Online Booking Application

Cliff Hill's Coding Project

This is a full-stack application simulating an online booking system for conference rooms.


Design Decisions

  • Backend: Python 3.13, FastAPI, SQLAlchemy, asyncpg, modular structure, dataclass-based models, and robust testing with Nox and Pytest.

  • Frontend: React, TypeScript, Material-UI, centralized logging, and a clean separation of components and pages.

  • Database: PostgreSQL, with a normalized schema for users, rooms, bookings, and invitees.

  • Containerization: Docker Compose for both production and development, with hot-reload and volume mounts for rapid iteration.

  • Backend:

    • Uses Python 3.13 for modern syntax and improved debugging.
    • Follows a modular structure: routers, services, schemas, and models are separated for maintainability.
    • Dataclasses and type hints improve readability and reliability.
    • Async SQLAlchemy for scalable database access.
  • Frontend:

    • React with TypeScript for type safety and maintainability.
    • Material-UI for a modern, responsive UI.
    • Centralized logging and clear separation of concerns.
  • Security:

    • No authentication in this demo, but structure allows for easy integration of OpenID/JWT.
    • Environment variables for secrets; in production, use a secrets manager.
  • AI Use:

    • AI was used to bootstrap Dockerfiles, Compose files, tests, and some documentation.

Security concerns

There is no login or security in place with this project. However if set up with zero trust, an example scenario could use OpenID or similar login security, encrypted JWTs, secure connections could be established to the database as well as all traffic being moved to https rather than http, locking everything down to ensure that those who communicate with any part of this system are authenticated and have only the access that they are authorized. Each endpoint would need to be able to check that the user is authenticated and has a valid token that has not expired in order to be used.

There already is several pieces for helping secure the application on the backend, using the HyperModern Python cookiecutter and the tools it brings into play, however additional systems could be tied into github to help check things further (like New Relic).

Typically, the database username/password/etc would not be stored in the repo but in a secrets component and loaded separately. This was bypassed this time as that was outside of the scope of this project and was additional overhead to work with.

AI Use

I used AI to stub out a couple of files:

  • The backend/Dockerfile and frontend/Dockerfile - to speed up the process of getting docker loaded efficiently for the project.
  • The compose.yml file - getting the different images gathered together quickly.
  • The compose.dev.yml file - used to get a further understanding of how to hook up an extension to the previous file.
  • The mermaid diagrams used in this file.
  • I have the CodeGPT plugin in VSCode, and it has helped with docstrings, logging messages, and sometimes reducing the time it takes me to write out the code.
  • I used AI to rapidly set up tests for each users component (router, service), then adapted it to the others independently. I believe AI is great for getting reasonable tests written quickly, and then I simply refined it, and replicated the kinds of tests across the different components.
  • I was experimenting with some AI edits for debugging the tests.
  • My frontend skills are less polished than my backend skills. I've been using AI along the lines of how I would use StackOverflow, answering questions to help me get the code written.
  • I used AI to help clean up and better structure the CSS for the frontend project, as well as get some code documentation in place.
  • AI was used to refactor code a few times to make it cleaner and better organized.

Development Environment Setup

Follow these steps to get your local development environment running:

1. Clone the repository

You can use either HTTPS or SSH:

HTTPS:

git clone https://github.com/xlorepdarkhelm/numinar-coding-project.git
cd numinar-coding-project

SSH:

git clone git@github.com:xlorepdarkhelm/numinar-coding-project.git
cd numinar-coding-project

2. Copy environment variables

cp .env.example .env
# Or, if present:
cp .env.sample .env

3. Install backend dependencies

cd backend
poetry install
pre-commit install

5. Install frontend dependencies

cd ../frontend
yarn install

6. Start Docker Compose in development mode (in a separate terminal)

From the project root:

docker compose -f compose.dev.yml up

This will start the backend (port 8000), frontend (port 3000), and Postgres (port 5432) with hot-reload enabled for rapid development.

7. Run tests (backend & frontend)

Backend tests:

cd backend
nox           # Run all tests and checks (also builds API docs)
# or run only unit tests:
nox --session=tests

API Documentation:

  • The nox command above will also build the backend API documentation.
  • View the API docs in development mode: Open http://localhost:8000/docs in your browser (when running dev Docker Compose).
  • Generated static docs (after running nox): See backend/build/api-docs/ relative to the project root.

Frontend tests:

cd frontend
yarn test

Tip:

  • You can run backend and frontend tests locally (see below for details).
  • You can connect to the database using psql with the credentials in your .env file.

This project is a full-stack conference room booking system, designed for clarity, modularity, and ease of local development. It consists of: This will start the backend (port 8000), frontend (port 3000), and Postgres (port 5432) with hot-reload enabled for rapid development.

Development mode exposes:

Connect to the database with psql:

psql -h localhost -U <user> -d <db>
# Example (using defaults):
psql -h localhost -U user -d mydb

Password is in your .env file.


Production Environment Setup

To run the application in production mode (optimized containers, only frontend exposed):

From the project root:

docker compose up

Production mode exposes:


API Documentation (Backend)

To build the backend API documentation (OpenAPI/Swagger UI):

cd backend
nox

This will run all tests and also build the API docs.

View the API docs in development mode:

Generated static docs (after running nox):

  • See backend/build/api-docs/ relative to the project root.

Running for production

The standard docker compose file is used to set up the project in the production environment, and can be run from the following command:

docker compose up

In this mode, only one port is exposed in docker - the port 3000, which is the frontend. Postgres and the backend ports are hidden.


Local Development & Setup

  1. Clone the repository

  2. Copy environment variables file - Copy the provided example file:

    cp .env.example .env
    
    
  3. Start in development mode:

    docker compose -f compose.dev.yml up
    

    The port configuration is in the .env file. The defaults would have:

    1. Install backend dependencies (for local development outside Docker):

      cd backend
      poetry install
      
      • This will set up a virtual environment and install all required packages for the backend.
    2. Get pre-commit configured:

      pre-commit install
      
    3. Connecting to the Database During Development:

      When running with compose.dev.yml, the database is exposed on port 5432. You can connect using psql:

      psql -h <host> -U <user> -d <db>
      

      Using the defaults, that would be:

      psql -h localhost -U user -d mydb
      

      Enter the password when prompted (see your .env file).

  4. Production mode:

    docker compose up
    
    • Only frontend port (default 3000) is exposed.

Local running

There is an alternative compose file specifically designed for running this project locally which allows for real-time editing and updating of either the frontend or backend components. The command ro tun everything locally is:

docker compose -f compose.dev.yml up

This compose file extends the standard compose file, adding in the necessary pieces to make the application usable in a local dev environment. In this configuration, the frontend is accessable from port 3000 like normal, the backend is accessable from port 8000, and postgres DB from 5432. The way this is set up, that command can easily be run in a separate terminal window during development for rapid testing of the piece(s) being worked on. Logging is at Debug level, and SQLAlchemy is set to echo mode, so queries are also logged. Realtime changes are reflected in the application as the code is run, and commands can be run locally from your terminal (like alembic) without needing to shell into the docker image.

For development builds, running "pre-commit install" inside the backend folder will install the pre-commit components for ensuring the code is good and clean before it gets commited to the repo.

The .env.sample can be copied to .env as well in order to get basic environment variables configured. As this is a system that usually has no external means to reach the database (in the production environment) I have left the actual username/password/etc for the database intact in the files in the repo.

Testing

The project was implemented with testing in the forefront.


Backend

Tests can be run from the nox command in the root backend directory. This will validate the backend with a wide range of tools, to ensure that everything is correct.

  • Run all tests and checks:

    cd backend
    nox
    
  • List available sessions:

    nox --list-sessions
    
  • Run a specific session (e.g., unit tests):

    nox --session=tests
    
  • Install pre-commit hooks:

    pre-commit install
    

Code coverage was lowered to 90 percent because 100 percent was not required for this demo, and some pieces were proving too painful to get tested right and would have taken additional time to finish. As it wasn't mandated that everything is tested, I left those parts alone.

Frontend

Tests can be run from yarn run jest in the frontend root directory. This validates that the components and pages work as intended.

  • Install dependencies:
    cd frontend
    yarn install
    
  • Run tests:
    yarn test
    
Further improvements

Integration testing, and end-to-end tests really would make this robust. Having all of the tests run in CICD before allowing code to be merged/commited to the main branch would be a mechanism to help ensure code quality. I would have set up the github project to have "feature branches" be made, to add whatever feature that a work item/issue had, and then Peer Reviews - typically set up with 2 peers reviewing 1 PR and approving it, aswell as all CICD checks/tests needing to pass before allowing the branch to be merged.

I would have templates in place for creating a PR, with a set of instructions that would give the "definition of done" - a checklist that would need to be completed before the issue could be marked as completed and a PR could then be reviewed.

Diagrams

Docker Compose Components

graph TD
  subgraph Docker_Network
    B[Frontend]
    C[Backend]
    D[PostgreSQL]
    C -- "SQL: 5432" --> D
  end
  Client[Client] -- "HTTP: 3000" --> B
  Client -- "HTTP/REST: 8000" --> C

Database Schema Diagram

classDiagram
  class User {
    string email
    string name
  }
  class Room {
    int id
    string name
    string location
    string equipment
    int capacity
  }
  class Booking {
    int id
    int room_id
    datetime start_time
    datetime end_time
    string title
  }
  class Invitee {
    int id
    int booking_id
    string user_email
  }

  User "1" <|-- "*" Invitee : user_email
  Room "1" <|-- "*" Booking : room_id
  Booking "1" <|-- "*" Invitee : booking_id

Component Diagram

graph TD
    subgraph Frontend
        F1[Pages] --> F2[Components]
        F2 --> F3[API Calls]
    end
    subgraph Backend
        B1[FastAPI Routers] --> B2[Pydantic Schemas]
        B1 --> B3[Service Layer]
        B3 --> B4[SQLAlchemy Models]
        B4 --> B5[PostgreSQL]
    end
    F3 -->|REST| B1

Backend Request Data Path

sequenceDiagram
  participant Frontend
  participant FastAPI_Router as FastAPI Router
  participant API_Endpoint as API Endpoint
  participant Pydantic_Schema as Pydantic Schema
  participant Service_Layer as Service Layer
  participant DB_Model as DB Model
  participant Database

  Frontend->>FastAPI_Router: HTTP Request (REST)
  FastAPI_Router->>API_Endpoint: Route Match
  API_Endpoint->>Pydantic_Schema: Validate & Parse Input
  Pydantic_Schema-->>API_Endpoint: Validated Data
  API_Endpoint->>Service_Layer: Pass Data
  Service_Layer->>DB_Model: Business Logic
  DB_Model->>Database: DB Query/Update
  Database-->>DB_Model: Query Result
  DB_Model-->>Service_Layer: Data
  Service_Layer-->>API_Endpoint: Response Data
  API_Endpoint-->>Frontend: HTTP Response (JSON)

SSE Update Flow (Real-Time Room Availability)

sequenceDiagram
  participant Frontend
  participant FastAPI_Router as FastAPI Router
  participant SSE_Endpoint as SSE Endpoint
  participant Event_Generator as Event Generator
  participant Publisher as Publisher

  Frontend->>SSE_Endpoint: Open EventSource /availability/stream
  SSE_Endpoint->>Event_Generator: Start Async Event Loop
  loop While Connected
    Event_Generator->>Publisher: Wait for Event or Timeout
    Publisher-->>Event_Generator: Room Availability Event | Keep-Alive
    Event_Generator-->>Frontend: Send SSE Event (data: ...)
  end
  Frontend-->>SSE_Endpoint: Disconnect (close EventSource)
  SSE_Endpoint-->>Event_Generator: Cleanup Subscriber
Description
The code I used for a coding challenge.
Readme 50 MiB
Languages
Python 54.2%
TypeScript 39%
CSS 5.3%
JavaScript 1%
HTML 0.4%
Other 0.1%