Framework
Massad/gin-boilerplate avatar
Massad/gin-boilerplate

gin-boilerplate: a Go REST API starter with auth, PDF and docs wired in

The fastest way to deploy a RESTful API with Gin Framework — PostgreSQL, JWT auth, Redis, PDF generation, and Swagger docs out of the box.

1,219 stars223 forksGoMIT

At a glance

What is it?
Massad's gin-boilerplate is a Gin based REST API template that arrives with sqlx, JWT authentication stored in Redis, a PostgreSQL schema with timestamp triggers, a maroto invoice example, Swagger documentation and a set of middlewares, and its third version rewrote the whole thing. The parts that will save you the most time are also the parts you will be tempted to replace, so it is worth knowing which is which.
Who is it for?
The boilerplate is worth starting from if you are writing a Go API with a relational database, a login and some documents to produce, because the wiring between Gin, sqlx, Redis and Swagger is the part you would otherwise spend a week on. It is the wrong choice if you want a minimal service, since the Redis dependency and the JWT scheme are baked in, and it is the wrong choice if you need multitenant data isolation, because the schema is a user and an article.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The inventory: what you get before writing a line

The pitch is the fastest way to deploy a RESTful API with the Gin framework, and the feature list is what a working service needs rather than what a tutorial needs. The data layer is sqlx, described as lightweight SQL extensions for Go, over PostgreSQL, and the database side includes JSON and JSONB queries plus trigger functions. Authentication is JWT with jwt-go middleware, and the tokens are stored in Redis through go-redis. The built-in pieces are the middlewares, CORS, RequestID and Auth, and a set of custom form validators with reusable error translation, which is the part that saves the most time once you have more than one endpoint. There is a Swagger API documentation setup, Go modules, optional SSL support, and an invoice example that renders an HTML preview and offers a PDF download generated by maroto, a pure Go PDF library with no external dependencies. The project is MIT licensed, and version 3 is a rewrite rather than an increment, with the release notes describing it as modernising the boilerplate with sqlx, middleware, reusable validation and the invoice PDF.

Setup starts with keys, and the app refuses to start without them

The prerequisites are Go 1.26.8 or newer, PostgreSQL and Redis, and the install is the usual three steps. You clone the repository and change into it, download the modules with go mod download, and copy the environment template into place:

bash
cp .env_rename_me .env

Then you edit .env with your database credentials and your signing keys, and the keys are the part with a procedure. You generate independent access and refresh signing keys by running openssl rand -hex 32 twice and paste one result into ACCESS_SECRET and the other into REFRESH_SECRET, and the readme is blunt about keeping that file private. The application refuses to start with blank keys, with keys shorter than 32 bytes, or with identical access and refresh keys, which is a better failure mode than a service that starts and then issues tokens nobody can distinguish. There is a migration warning too. Existing installations that copied the old sample signing keys must replace both of them, and changing them invalidates existing tokens, so users have to log in again. Redis authentication uses REDIS_PASSWORD when it is configured on the server.

The database: sqlx, JSON columns and trigger-managed timestamps

The schema comes as a plain SQL file you import into your database:

bash
psql -U postgres -h localhost < ./db/database.sql

The database includes trigger functions, created_at_column() and update_at_column(), that automatically manage the created_at and updated_at timestamps on the user and article tables. Doing this in the database rather than in Go is the right call, because it means a row updated by a script, a migration or another service still has correct timestamps. The models directory holds the database structs and the queries, using sqlx with raw SQL, so there is no ORM between you and the database and no query builder to fight with. The example resource is an article, which is the smallest thing with a list, a read, an update and a delete, and the endpoint table shows the shape: bearer authenticated GET and POST on the article routes, plus the user routes for register, login and logout, and the refresh route under token. The JSON and JSONB query support in the schema is there because the boilerplate assumes you will want to store a document alongside the relational rows, which is the common case for an API that grew out of a prototype.

Tokens in Redis, and what logout actually invalidates

The authentication design is worth reading carefully, because it is where a boilerplate makes decisions for you. Login returns an access_token with a 15 minute lifetime and a refresh_token with a 7 day lifetime, and you send the access token as Authorization: Bearer in each request. When the access token expires you exchange the refresh token at the token refresh route for new tokens. Both tokens are stored in Redis, and logout invalidates the presented access token, while refresh tokens remain valid until rotation or expiry. That is a deliberate asymmetry and it has consequences: a logged out session can still be revived with a refresh token that the user did not think of as revoked. Refresh rotation checks the stored Redis owner and consumes the refresh token atomically, which is what stops a stolen refresh token from being used twice. There is a practical consequence for anyone who restricts Redis with an ACL. Allow EVAL and EVALSHA for this operation, or rotation will fail. A team that wants a stricter logout has to change this design, and it is much easier to decide that before you ship than after.

The invoice example, and why maroto is in the stack

The invoice example exists to show a feature that is annoying to bolt on later, which is producing a document. There are two endpoints. One returns an HTML preview of a sample invoice with a download button, the other downloads the sample invoice as a PDF, and the page you open at the invoice route shows an HTML invoice with a Download PDF button that generates the PDF using maroto, which is pure Go with no external dependencies. The reason that matters is the deployment story. The usual alternative is a headless browser or a document service, and both bring a runtime into your stack. A pure Go PDF library means the binary you already ship can produce the document, which is why the example is part of the boilerplate rather than something in a separate repository. It is also a decent test of whether the rest of the stack holds together, since it exercises the router, the templates in the public directory and the response path in one request.

Adding a resource, and keeping the docs in step

The readme documents the workflow for a new resource in five steps, and it is short enough to memorise. Create a model in models with your SQL queries. Create form structs in forms with binding tags and a ValidationMessages map. Create a controller in controllers, and use forms.Translate with that map to turn validation errors into readable messages. Register the routes in main.go. Then run make generate_docs to update the Swagger documentation. Two details show the project has been used in anger. The forms.Translate helper exists because the same validation error appears in several endpoints and you should not write the message twice, and the ValidationMessages map is per form struct, so the messages live next to the fields they describe. The documentation step is not optional, because the Swagger output is generated from the code by the Swag tool, and the Makefile pins that tool to version 1.16.6, installs it into a cache directory inside the project, and only then runs swag init. The result is served at the swagger index path on the local port, and the docs directory in the tree holds the generated files.

Running it, testing it, and putting it behind a proxy

The run path is short. make run starts the application, and the Makefile simply runs the Go files in the root, or you can do it by hand with go run *.go. Building is go build -v followed by running the binary. The tests are integration tests that require running PostgreSQL and Redis, which is worth knowing before you run make test and wonder what happened, and the readme tells you to export your environment first:

bash
set -a
. ./.env
set +a
make test

You should point them at a dedicated test database and a dedicated Redis instance, and the connection parser tests specifically require PostgreSQL trust authentication because they exercise different password values, which is a detail that will fail on a hardened server. CI provisions disposable services for these checks. SSL is optional and off by default, enabled with SSL=TRUE in .env and a certificate generated by sh generate-certificate.sh into a cert directory, and set back to SSL=FALSE to turn it off. The last thing to configure before you deploy is the proxy story. By default SetTrustedProxies(nil) is set, so Gin trusts no proxy headers at all, which is the safe default; behind nginx or CloudFlare you set your own trusted proxy list, and CORS is configured in middleware/cors.go where the Access-Control-Allow-Origin header needs to point at your real domain.

Editorial conclusion

The boilerplate is worth starting from if you are writing a Go API with a relational database, a login and some documents to produce, because the wiring between Gin, sqlx, Redis and Swagger is the part you would otherwise spend a week on. It is the wrong choice if you want a minimal service, since the Redis dependency and the JWT scheme are baked in, and it is the wrong choice if you need multitenant data isolation, because the schema is a user and an article. Before you build on it, read the token storage design, since invalidating the access token on logout while leaving refresh tokens valid is a decision you inherit, and set the trusted proxies explicitly rather than relying on the default.

Frequently asked questions

What is the gin-boilerplate project?

It is a MIT licensed template for building a RESTful API with the Gin framework in Go. It ships with sqlx over PostgreSQL, JWT authentication with the tokens stored in Redis, CORS, RequestID and auth middleware, reusable form validation, a maroto invoice PDF example and generated Swagger documentation.

What do I need to run the gin-boilerplate?

Go 1.26.8 or newer, a PostgreSQL server and a Redis server. The tests are integration tests and need both running, ideally dedicated instances, and the connection parser tests require PostgreSQL trust authentication because they exercise different password values.

How does logout work with JWT tokens in this boilerplate?

Login returns an access token valid for 15 minutes and a refresh token valid for 7 days, and both are stored in Redis. Logout invalidates the presented access token, while refresh tokens remain valid until rotation or expiry. Refresh rotation checks the stored Redis owner and consumes the token atomically, which needs EVAL and EVALSHA allowed if you restrict Redis with an ACL.

How do I add a new resource to the gin-boilerplate?

Create a model with your SQL queries in models, create form structs with binding tags and a ValidationMessages map in forms, create a controller in controllers that uses forms.Translate for validation errors, register the routes in main.go, then run make generate_docs to update the Swagger documentation.

Official sources

  1. Issues
  2. License: MIT
  3. Massad/gin-boilerplate on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/massad-gin-boilerplate.svg)](https://hysenlabs.com/projects/massad-gin-boilerplate)