Skip to content

stfsy/go-api-kit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

68 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

contributions - welcome GitHub License
Go Report Card Go


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

  • PORT: default=8080

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

Security Headers Middleware

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

SendStructAsJson

Sends a struct as a JSON response (sets Content-Type to application/json). The struct is marshaled to JSON automatically.

import "github.com/stfsy/go-api-kit/server/handlers"

type MyResponse struct {
	Status int    `json:"status"`
	Title  string `json:"title"`
}

handlers.SendStructAsJson(w, MyResponse{Status: 200, Title: "Success"})

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

About

API Starter Kit for Golang applications

Topics

Resources

License

Stars

Watchers

Forks

Contributors 4

  •  
  •  
  •  
  •