ghost

package module
v0.0.0-...-7f06e90 Latest Latest
Warning

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

Go to latest
Published: Jun 14, 2022 License: BSD-3-Clause Imports: 8 Imported by: 0

README

Ghost - Build REST APIs from structs using Generics

Why Ghost?

We all love Go for it's high performance IO, Goroutines, channels etc but we also have to write boring REST APIs that required a lot of hand code writing. We had to repeat ourselves for each resource and when I read the "When To Use Generics" I thought, REST APIs are not a collection, but we are actually writing the exact same code multiple times. Is building REST APIs a generics thing? What's the minimal we could write to get a REST API?

What is Ghost?

Ghost is a collection of generic structs and interfaces that when composed, become a http.Handler that provides a REST API.

type User struct {
	Name string
}

type SearchQuery struct {
	Name string
}

func main() {
	store := ghost.NewMapStore(&User{}, SearchQuery{}, uint64(0))
	g := ghost.New(store)
	// g is a http.Handler and it provides GET /?name=:name, GET /:userid, POST /, PUT /:userid, DELETE /:userid
	http.ListenAndServe("127.0.0.1:8080", g)
}

Minimal work is to write your struct, choose a store from the stores and call ghost.New.
In reality you would write your own store. That's where the business logic is, and what translates into value.

What is generic in Ghost?

Ghost has 3 type parameters, R Resource, Q Query and P PKey.

Resource is the REST resource. Ghost provides CRUD operations for resources.

Query represents the query when searching for resources to return a list.

PKey is a key that identifies the resource. A primary key.

Types in Ghost

TODO

  • Validator
  • Hooks
  • Example using common ORMs
  • OpenAPIv3 integration

Documentation

Index

Constants

This section is empty.

Variables

Functions

func DefaultErrorHandler

func DefaultErrorHandler(encoding Encoding[Error]) func(err error) http.Handler

func New

func New[R Resource, Q Query, P PUintKey](store Store[R, Q, P]) http.Handler

New returns a http.Handler. New requires PKey to be an integer.

func NewS

func NewS[R Resource, Q Query, P PStrKey](store Store[R, Q, P]) http.Handler

NewS returns a http.Handler. NewS requires PKey to be a string.

func StrPath

func StrPath[P PStrKey](s string) (P, error)

func UintPath

func UintPath[P PUintKey](s string) (P, error)

Types

type AfterCreate

type AfterCreate interface {
	AfterCreate(context.Context) error
}

type AfterDelete

type AfterDelete[P PKey] interface {
	AfterDelete(context.Context, P) error
}

type AfterList

type AfterList[R Resource, Q Query] interface {
	AfterList(context.Context, *Q, []R) error
}

type AfterRead

type AfterRead[Q Query, P PKey] interface {
	AfterRead(context.Context, P, *Q) error
}

type AfterUpdate

type AfterUpdate[P PKey] interface {
	AfterUpdate(context.Context, P) error
}

type BeforeCreate

type BeforeCreate interface {
	BeforeCreate(context.Context) error
}

type BeforeDelete

type BeforeDelete[P PKey] interface {
	BeforeDelete(context.Context, P) error
}

type BeforeList

type BeforeList[Q Query] interface {
	BeforeList(context.Context, *Q) error
}

type BeforeRead

type BeforeRead[Q Query, P PKey] interface {
	BeforeRead(context.Context, P, *Q) error
}

type BeforeUpdate

type BeforeUpdate[P PKey] interface {
	BeforeUpdate(context.Context, P) error
}

type Encoding

type Encoding[R Resource] interface {
	Encode(http.ResponseWriter, R, int) error
	EncodeList(http.ResponseWriter, []R, int) error
	EncodeEmpty(http.ResponseWriter, int) error
	Decode(*http.Request) (R, error)
}

type Error

type Error struct {
	Code int   `json:"-"`
	Err  error `json:"error"`
}

func (Error) Error

func (e Error) Error() string

func (Error) MarshalJSON

func (e Error) MarshalJSON() ([]byte, error)

type Ghost

type Ghost[R Resource, Q Query, P PKey] struct {
	Server       Server
	Mux          func(Server) Handler
	ErrorHandler func(error) http.Handler
}

func (Ghost[R, Q, P]) ServeHTTP

func (g Ghost[R, Q, P]) ServeHTTP(w http.ResponseWriter, r *http.Request)

type Handler

type Handler = func(http.ResponseWriter, *http.Request) error

func DefaultMux

func DefaultMux[R Resource, Q Query](s Server) Handler

type Identifier

type Identifier[P PKey] interface {
	PKey(*http.Request) (P, error)
}

type JSON

type JSON[R Resource] struct{}

JSON is an Encoding.

func (JSON[R]) Decode

func (j JSON[R]) Decode(r *http.Request) (R, error)

func (JSON[R]) Encode

func (j JSON[R]) Encode(w http.ResponseWriter, r R, code int) error

func (JSON[R]) EncodeEmpty

func (j JSON[R]) EncodeEmpty(w http.ResponseWriter, code int) error

func (JSON[R]) EncodeList

func (j JSON[R]) EncodeList(w http.ResponseWriter, rs []R, code int) error

type PKey

type PKey interface {
	comparable
}

type PStrKey

type PStrKey interface {
	comparable
	string
}

type PUintKey

type PUintKey interface {
	comparable
	~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}

type PathIdentifier

type PathIdentifier[P PKey] func(string) (P, error)

PathIdentifier is an Identifier which extracts the PKey from the request URL path.

func (PathIdentifier[P]) PKey

func (pi PathIdentifier[P]) PKey(r *http.Request) (P, error)

type Querier

type Querier[Q Query] interface {
	Query(*http.Request) (Q, error)
}

type Query

type Query any

type QueryParser

type QueryParser[Q Query] struct {
	// contains filtered or unexported fields
}

QueryParser is a Querier which maps the URL query parameters to a Query.

func NewQueryParser

func NewQueryParser[Q Query]() QueryParser[Q]

func (QueryParser[Q]) Query

func (qp QueryParser[Q]) Query(r *http.Request) (Q, error)

type Resource

type Resource any

type Server

func NewServer

func NewServer[R Resource, Q Query, P PKey](store Store[R, Q, P], encoding Encoding[R], identifier Identifier[P], querier Querier[Q]) Server

type Store

type Store[R Resource, Q Query, P PKey] interface {
	Create(context.Context, *R) error
	Read(context.Context, P, *Q) (*R, error)
	Update(context.Context, P, *R) error
	Delete(context.Context, P) error
	List(context.Context, *Q) ([]R, error)
}

func NewHookStore

func NewHookStore[R Resource, Q Query, P PKey](store Store[R, Q, P]) Store[R, Q, P]

func NewMapStore

func NewMapStore[R Resource, Q Query, P PUintKey](r R, q Q, p P) Store[R, Q, P]

func NewMapStrStore

func NewMapStrStore[R Resource, Q Query, P PStrKey](r R, q Q, p P) Store[R, Q, P]

Directories

Path Synopsis
store

Jump to

Keyboard shortcuts

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