The fastest way to deploy a RESTful API with Gin Framework — structured with PostgreSQL, JWT authentication stored in Redis, and ready to build on.
- sqlx: Lightweight SQL extensions for Go
- jwt-go: JSON Web Tokens (JWT) middleware
- go-redis: Redis client
- maroto: Pure Go PDF generation
- Built-in CORS, RequestID, and Auth middleware
- Built-in custom form validators with reusable error translation
- Invoice example — HTML preview and PDF download
- Swagger API documentation
- PostgreSQL with JSON/JSONB queries and trigger functions
- SSL support
- Go Modules
- Go 1.26.8+
- PostgreSQL
- Redis
Clone the repository:
git clone https://github.com/Massad/gin-boilerplate.git
cd gin-boilerplateInstall dependencies:
go mod downloadSet up your environment:
cp .env_rename_me .env
# Edit .env with your database credentials and signing keysGenerate independent access and refresh signing keys by running
openssl rand -hex 32 twice, then paste one result into ACCESS_SECRET and
the other into REFRESH_SECRET in your local .env. Keep that file private.
The application refuses to start with blank keys, keys shorter than 32 bytes,
or identical access and refresh keys. Redis authentication uses
REDIS_PASSWORD when configured on your Redis server.
Existing installations that copied the old sample signing keys must replace both keys. Changing them invalidates existing tokens, so users must log in again.
Import the database schema:
psql -U postgres -h localhost < ./db/database.sqlThe database includes trigger functions (created_at_column() and update_at_column()) that automatically manage created_at and updated_at timestamps on the user and article tables.
make runOr directly:
go run *.gogo build -v
./gin-boilerplateTests are integration tests that require running PostgreSQL and Redis:
set -a
. ./.env
set +a
make testUse a dedicated test database and Redis instance. The connection parser tests require PostgreSQL trust authentication because they exercise different password values. CI provisions disposable services for these checks.
To enable SSL, set SSL=TRUE in .env and generate certificates:
mkdir cert/
sh generate-certificate.shTo disable SSL, set SSL=FALSE in .env.
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /v1/user/register |
No | Register a new user |
| POST | /v1/user/login |
No | Login and receive JWT tokens |
| GET | /v1/user/logout |
Bearer | Logout (invalidates token) |
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /v1/article |
Bearer | Create an article |
| GET | /v1/articles |
Bearer | Get all user articles |
| GET | /v1/article/:id |
Bearer | Get one article |
| PUT | /v1/article/:id |
Bearer | Update an article |
| DELETE | /v1/article/:id |
Bearer | Delete an article |
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /v1/token/refresh |
No | Refresh access and refresh tokens |
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /v1/invoice |
No | HTML preview of a sample invoice with download button |
| GET | /v1/invoice/download |
No | Download sample invoice as PDF |
Generate and view API documentation:
make generate_docs
make runThen open: https://localhost:9000/swagger/index.html
Once the server is running, open the invoice preview in your browser:
http://localhost:9000/v1/invoice
The page shows an HTML invoice with a Download PDF button that generates a PDF using maroto (pure Go, no external dependencies).
By default, SetTrustedProxies(nil) is configured — Gin will not trust any proxy headers. If you deploy behind a reverse proxy (nginx, CloudFlare, etc.), set your trusted proxy IPs:
r.SetTrustedProxies([]string{"192.168.1.0/24", "10.0.0.0/8"})CORS is configured in middleware/cors.go. Update the Access-Control-Allow-Origin header for your domain in production.
This boilerplate uses Bearer Token authentication:
- Login returns an
access_token(15 min) andrefresh_token(7 days) - Include the access token in requests:
Authorization: Bearer <access_token> - When the access token expires, use
/v1/token/refreshwith the refresh token to get new tokens - Both tokens are stored in Redis. Logout invalidates the presented access token; refresh tokens remain valid until rotation or expiry.
Refresh rotation checks the stored Redis owner and consumes the refresh token
atomically. If you restrict Redis commands with an ACL, allow EVAL and
EVALSHA for this operation.
controllers/ HTTP handlers
models/ Database structs and queries (sqlx + raw SQL)
forms/ Request validation structs and reusable error translation
middleware/ CORS, RequestID, and JWT auth middleware
invoice/ Invoice HTML/PDF generation example
db/ PostgreSQL and Redis initialization
docs/ Swagger documentation (auto-generated)
public/ Static files and HTML templates
- Create a model in
models/with your SQL queries - Create form structs in
forms/with binding tags and aValidationMessagesmap - Create a controller in
controllers/— useforms.Translate(err, messages)for validation errors - Register routes in
main.go - Run
make generate_docsto update Swagger
- v2.0 — JWT authentication with Redis (current architecture)
- v1.x — Session/cookie authentication (v1 branch)
MIT
