Seat booking app built with Express, PostgreSQL, JWT auth, and plain HTML pages.
- Node.js + Express
- PostgreSQL (pg)
- JWT access token + refresh token cookie
- Frontend pages in HTML + Tailwind CDN
- Nodemailer for email verification
index.mjs: server entry, DB pool, seat booking routes, static page servingsrc/app.js: API router mounted at/apisrc/modules/auth/*: auth routes, controller, service, middleware, DTO validationfrontend/pages/*: login/register pagesinit.sql: creates tables and seeds seats
- Node.js 18+
- npm
- Docker Desktop (or Docker Engine + Compose)
npm installCreate a .env file in the project root:
# JWT
JWT_ACCESS_TOKEN_SECRET=replace_with_strong_secret
JWT_REFRESH_TOKEN_SECRET=replace_with_strong_secret
JWT_ACCESS_TOKEN_EXPIRES_IN=15m
JWT_REFRESH_TOKEN_EXPIRES_IN=7d
# Email (Mailtrap or SMTP provider)
SMTP_PORT=587
SMTP_USER=your_smtp_user
SMTP_PASS=your_smtp_pass
SMTP_FROM_NAME=Axis ChaiCode Cinema
SMTP_FROM_EMAIL=no-reply@example.com
# URL used in verification email links
CLIENT_URL=http://localhost:8989Notes:
- The PostgreSQL connection in
index.mjsis currently hardcoded to local Docker defaults:- host:
localhost - port:
5433 - user:
postgres - password:
postgres - database:
sql_class_2_db
- host:
DATABASE_URLis not currently used by the app.
npm run db:upnpm startServer runs at:
npm start: start server withnode index.mjsnpm run dev: start withnodemon index.mjsnpm run db:up: start postgres containernpm run db:down: stop container and remove volume
GET /: main page (index.html)GET /login.html: login pageGET /register.html: register pageGET /seats: protected, requiresAuthorization: Bearer <accessToken>PUT /:id/:name: book a seat
Base prefix: /api
GET /health
Auth base prefix: /api/auth
POST /registerPOST /loginPOST /refresh-tokenPOST /logout(protected)GET /verify-email/:tokenGET /health(protected)
- Register via
POST /api/auth/register. - Verify email using the link sent by email (
/api/auth/verify-email/:token). - Login via
POST /api/auth/login. - Response includes
accessTokenand setsrefreshTokencookie. - Use
Authorization: Bearer <accessToken>for protected routes. - Refresh access token with
POST /api/auth/refresh-token. - Logout with
POST /api/auth/logout.
POST http://localhost:8989/api/auth/login
Content-Type: application/json
{
"email": "you@example.com",
"password": "yourPassword"
}POST http://localhost:8989/api/auth/refresh-tokenRequires refreshToken cookie from login response.
POST http://localhost:8989/api/auth/logout
Authorization: Bearer <accessToken>init.sqlcreatesseatsanduserstables.init.sqlseeds 40 seats only if not already present.- SQL in
insert.sqlis for manual experimentation only.
Check seat count:
docker compose exec -T db psql -U postgres -d sql_class_2_db -c "select count(*) from seats;"Reset DB and re-run init script:
docker compose down -v
docker compose up -d dbView container logs:
docker compose logs --tail=120 db
docker compose logs --tail=120 app