DIY Go API: a relational REST template with tenant and role models baked in
A Go RESTful API template
At a glance
- What is it?
- diygoapi is a Go REST API template built around PostgreSQL, Google OAuth 2.0 and a role based access control model, organised as a teaching example of package layout rather than a framework. It suits a developer who wants to read a working structure, and a production team will need to replace several decisions.
- Who is it for?
- diygoapi fits a Go developer who wants to study a complete, opinionated package layout for a relational REST service, since the value of the project is the worked structure with its auth, gateway, service and repository layers laid out in code rather than described.
- 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 90 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 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The stated goal is a template, and the project says so plainly
diygoapi is described as a RESTful API template built with Go, and the framing matters more here than in most repositories. The stated goal is to be an example of a relational database backed REST HTTP web server with the characteristics needed to succeed in a high volume environment. The author describes the project as co-opting the DIY ethos of the Go community, using the standard library wherever possible and only reaching for third party libraries where doing so would be unduly burdensome, with structured logging and OAuth 2.0 given as examples. That is a narrower ambition than a framework, and it is a deliberate one. The repository reads like a teaching artefact, and the fact that the author explains the reasoning behind the package layout, mentions struggling with the different patterns people use for package layouts, and invites issues about it confirms the intent.
The package layout is the product, split by responsibility
The top level of the repository is the argument the project is making about how a Go service should be arranged. There is a gateway directory that presumably handles inbound HTTP concerns, a service directory for business logic, a sqldb directory for the database layer, an errs directory for error handling, a logger directory built on zerolog, a uuid directory, a secure directory, a media directory, a server directory, and a cmd directory for the entry points. Alongside the directories sit root level files such as app.go, auth.go, context.go, db.go, org.go, user.go and movie.go, each with a matching test file, plus app_test.go, auth_test.go, context_test.go and user_test.go. The domain files are named after the entities in the model, and there is a deploy directory, a scripts directory and a config directory. This is the layout you would end up with after writing a service and then tidying, which is exactly why it is useful to read.
The data model is multi-tenant, and the project admits it is overkill
The key terms section defines the entities, and the definitions are the interesting part. A Person is a human being, a User is a persona that authenticates with a provider, and since a Person can authenticate with multiple providers, a Person can have one or many Users. Most operations are considered at the User level, and roles are assigned at the User level rather than the Person level, which the project says allows for more fine grained access control. An App always belongs to exactly one Org, and an Org can have multiple Persons, Users and Apps. The disclaimer is worth quoting in spirit because it frames everything: the data model is set up for a business to business multi-tenant software as a service, which is overkill for a simple CRUD app, but it can serve a single tenant just fine. The demo API is a movie database with basic create, read, update and delete operations.
Authentication is Google OAuth 2.0 with bearer tokens on every request
Every request to the demo webserver requires authentication, and the project has picked Google's OAuth 2.0 for it. That means setting up a client ID and client secret and obtaining an access token, with the Google OAuth 2.0 Playground recommended for getting fresh tokens during testing. Once a user has authenticated, the Google access token must be sent as a Bearer token in the Authorization header on every call. The failure behaviour is specified: with no token present the server responds with an HTTP 401 and an empty body, and if a token is present but invalid, the Google OAuth 2.0 v2 API is used to validate it and the server again responds with an HTTP 401 and an empty body. Authorization is a separate step from authentication. A valid token is not enough, because a user must first register with the system and be given a role, which the SelfRegister service handles by automatically assigning a default role of movieAdmin. Roles carry permissions, permissions carry resources, and the model is role based access control.
Configuration follows CLI flags, then environment, then file, then defaults
Configuration in this project goes through the ff library from Peter Bourgon, and the priority order is stated explicitly: CLI flags over environment variables over a config file over defaults. The config file defaults to ./config/config.json, which is the simplest path for local development because you create that file and it is picked up. The project also uses CUE for config file generation, with a Taskfile.yml at the root as the task runner, and the minimum requirements list Go, PostgreSQL, Google OAuth 2.0, Task and CUE. One operational detail is spelled out: you need a 256 bit ciphertext string, which can be parsed into a 32 byte encryption key, generated through a task in the repo. The repository also carries a .claude directory and a CLAUDE.md, and the configuration section notes that a /db-init-config command is available in Claude Code to collect database connection values, generate a fresh encryption key, write config/config.cue and run task gen-config, which is a convenience for anyone generating a scaffold quickly.
Cloning the template and running the tests
Getting the code is the first step of the walkthrough, and either the standard git command or the GitHub CLI works.
$ git clone https://github.com/gilcrest/diygoapi.git
Cloning into 'diygoapi'...The CLI alternative is shorter and the project points out that the GitHub CLI is itself written in Go.
$ gh repo clone gilcrest/diygoapi
Cloning into 'diygoapi'...The dependency set is small enough to read in one screen, and the go.mod file gives the Go version and the direct requirements. The direct dependencies include pgx for PostgreSQL, the google uuid package, the ff configuration library, zerolog for structured logging, the alice middleware chain library, the OAuth 2.0 package, the Google API client, go-cmp and quicktest for tests, and pkg/errors. The indirect list pulls in the usual cloud and OpenTelemetry dependencies that the Google client brings with it. The project ships test files next to the code they cover, and the media directory plus the examples in the walkthrough suggest the API returns structured output rather than bare rows.
Where this template stops being the right starting point
There are three things a production team would replace, and the project is honest enough that you can see all of them. The first is the data model, and the disclaimer says it plainly: multi-tenant by business to business design, described as overkill for a simple CRUD app, which is an admission that the schema costs you complexity in queries and joins whether you need it or not. The second is the identity provider, since authentication is bound to Google OAuth 2.0 and a single bearer token, and a real product usually needs more than one way in, plus refresh, plus revocation. The third is the authorization model, where the SelfRegister service hands every self-registering user the movieAdmin role, which grants access to all resources, and the project says as much with a shrug. That is a demo decision and it is dangerous to carry into production unmodified. The repository also publishes no GitHub releases, so there are no tagged versions to pin your deployment to, and the last push to the repository was on 2026-07-25.
Editorial conclusion
diygoapi fits a Go developer who wants to study a complete, opinionated package layout for a relational REST service, since the value of the project is the worked structure with its auth, gateway, service and repository layers laid out in code rather than described. It does not fit a team that wants a drop-in production framework, because the data model is deliberately heavier than a CRUD app needs, the authentication is tied to a single provider, and the project publishes no GitHub releases. Before you build on it, read the data model disclaimer and decide whether multi-tenancy is something you want, pick a database driver and configuration path from the standard library first, and confirm that the role assignment flow matches how your organisation actually grants access.
Frequently asked questions
What is diygoapi and who is it for?
It is a RESTful API template written in Go, backed by PostgreSQL, that exists as an example of a relational REST web server built for a high volume environment. The author presents it as a teaching example of package layout as much as a starting point for a project.
How do I authenticate requests to the diygoapi server?
You set up a Google OAuth 2.0 client ID and secret, obtain an access token, and send it as a Bearer token in the Authorization header on every request. A missing or invalid token produces an HTTP 401 response with an empty body.
How does the diygoapi authorization model work?
It is role based access control. Users must register with the system first, which the SelfRegister service handles by assigning the default role movieAdmin, and roles carry permissions which are assigned to service endpoints. The user's role determines whether the request is authorized for a given endpoint.
How is configuration handled in diygoapi?
All programs use the ff library, with a priority order of CLI flags over environment variables over a config file over defaults. The config file defaults to ./config/config.json, CUE is used for config file generation through a task, and you need a 256 bit ciphertext string that parses into a 32 byte encryption key.
Is the diygoapi data model only for multi-tenant applications?
The model is designed for a business to business multi-tenant software as a service, which the project calls overkill for a simple CRUD app, and it says it can serve a single tenant just fine. The demo API itself is a movie database with create, read, update and delete operations.
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/gilcrest-diygoapi)Was this useful?