A secure, lightweight RESTful API for creating and managing events and registrations. This project demonstrates a practical structure using Gin for routing, PostgreSQL for persistence, bcrypt for password hashing, and JWTs for stateless authentication.
- User management
- Sign up with username and password (passwords are hashed before storage).
- Login with credentials and receive a signed JWT access token.
- Event management
- Create, update, delete events (authenticated users only). Events contain: id, name, description, location, datetime, owner (user_id).
- List all events and get details of a single event (public read access).
- Registration
- Authenticated users can register and unregister for events.
- Security: passwords must be hashed; tokens signed with a server secret; avoid exposing secrets in source.
- Performance: use connection pooling for the database; limit max open connections.
- Reliability: validate inputs, return appropriate HTTP status codes, and handle DB errors gracefully.
- Observability: included logging and basic health checks.
- Authentication method: JSON Web Tokens (JWT) using HMAC SHA256 (HS256).
- Token creation:
utils.GenerateTokenString(username, userId)generates a signed token with an expiry. - Token validation:
middleware.Authenticateexpects the JWT in theAuthorizationheader and callsutils.ValidateTokenStringAndGetUserID(server-side verification) to populate request context withuserID.
- Token creation:
- Authorization: route handlers check
userID(from the middleware) to ensure only owners can update or delete their events.
Security notes:
- The current JWT secret is hardcoded in
utils/jwt.goasNOT_SO_SECRET_CHANGE_ME— this must be replaced with an environment variable or a secret manager before production. - Tokens are currently short-lived in the code (expiration set when generated).
- Passwords: the project uses bcrypt (
golang.org/x/crypto/bcrypt) viautils.HashPasswordandutils.CheckPasswordHash. - JWT signing secret: used to sign and validate tokens. Move this secret to an env var (e.g.,
JWT_SECRET) and never commit to source control.
- Database: PostgreSQL via the
github.com/lib/pqdriver. - Connection:
db.InitDB()indb/db.goopens the connection using a connection string (currently hardcoded). Update to use environment variables likeDATABASE_URL. - Pooling: set
SetMaxOpenConns,SetMaxIdleConns,SetConnMaxIdleTimeandSetConnMaxLifetimeto reasonable values based on your DB capacity. - Schema (expected tables)
users(id bigint primary key, username text unique, password text)events(id bigint primary key, name text, description text, location text, datetime timestamp, user_id bigint)registrations(id bigint primary key, user_id bigint, event_id bigint)
Note: Add a migration tool (migrate) for db versioning.
- POST /signup — create a new user (body: username, password)
- POST /login — authenticate and receive
{ "token": "<jwt>" } - GET /events — list events (public)
- GET /events/:id — get an event (public)
- POST /events — create event (authenticated)
- PUT /events/:id — update event (authenticated, owner only)
- DELETE /events/:id — delete event (authenticated, owner only)
- POST /events/:id/register — register current user to event (authenticated)
- DELETE /events/:id/unregister — unregister current user (authenticated)
- The current middleware expects the token in the
Authorizationheader (Authorization: Bearer <token>). The login route returns the token as JSON.
- Run
docker-compose upto initialize postgres - Build and run:
go run .- API will be available at http://localhost:8080
- Replace hardcoded secrets (read JWT secret and DB connection from env).
- Add tests: unit tests for handlers/models and integration tests against a test DB.
- Add OpenAPI/Swagger documentation and protect the docs in production.