gothinkster/golang-gin-realworld-example-app: a Go and Gin reference backend that follows the RealWorld API spec
Exemplary real world application built with Golang + Gin
At a glance
- What is it?
- A Go 1.21 codebase built on Gin, GORM v2 and golang-jwt/v5 that implements the RealWorld spec's users, articles, comments and tags endpoints. It is a teaching and starter repository, not a production service.
- Who is it for?
- Adopt it if you want a working Go and Gin API to read, test against, or fork before you know your own domain model. Do not adopt it as a production service: SQLite plus AutoMigrate, a single hello.go entrypoint and no migration tooling are starter choices, not operational ones.
- 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 54 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 September 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A Go and Gin reference backend that answers the RealWorld spec, not a product
The README describes this repository as a "Golang/Gin codebase containing real world examples (CRUD, auth, advanced patterns, etc) that adheres to the RealWorld spec and API". That sentence is the whole scope. RealWorld is a shared contract: every implementation in the family exposes the same routes, the same JSON envelopes and the same error shape, so a frontend written against one backend can be pointed at another. This repository is the Go and Gin member of that family, and its value is that a reader can compare a Go implementation against the spec directly.
The audience follows from that. If you are learning how Gin routes, middleware and binding fit together, the code is short enough to read end to end. If you are starting a Go service and want a working skeleton with authentication already wired, it saves a day. If you are building something with a team, a schema you already know and a deployment target, the RealWorld domain (users, profiles, articles, comments, tags, favorites, follows) is a constraint rather than a help, because you will delete most of it.
Package-per-domain layout: routers, models, serializers and validators side by side
The directory listing in the README shows the organising idea. Each domain gets a folder, and inside that folder the responsibilities are split by file suffix: models.go for data models and DB operations, serializers.go for response formatting, routers.go for business logic and router binding, middlewares.go for the before and after logic of a request, validators.go for form and JSON checking. common/ holds utils.go and database.go, and hello.go at the root is the entrypoint.
This is a deliberate break from the classic Gin tutorial layout, where handlers, models and middleware live in three global folders. Here, adding a feature means touching one directory. The trade-off is that shared concerns have no obvious home: the README's own Todo list includes "Code structure optimize (I think some place can use interface)", which is the author conceding that the boundaries are not final. A second cost is that a small domain folder carries five files, so a trivial endpoint still spreads across models, serializers and routers. The API surface itself is documented in the api/ directory alongside the code, which is the file to read when you want to know which route maps to which handler without running the server.
Installing it and hitting the API for the first time
Go 1.21 or higher is required, per the README, and the go.mod file confirms the module is github.com/gothinkster/golang-gin-realworld-example-app with a go 1.21 directive. Install Go from the official instructions, then build and test from the project root. The three commands below are the ones the README gives under Install Dependencies; the test run is what tells you the SQLite driver and cgo are working on your machine.
go build ./...
go test ./...
go mod tidyConfiguration is environment variables only, either exported in your shell or placed in a .env file that you source yourself. The .env.example file lists PORT, GIN_MODE and DB_PATH, with TEST_DB_PATH commented out as optional. The README notes the defaults: port 8080, Gin mode debug, and a SQLite file at ./data/gorm.db.
export PORT=3000
export DB_PATH=./data/myapp.db
go run hello.goAfter go run hello.go the server listens on the port you set, and the SQLite file appears under the path you gave. The README documents no seed command and no fixture file, so a fresh database starts empty and you create the first account through the registration route the RealWorld spec defines. If you want the coverage numbers the README quotes, the same file gives the two commands for a local report.
go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.outSQLite plus AutoMigrate is the starter's biggest operational compromise
The dependency table in the README lists gorm.io/driver/sqlite v1.5.7 and notes that it "requires cgo; use glebarez/sqlite for pure Go". That single line is the most consequential fact in the repository for anyone planning to deploy it. A cgo dependency complicates cross-compilation and static builds, and it is the reason a plain go build can fail on a machine without a C toolchain where a pure-Go driver would not.
The database choice matters more than the driver. SQLite is a file, so the default configuration is a single-writer store with no network layer, no connection pooling story and no separate process to restart. Nothing in the README describes a migration system, a schema versioning step or a rollback path; the repository ships gorm.db as a file at the root, which suggests the schema is created by the application at startup. For a teaching repository that is exactly right, because it removes every obstacle between cloning and a working API. For a service with more than one instance, two writers, or a schema change that has to be reversible, it is the wrong foundation. Note also that the README's own Todo list still carries "More elegance config" and "ProtoBuf support" as open items, so configuration handling is acknowledged as unfinished.
What the 2025 dependency refresh actually changed
The Recent Updates section documents a modernization pass rather than a feature release. The Go requirement moved from 1.15 to 1.21 or higher. The ORM moved from the deprecated jinzhu/gorm v1 to gorm.io/gorm v2. JWT handling moved from dgrijalva/jwt-go to golang-jwt/jwt/v5, which the README says "fixes CVE-2020-26160". Validator tags were updated to match gin v1.10.0.
The same section lists four spec-compliance corrections that are worth knowing because they change behaviour rather than internals. GET /profiles/:username now allows anonymous access, POST /users/login returns 401 instead of 403 on failure, GET /articles/feed is registered as a dedicated authenticated route, and the two DELETE endpoints return an empty response body. If you have a client written against an older fork of this codebase, the login status code change alone will break error handling that keys on 403. The README does not provide a changelog file or tagged releases; the Recent Updates prose is the only record of what moved, and it is undated, so you cannot tell from the repository which commit introduced which change.
gothinkster/node-express-realworld-example-app and the rest of the RealWorld family
The natural alternative is another RealWorld implementation, and the one people search for most often alongside this repository is gothinkster/node-express-realworld-example-app. The difference is not cosmetic: the Node version runs on Express with JavaScript, so its middleware chain, its error handling and its ORM choice all differ, while the routes and JSON payloads stay identical because both projects answer the same spec. That is the point of the family. You can read the same endpoint in two languages and compare how each ecosystem expresses authentication, validation and serialization.
Choosing between them comes down to what you already run. If your team writes Go and you want the RealWorld contract as a starting point, this repository is the closer fit. If you want a JavaScript backend or you are comparing frameworks rather than languages, the Node implementation is the more direct reference. Outside the RealWorld family, the honest alternative is a plain Gin scaffold with your own domain model, because the main thing this repository gives you beyond Gin itself is the RealWorld schema, and that is also the main thing you will throw away.
Test coverage, licence and the cost of keeping up
The README reports coverage figures twice, and the two sets disagree. An earlier table gives articles 93.4%, users 99.5%, common 85.7% and a total of 90.0%. A later section labelled "Current test coverage (2026)" gives articles 92.1%, users 99.5%, common 85.7% and a total of 89.2%. The users and common numbers match; the articles number and the total do not. Treat the exact figures as approximate and run the coverage commands yourself if the number matters to you. The suite is real either way, built on stretchr/testify v1.10.0, and it is the reason the dependency refresh could be done with confidence.
On maintenance: the last push to the default branch was on 2026-08-08, and the repository is not archived. The dependency table pins specific versions with release dates and notes on what later majors do, for example that gorm v1.30+ has breaking changes and that validator v10.30+ requires Go 1.24. That table is effectively the upgrade plan, and it is the part of the README that will age fastest. The licence is MIT, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are kept; that is a summary of the licence text, not legal advice, and the LICENSE file at the repository root is the document that governs. Because the project is a sample rather than a library, there is no versioned API to keep compatible with, so upgrading means re-reading the diff yourself.
Editorial conclusion
Adopt it if you want a working Go and Gin API to read, test against, or fork before you know your own domain model. Do not adopt it as a production service: SQLite plus AutoMigrate, a single hello.go entrypoint and no migration tooling are starter choices, not operational ones. Before you build on it, open hello.go, common/database.go and users/middlewares.go to see how routes, the DB handle and the JWT check are wired, then run go test ./... and confirm the suite passes on your machine.
Frequently asked questions
What is gothinkster/golang-gin-realworld-example-app?
It is a Go codebase built with Gin that implements the RealWorld spec and API, covering CRUD, authentication, routing and pagination. The README describes it as a demonstration of a fully fledged fullstack application rather than a library.
Which Go version does gothinkster/golang-gin-realworld-example-app require?
Go 1.21 or higher. The README's Recent Updates section states the requirement moved from Go 1.15, and go.mod carries a go 1.21 directive.
How do I run gothinkster/golang-gin-realworld-example-app locally?
Build and test with go build ./... and go test ./..., then start the server with go run hello.go. Port, Gin mode and SQLite path come from the PORT, GIN_MODE and DB_PATH environment variables, with defaults of 8080, debug and ./data/gorm.db.
Does gothinkster/golang-gin-realworld-example-app include database migrations?
The README documents no migration tool, schema versioning step or rollback command. A gorm.db file sits at the repository root, which points to the schema being created by the application rather than by a migration process.
Official sources
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.
[](https://hysenlabs.com/projects/gothinkster-golang-gin-realworld-example-app)