go-api-kit
Kickstarts your API by providing out-of-the-box implementations for must-have modules and components for a successful API.
Provides a solid foundation for SaaS, Client/Server and API products. It provides out-of-the-box mitigations for the 10 OWASP risks for APIs:
📦 Installation
go get https://github.com/stfsy/go-api-kit
🚀 Usage
main.go
package main
import (
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"github.com/stfsy/go-api-kit/server"
)
var s *server.Server
func main() {
startServerNonBlocking()
stopServerAfterSignal()
}
func stopServerAfterSignal() {
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
s.Stop()
fmt.Println("Graceful shutdown complete.")
}
func startServerNonBlocking() {
s = server.NewServer(&server.ServerConfig{
MuxCallback: func(*http.ServeMux) {
// add your endpoints and middlewares here
},
ListenCallback: func() {
// do sth just after listen was called on the server instance and
// just before the server starts serving requests
},
// port override is optional but can be used if you want to
// define the port manually. If empty the value of env.PORT is used.
PortOverride: "8080",
})
go func() {
err := s.Start()
if err != nil {
panic(fmt.Errorf("unable to start server %w", err))
}
}()
}
Configuration
This module will read the following environment variables.
Env Vars
API_KIT_ENV: default=production
API_KIT_MAX_BODY_SIZE: default=10485760 (bytes) = 10 MB
API_KIT_READ_TIMEOUT: default=10 (seconds)
API_KIT_WRITE_TIMEOUT: default=10 (seconds)
API_KIT_IDLE_TIMEOUT: default=620 (seconds)
Standard Env Vars
Middlewares
The module provides several ready-to use middlewares which are compatible with e.g. https://github.com/urfave/negroni.
Access Log Middleware
Logs each incoming request to give insights about usage and response times.
import (
"net/http"
"github.com/urfave/negroni"
"github.com/stfsy/go-api-kit/server/middlewares"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
})
n := negroni.New()
n.Use(middlewares.NewAccessLog())
n.UseHandler(mux)
http.ListenAndServe(":8080", n)
}
Source
Content Type Middleware
Validates the incoming content type, if the request method implies a state change (e.g. POST).
import (
"net/http"
"github.com/urfave/negroni"
"github.com/stfsy/go-api-kit/server/middlewares"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
})
n := negroni.New()
n.Use(middlewares.NewRequireContentTypeMiddleware("application/json"))
n.UseHandler(mux)
http.ListenAndServe(":8080", n)
}
Source
Max Body Length Middleware
Limits the maximum allowed size of the request body.
import (
"net/http"
"github.com/urfave/negroni"
"github.com/stfsy/go-api-kit/server/middlewares"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
})
n := negroni.New()
n.Use(middlewares.NewRequireMaxBodyLengthMiddleware())
n.UseHandler(mux)
http.ListenAndServe(":8080", n)
}
Source
Adds additional security headers to the response to prevent common attacks and protect users and their data.
import (
"net/http"
"github.com/urfave/negroni"
"github.com/stfsy/go-api-kit/server/middlewares"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
})
n := negroni.New()
n.Use(middlewares.NewRespondWithSecurityHeadersMiddleware())
n.UseHandler(mux)
http.ListenAndServe(":8080", n)
}
Source
Upstream Cache Control Middleware
Instructs proxy servers between the client and the API to not cache responses.
import (
"net/http"
"github.com/urfave/negroni"
"github.com/stfsy/go-api-kit/server/middlewares"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
})
n := negroni.New()
n.Use(middlewares.NewNoCacheHeadersMiddleware())
n.UseHandler(mux)
http.ListenAndServe(":8080", n)
}
Source
Functions
Response Sender Functions
These functions help you send plain text or JSON responses easily:
SendText
Sends a plain text response.
import "github.com/stfsy/go-api-kit/server/handlers"
handlers.SendText(w, "Hello, world!")
Source
SendJson
Sends a JSON response (sets Content-Type to application/json).
import "github.com/stfsy/go-api-kit/server/handlers"
handlers.SendJson(w, []byte(`{"message":"ok"}`))
Source
Response Error Sender Functions
These functions send standardized error responses with the correct HTTP status code and a JSON body. Each function takes an http.ResponseWriter and an optional details map for additional error info. The error response now uses the following structure:
{
"status": 400,
"title": "Bad Request",
"details": {
"zip_code": {
"validator": "required",
"message": "must not be undefined",
}
}
}
Example usage:
import "github.com/stfsy/go-api-kit/server/handlers"
// With details (field-level errors):
handlers.SendBadRequest(w, handlers.ErrorDetails{
"zip_code": {
"validator": "required",
"message": "must not be undefined",
}
})
handlers.SendUnauthorized(w, handlers.ErrorDetails{
"x-api-key": {
"message": "must not be null",
},
})
// Without details (generic error):
handlers.SendInternalServerError(w, nil)
Source
Available error response functions:
| Status Code |
Title |
Function |
| 400 |
Bad Request |
SendBadRequest |
| 401 |
Unauthorized |
SendUnauthorized |
| 403 |
Forbidden |
SendForbidden |
| 404 |
Not Found |
SendNotFound |
| 405 |
Method Not Allowed |
SendMethodNotAllowed |
| 406 |
Not Acceptable |
SendNotAcceptable |
| 408 |
Request Timeout |
SendRequestTimeout |
| 409 |
Conflict |
SendConflict |
| 410 |
Gone |
SendGone |
| 411 |
Length Required |
SendLengthRequired |
| 412 |
Precondition Failed |
SendPreconditionFailed |
| 413 |
Payload Too Large |
SendPayloadTooLarge |
| 414 |
URI Too Long |
SendURITooLong |
| 415 |
Unsupported Media Type |
SendUnsupportedMediaType |
| 416 |
Range Not Satisfiable |
SendRangeNotSatisfiable |
| 417 |
Expectation Failed |
SendExpectationFailed |
| 422 |
Unprocessable Entity |
SendUnprocessableEntity |
| 429 |
Too Many Requests |
SendTooManyRequests |
| 500 |
Internal Server Error |
SendInternalServerError |
| 501 |
Not Implemented |
SendNotImplemented |
| 502 |
Bad Gateway |
SendBadGateway |
| 503 |
Service Unavailable |
SendServiceUnavailable |
| 504 |
Gateway Timeout |
SendGatewayTimeout |
| 505 |
HTTP Version Not Supported |
SendHTTPVersionNotSupported |
ValidatingHandler (Generic Request Validation)
Wraps your handler to automatically decode and validate JSON request bodies for POST, PUT, and PATCH methods. For other methods, the handler receives nil as the payload.
To enable JSON payload validation, add https://github.com/go-playground/validator compatible tags to your struct.
Usage
import "github.com/stfsy/go-api-kit/server/handlers"
type MyPayload struct {
Name string `json:"name" validate:"required"`
}
handlers.ValidatingHandler[MyPayload](func(w http.ResponseWriter, r *http.Request, p *MyPayload) {
// Use validated payload
w.Write([]byte(p.Name))
})
Source
Validation Errors
Validation errors will be sent to the client automatically with status code 400. The response will have content type application/problem+json. Here's an example:
{
"status": 400,
"title": "Bad Request",
"details": {
"zip_code": {
"validator": "required",
"message": "must not be undefined",
}
}
}
🧪 Running Tests
To run tests, run the following command
./test.sh
📄 License
MIT