zeal

package module
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 11, 2024 License: MIT Imports: 12 Imported by: 0

README

Zeal

A type-safe REST API framework for Go!

Structs can be used to define and validate URL parameters, request bodies and response types.

Automatically generates OpenAPI 3 schema documentation using REST and serves it using Swagger.

Usage

func main() {
    r := zeal.NewRouter("API")

    addRoutes(r)

    r.Api.StripPkgPaths = []string{"main", "models", "github.com/DandyCodes/zeal"}
    spec := r.CreateSpec("v1.0.0", "Spec")
    r.ServeSwaggerUI(spec, "GET /swagger-ui/")

    fmt.Println("Listening on port 3000...")
    fmt.Println("Visit http://localhost:3000/swagger-ui to see API definitions")
    http.ListenAndServe(":3000", r)
}

Routes handled by Zeal are automatically documented in the OpenAPI schema

This route has no response body, URL params or request body

zeal.Handle(r, "POST /", func(c zeal.Ctx[any], params any, body any) {
    fmt.Println("Hello, world!")
})

The route context receives the response type as a type parameter

This route responds with an int - zeal.Ctx[int]

zeal.Handle(r, "GET /answer", func(c zeal.Ctx[int], p any, b any) {
    c.JSON(42)
})

The JSON method will only accept data of the declared response type


Example data

var foodMenu = models.Menu{
    ID:    1,
    Items: []models.Item{{Name: "Steak", Price: 13.95}, {Name: "Potatoes", Price: 3.95}},
}

var drinksMenu = models.Menu{
    ID:    2,
    Items: []models.Item{{Name: "Juice", Price: 1.25}, {Name: "Soda", Price: 1.75}},
}

var menus = []models.Menu{foodMenu, drinksMenu}

This route responds with a slice of menus - zeal.Ctx[[]models.Menu]

zeal.Handle(r, "GET /menus", func(c zeal.Ctx[[]models.Menu], p any, b any) {
    c.JSON(menus, http.StatusOK)
})

The JSON method can be passed an optional HTTP status code (the route responds with 200 OK by default)


Params can be query or path params

Struct representing URL params can be defined in-line

zeal.Handle(r, "GET /menus/{ID}", func(c zeal.Ctx[models.Menu], p struct{ ID int }, b any) {
    for _, menu := range menus {
        if menu.ID == p.ID {
            c.JSON(menu)
            return
        }
    }
    c.Error(http.StatusNotFound)
})

Params struct fields must be capitalized (i.e. 'Quiet')

type PutItemParams struct {
    Quiet bool
}
zeal.Handle(r, "PUT /items",
    func(c zeal.Ctx[models.Item], p PutItemParams, item models.Item) {
        if item.Price < 0 {
            c.Error(http.StatusBadRequest, "Price cannot be negative")
            return
        }

        for i := range menus {
            for j := range menus[i].Items {
                if menus[i].Items[j].Name == item.Name {
                    if !p.Quiet {
                        fmt.Println("Updating item:", item)
                    }
                    menus[i].Items[j].Price = item.Price
                    updatedItem := menus[i].Items[j]
                    c.JSON(updatedItem)
                    return
                }
            }
        }

        if !p.Quiet {
            fmt.Println("Creating new item:", item)
        }
        menus[0].Items = append(menus[0].Items, item)
        updatedItem := menus[0].Items[len(menus[0].Items)-1]
        c.JSON(updatedItem, http.StatusCreated)
    })

Params and request bodies are converted to their declared type

If this fails, http.StatusUnprocessableEntity 422 is sent immediately

zeal.Handle(r, "POST /items", HandlePostItem)

func HandlePostItem(c zeal.Ctx[any], p struct{ MenuID int }, newItem models.Item) {
    if newItem.Price < 0 {
        c.Error(http.StatusBadRequest, "Price cannot be negative")
        return
    }

    for i := range menus {
        if menus[i].ID != p.MenuID {
            continue
        }

        menus[i].Items = append(menus[i].Items, newItem)
        c.Status(http.StatusCreated)
        return
    }

    c.Error(http.StatusNotFound)
}

The Error method takes an HTTP status code and an optional message

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Handle added in v0.5.0

func Handle[T_Response, T_Params, T_Body any](router *Router, pattern string, handler Handler[T_Response, T_Params, T_Body])

Types

type Ctx added in v0.4.0

type Ctx[T_Response any] struct {
	ResponseWriter http.ResponseWriter
	Request        *http.Request
}

func (Ctx[T_Response]) Error added in v0.5.0

func (c Ctx[T_Response]) Error(status int, errorMsg ...string)

func (*Ctx[T_Response]) JSON added in v0.5.0

func (c *Ctx[T_Response]) JSON(data T_Response, status ...int)

func (Ctx[T_Response]) Status added in v0.4.1

func (c Ctx[T_Response]) Status(status int)

type Handler added in v0.5.0

type Handler[T_Response, T_Params, T_Body any] func(Ctx[T_Response], T_Params, T_Body)

type Router

type Router struct {
	http.ServeMux
	Api *rest.API
}

func NewRouter

func NewRouter(name string) *Router

func (*Router) CreateSpec

func (router *Router) CreateSpec(version string, description string) *openapi3.T

func (*Router) ServeSwaggerUI

func (router *Router) ServeSwaggerUI(spec *openapi3.T, path string)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL