Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/abemedia/go-don
API framework written in Golang.
https://github.com/abemedia/go-don
go golang http
Last synced: 5 days ago
JSON representation
API framework written in Golang.
- Host: GitHub
- URL: https://github.com/abemedia/go-don
- Owner: abemedia
- License: mit
- Created: 2022-02-28T21:29:55.000Z (over 2 years ago)
- Default Branch: master
- Last Pushed: 2024-09-27T05:53:48.000Z (about 2 months ago)
- Last Synced: 2024-10-26T21:32:36.350Z (18 days ago)
- Topics: go, golang, http
- Language: Go
- Homepage: https://pkg.go.dev/github.com/abemedia/go-don
- Size: 354 KB
- Stars: 52
- Watchers: 5
- Forks: 5
- Open Issues: 13
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Don - Go API Framework
[![GoDoc](https://pkg.go.dev/badge/github.com/abemedia/go-don)](https://pkg.go.dev/github.com/abemedia/go-don)
[![Codecov](https://codecov.io/gh/abemedia/go-don/branch/master/graph/badge.svg)](https://codecov.io/gh/abemedia/go-don)
[![Go Report Card](https://goreportcard.com/badge/github.com/abemedia/go-don)](https://goreportcard.com/report/github.com/abemedia/go-don)Don is a fast & simple API framework written in Go. It features a super-simple API and thanks to
[fasthttp](https://github.com/valyala/fasthttp) and a custom version of
[httprouter](https://github.com/abemedia/httprouter) it's blazing fast and has a low memory
footprint.As Don uses Go generics it requires Go 1.18 to work.
Minor version updates should be considered breaking changes.## Contents
- [Overview](#don---go-api-framework)
- [Basic Example](#basic-example)
- [Configuration](#configuration)
- [Support multiple formats](#support-multiple-formats)
- [Currently supported formats](#currently-supported-formats)
- [Adding custom encoding](#adding-custom-encoding)
- [Request parsing](#request-parsing)
- [Headers & response codes](#headers--response-codes)
- [Sub-routers](#sub-routers)
- [Middleware](#middleware)
- [Benchmarks](#benchmarks)## Basic Example
```go
package mainimport (
"context"
"errors"
"fmt"
"net/http""github.com/abemedia/go-don"
_ "github.com/abemedia/go-don/encoding/json" // Enable JSON parsing & rendering.
_ "github.com/abemedia/go-don/encoding/yaml" // Enable YAML parsing & rendering.
)type GreetRequest struct {
Name string `path:"name"` // Get name from the URL path.
Age int `header:"X-User-Age"` // Get age from HTTP header.
}type GreetResponse struct {
// Remember to add all the tags for the renderers you enable.
Greeting string `json:"data" yaml:"data"`
}func Greet(ctx context.Context, req GreetRequest) (*GreetResponse, error) {
if req.Name == "" {
return nil, don.Error(errors.New("missing name"), http.StatusBadRequest)
}res := &GreetResponse{
Greeting: fmt.Sprintf("Hello %s, you're %d years old.", req.Name, req.Age),
}return res, nil
}func Pong(context.Context, any) (string, error) {
return "pong", nil
}func main() {
r := don.New(nil)
r.Get("/ping", don.H(Pong)) // Handlers are wrapped with `don.H`.
r.Post("/greet/:name", don.H(Greet))
r.ListenAndServe(":8080")
}
```## Configuration
Don is configured by passing in the `Config` struct to `don.New`.
```go
r := don.New(&don.Config{
DefaultEncoding: "application/json",
DisableNoContent: false,
})
```### DefaultEncoding
Set this to the format you'd like to use if no `Content-Type` or `Accept` headers are in the
request.### DisableNoContent
If you return `nil` from your handler, Don will respond with an empty body and a `204 No Content`
status code. Set this to `true` to disable that behaviour.## Support multiple formats
Support multiple request & response formats without writing extra parsing or rendering code. The API
uses the `Content-Type` and `Accept` headers to determine what input and output encoding to use.You can mix multiple formats, for example if the `Content-Type` header is set to `application/json`,
however the `Accept` header is set to `application/x-yaml`, then the request will be parsed as JSON,
and the response will be YAML encoded.If no `Content-Type` or `Accept` header is passed the default will be used.
Formats need to be explicitly imported e.g.
```go
import _ "github.com/abemedia/go-don/encoding/yaml"
```### Currently supported formats
#### JSON
MIME: `application/json`
Parses JSON requests & encodes responses as JSON. Use the `json` tag in your request & response
structs.#### XML
MIME: `application/xml`, `text/xml`
Parses XML requests & encodes responses as XML. Use the `xml` tag in your request & response
structs.#### YAML
MIME: `application/yaml`, `text/yaml`, `application/x-yaml`, `text/x-yaml`, `text/vnd.yaml`
Parses YAML requests & encodes responses as YAML. Use the `yaml` tag in your request & response
structs.#### Form (input only)
MIME: `application/x-www-form-urlencoded`, `multipart/form-data`
Parses form data requests. Use the `form` tag in your request struct.
#### Text
MIME: `text/plain`
Parses non-struct requests and encodes non-struct responses e.g. `string`, `int`, `bool` etc.
```go
func MyHandler(ctx context.Context, req int64) (string, error) {
// ...
}
```If the request is a struct and the `Content-Type` header is set to `text/plain` it returns a
`415 Unsupported Media Type` error.#### MessagePack
MIME: `application/msgpack`, `application/x-msgpack`, `application/vnd.msgpack`
Parses MessagePack requests & encodes responses as MessagePack. Use the `msgpack` tag in your
request & response structs.#### TOML
MIME: `application/toml`
Parses TOML requests & encodes responses as TOML. Use the `toml` tag in your request & response
structs.#### Protocol Buffers
MIME: `application/protobuf`, `application/x-protobuf`
Parses protobuf requests & encodes responses as protobuf. Use pointer types generated with `protoc`
as your request & response structs.### Adding custom encoding
Adding your own is easy. See [encoding/json/json.go](./encoding/json/json.go).
## Request parsing
Automatically unmarshals values from headers, URL query, URL path & request body into your request
struct.```go
type MyRequest struct {
// Get from the URL path.
ID int64 `path:"id"`// Get from the URL query.
Filter string `query:"filter"`// Get from the JSON, YAML, XML or form body.
Content float64 `form:"bar" json:"bar" yaml:"bar" xml:"bar"`// Get from the HTTP header.
Lang string `header:"Accept-Language"`
}
```Please note that using a pointer as the request type negatively affects performance.
## Headers & response codes
Implement the `StatusCoder` and `Headerer` interfaces to customise headers and response codes.
```go
type MyResponse struct {
Foo string `json:"foo"`
}// Set a custom HTTP response code.
func (nr *MyResponse) StatusCode() int {
return 201
}// Add custom headers to the response.
func (nr *MyResponse) Header() http.Header {
header := http.Header{}
header.Set("foo", "bar")
return header
}
```## Sub-routers
You can create sub-routers using the `Group` function:
```go
r := don.New(nil)
sub := r.Group("/api")
sub.Get("/hello", don.H(Hello))
```## Middleware
Don uses the standard fasthttp middleware format of
`func(fasthttp.RequestHandler) fasthttp.RequestHandler`.For example:
```go
func loggingMiddleware(next fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
log.Println(string(ctx.RequestURI()))
next(ctx)
}
}
```It is registered on a router using `Use` e.g.
```go
r := don.New(nil)
r.Post("/", don.H(handler))
r.Use(loggingMiddleware)
```Middleware registered on a group only applies to routes in that group and child groups.
```go
r := don.New(nil)
r.Get("/login", don.H(loginHandler))
r.Use(loggingMiddleware) // applied to all routesapi := r.Group("/api")
api.Get("/hello", don.H(helloHandler))
api.Use(authMiddleware) // applied to routes `/api/hello` and `/api/v2/bye`v2 := api.Group("/v2")
v2.Get("/bye", don.H(byeHandler))
v2.Use(corsMiddleware) // only applied to `/api/v2/bye````
To pass values from the middleware to the handler extend the context e.g.
```go
func myMiddleware(next fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
ctx.SetUserValue(ContextUserKey, "my_user")
next(ctx)
}
}
```This can now be accessed in the handler:
```go
user := ctx.Value(ContextUserKey).(string)
```## Benchmarks
To give you a rough idea of Don's performance, here is a comparison with Gin.
### Request Parsing
Don has extremely fast & efficient binding of request data.
| Benchmark name | (1) | (2) | (3) | (4) |
| ------------------------ | ------: | ----------: | --------: | -----------: |
| BenchmarkDon_BindRequest | 2947474 | 390.3 ns/op | 72 B/op | 2 allocs/op |
| BenchmarkGin_BindRequest | 265609 | 4377 ns/op | 1193 B/op | 21 allocs/op |Source: [benchmarks/binding_test.go](./benchmarks/binding_test.go)
### Serving HTTP Requests
Keep in mind that the majority of time here is actually the HTTP roundtrip.
| Benchmark name | (1) | (2) | (3) | (4) |
| ----------------- | ----: | ----------: | --------: | -----------: |
| BenchmarkDon_HTTP | 45500 | 25384 ns/op | 32 B/op | 3 allocs/op |
| BenchmarkGin_HTTP | 22995 | 49865 ns/op | 2313 B/op | 21 allocs/op |Source: [benchmarks/http_test.go](./benchmarks/http_test.go)