A lightweight proof-of-concept for a public-facing club registration form. Users select a membership type, enter their personal details, review their submission, and register. All is done in a simple 3-step flow. Form configuration is served by a Go API; submissions are persisted in a SQLite database.
git clone https://github.com/Behfar90/Form-POC.git
cd Form-POC
makeavailability:makecomes pre-installed on Mac. On Linux, install it withsudo apt install make(Debian/Ubuntu) orsudo dnf install make(Fedora/RHEL) if missing. On Windows, use WSL (recommended) or installmakevia Chocolatey (choco install make) or Scoop (scoop install make).
Prerequisites: Docker Desktop — no other runtimes needed.
First-time setup:
- Start Docker Desktop
- Build the images:
make docker-build
- Start the containers:
make docker-up
- Open http://localhost:3000
Inspect the database:
make docker-dbPrints all rows in the registrations table. The app doesn't need to be stopped first.
All Docker commands:
| Command | Description |
|---|---|
make docker-build |
Build images (re-run after code changes) |
make docker-up |
Start containers in the background |
make docker-down |
Stop containers |
make docker-logs |
Tail container logs |
make docker-restart |
Restart containers |
make docker-clean |
Stop containers and wipe the database volume |
make docker-db |
Print all rows in the registrations table |
Reset and rebuild from scratch:
make docker-clean
make docker-build
make docker-upHow it works
Browser → http://localhost:3000
│
▼
nginx (app container)
├── / → serves the React app
├── /form-details → proxied to Go API (api container)
└── /registrations → proxied to Go API (api container)
│
▼
SQLite database
(persisted in a Docker volume)
The api container is not exposed directly — all traffic goes through nginx. The SQLite database is stored in a named Docker volume so data survives container restarts.
Prerequisites: Go 1.21+ and Node.js 18+
-
Install frontend dependencies:
cd app && npm install
-
Start the API in one terminal:
cd api && go run .
Runs on http://localhost:8080
-
Start the frontend in a second terminal:
cd app && npm start
Runs on http://localhost:5173
Same as Option 2, but from the project root:
# Install frontend dependencies once
make setup
# Start both API and frontend together (Ctrl+C stops both)
make start
# Or start them independently
make start-api # API on http://localhost:8080
make start-app # Frontend on http://localhost:5173| Layer | Technology | Reason |
|---|---|---|
| Frontend | React + TypeScript | Component model suits a multi-step form; TypeScript catches data shape mismatches early |
| Frontend | clsx | Simple utility for conditional class names, keeps JSX clean |
| Frontend | lucide-react | A lightweight icon library for React |
| Build tool | Vite | Fast dev server with minimal configuration |
| Backend | Go + Fiber | Low overhead, single binary, clean JSON handling |
| Database | SQLite | No server to manage; appropriate for a POC with low write concurrency |
| Proxy | nginx | Serves the static build and proxies API calls, keeping the API off the public port |
| Containerisation | Docker Compose | One command to run the full stack with no local runtimes required |
- Form configuration management — currently the form details are hardcoded in the API (with form description part missing there!); a more flexible approach would be to store form details in the database and serve them from there
- Authentication — the registrations endpoint is open; reads should require some form of auth
- Duplicate detection — reject submissions with the same email address for the same form
- Email confirmation — send a confirmation email on successful registration
- Phone number validation — add client-side validation for the phone number field using a third-party library like
libphonenumber-js - Improved user-input handling — provide more user-friendly hints/messages on Step 2 user inputs (instead of a bad pattern block).
- Automated tests — add unit and integration tests for both frontend and backend, especially for users form handling and critical paths like form submission and database interactions (which I unfortunately didn't have time to cover in this POC)
The AI assistant used in this project is Claude Code by Anthropic. It was first brought in planning phase to brainstrom the overall approach to implement the app, and to weigh up both FE and BE package options to use (in terms of the tradeoff between the value each library provides agaisnt the complexity and bundle weight it adds to the project).
On the frontend, Claude Code was selectively used to generate repetitive boilerplate code (e.g. to avoid writing form fields manually). At the end, I used Caude Code as a Chaos Engineering tool to show me out-of-my-sight bad patterns, non-obvious bugs, and ofc the gaps in responsive behavior.
On the backend, Claude Code generated the boilerplate for the API endpoints and database interactions, helped to produce the Dockerfile, docker-compose.yml, and Makefile commands and more importantly, served as a Go reference to refresh my memory on syntax and best practices, since I hadn't used it for years.
Finally, I used Claude Code to draft most of this README file content, including the instructions and the architecture diagram, primarily for the sake of speed. The content was then reviewed, edited, and extended by hand.