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.
- Unit Testing (with reasonably high coverage %)
- Verbose Code Documentation
- Instrumentation
- configurable http request/response logging
- Helpful debug logging
- API Metrics
- Performance Monitoring
- 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
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"
}
}
}
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",
"param": "Year",
"message": "The first film was in 1878, Year must be >= 1878"
}
}
This is achieved using a carve-out of the Upspin project and specifically the error handling functionality summarized so eloquently by Rob Pike in this article. My copy of the errors functionality has changes that suit my use cases. Namely, I have built a new function errors.RE
(for Response Error), which allows the engineer to easily build an HTTP response error.
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