README
¶
go-API-template
A RESTful API template (built with Go) - work in progress...
-
The goal of this app is to make an example/template of relational database-backed APIs that have characteristics needed to ensure success in a high volume environment.
-
This is a work in progress - you'll notice most things below are not checked. Any feedback and/or support are welcome. I have thick skin, so please feel free to tell me how bad something is and I'll make it better.
Critical components of any API (in no particular order)
- Unit Testing (with reasonably high coverage %)
- Verbose Code Documentation
- Instrumentation
- configurable http request/response logging
- Helpful debug logging
- API Metrics
- Performance Monitoring
- "Vendored" dependencies (done via go modules)
- Intentionally Minimal Dependencies
- gorilla for routing, pq for postgres, zerolog for logging, xid for unique id generation
- Intentionally Minimal Dependencies
- Fault tolerant - Proper Error Raising/Handling
- RESTful service versioning
- Security/Authentication/Authorization using HTTPS/OAuth2, etc.
- Containerized
- Generated Client examples
- Extensive API Documentation for Clients of the API (see twilio, Uber, Stripe and mailchimp as good examples - potentially use Docusaurus
Configurable HTTP Request Logging
Go-API-Template uses httplog to allow for "configurable request/response logging". With httplog you can enable request and response logging, get a unique ID for each request as well as set certain request elements into the context for retrieval later in the response body.
Note the audit section of the response body below, this is provided by httplog. Check out the repo for more details in the README.
{
"username": "gopher",
"mobile_id": "617-445-2778",
"email": "repoman@alwaysintense.com",
"first_name": "Otto",
"last_name": "Maddox",
"update_user_id": "gilcrest",
"created": 1539138260,
"audit": {
"id": "beum5l708qml02e3hvag",
"url": {
"host": "127.0.0.1",
"port": "8080",
"path": "/api/v1/adapter/user",
"query": "qskey1=fake&qskey2=test"
}
}
}
HTTP JSON Error Responses
For error responses, the api sends a simple structured JSON message in the response body, similar to Stripe, Uber and many others, e.g.:
{
"error": {
"kind": "input validation error",
"message": "Username is a required field"
}
}
This is achieved by sending a custom HTTPErr error type as a parameter to the httpError function, which then writes then replies to the request using the http.Error function. The structure of HTTPErr is based on Matt Silverlock's blog post here, but I ended up not using the wrapper/handler technique instead opting for a different approach. You'll also note the use of constants from a custom lib/errors package. This is pretty much lifted straight from the upspin.io project, with minor tweaks to suit go-API-template purposes.
The package makes error handling fun - you’ll always return a pretty good looking error and setting up errors is pretty easy.
When creating errors within your app, you should not have to setup every error as an HTTPErr— you can return "normal" errors lower down in the code and, depending on how you organize your code, you can catch the error and form the HTTPErr at the highest level so you’re not having to deal with populating a cumbersome struct all throughout your code. The below example is taken from the handler, which in this case is the highest level. In this case, any errors returned from the SetUsername method can be qualified as validation errors and the error caught from this method would be a "normal" Go error.
err = usr.SetUsername(rqst.Username)
if err != nil {
err = HTTPErr{
Code: http.StatusBadRequest,
Kind: errors.Validation,
Err: err,
}
httpError(w, err)
return
}
Helpful Resources I've used in this app (outside of the standard, yet amazing blog.golang.org and golang.org/doc/, etc.)
websites/youtube
Books
Blog/Medium Posts
- How I write Go HTTP services after seven years
- The http Handler Wrapper Technique in #golang, updated -- by Mat Ryer
- Writing middleware in #golang and how Go makes it so much fun. -- by Mat Ryer
- http.Handler and Error Handling in Go -- by Matt Silverlock
- How to correctly use context.Context in Go 1.7 -- by Jack Lindamood
- Standard Package Layout
- Practical Persistence in Go: Organising Database Access
- Writing a Go client for your RESTful API