Documentation
¶
Index ¶
- Variables
- func GetIDParam(r *http.Request, name string) string
- func GetLoggerFromContext(ctx context.Context) *slog.Logger
- func GetResourceFromContext[T Resource](ctx context.Context, key ContextKey) (T, error)
- func IDParamKey(name string) string
- func NewContextWithLogger(ctx context.Context, logger *slog.Logger) context.Context
- func Test[T Resource](t *testing.T, api *API[T], r *http.Request) *httptest.ResponseRecorder
- func TestServe[T Resource](t *testing.T, api *API[T]) (string, func())
- func TestWithParentRoute[T, P Resource](t *testing.T, api *API[T], parent P, parentName, parentBasePath string, ...) *httptest.ResponseRecorder
- type API
- func (a *API[T]) AddCustomIDRoute(route chi.Route)
- func (a *API[T]) AddCustomRoute(route chi.Route)
- func (a *API[T]) AddMiddlewares(m chi.Middlewares)
- func (a *API[T]) AddNestedAPI(childAPI relatedAPI)
- func (a *API[T]) AnyClient(addr string) *Client[*AnyResource]
- func (a *API[T]) Base() string
- func (a *API[T]) Client(addr string) *Client[T]
- func (a *API[T]) Delete(w http.ResponseWriter, r *http.Request)
- func (a *API[T]) Get(w http.ResponseWriter, r *http.Request)
- func (a *API[T]) GetAll(w http.ResponseWriter, r *http.Request)
- func (a *API[T]) GetFromRequest(r *http.Request) (T, *ErrResponse)
- func (a *API[T]) GetIDParam(r *http.Request) string
- func (a *API[T]) GetParentIDParam(r *http.Request) string
- func (a *API[T]) GetRequestBodyFromContext(ctx context.Context) T
- func (a *API[T]) GetRequestedResource(r *http.Request) (T, *ErrResponse)
- func (a *API[T]) GetRequestedResourceAndDo(do func(*http.Request, T) (render.Renderer, *ErrResponse)) http.HandlerFunc
- func (a *API[T]) GetResourceFromContext(ctx context.Context) (T, error)
- func (a *API[T]) IDParamKey() string
- func (a *API[T]) Name() string
- func (a *API[T]) NewContextWithRequestBody(ctx context.Context, item T) context.Context
- func (a *API[T]) Parent() relatedAPI
- func (a *API[T]) ParentContextKey() ContextKey
- func (a *API[T]) Patch(w http.ResponseWriter, r *http.Request)
- func (a *API[T]) Post(w http.ResponseWriter, r *http.Request)
- func (a *API[T]) Put(w http.ResponseWriter, r *http.Request)
- func (a *API[T]) ReadRequestBodyAndDo(do func(*http.Request, T) (T, *ErrResponse)) http.HandlerFunc
- func (a *API[T]) ResponseWrapper(responseWrapper func(T) render.Renderer)
- func (a *API[T]) Route(r chi.Router)
- func (a *API[T]) Router() chi.Router
- func (a *API[T]) RunCLI()
- func (a *API[T]) RunWithArgs(out io.Writer, args []string, port int, address string, pretty bool) error
- func (a *API[T]) Serve(port string)
- func (a *API[T]) SetAfterDelete(after func(*http.Request) *ErrResponse)
- func (a *API[T]) SetBeforeDelete(before func(*http.Request) *ErrResponse)
- func (a *API[T]) SetGetAllFilter(f func(*http.Request) FilterFunc[T])
- func (a *API[T]) SetOnCreateOrUpdate(onCreateOrUpdate func(*http.Request, T) *ErrResponse)
- func (a *API[T]) SetStorage(s Storage[T])
- func (a *API[T]) Stop()
- type AnyResource
- type Client
- func (c *Client[T]) Delete(ctx context.Context, id string, parentIDs ...string) error
- func (c *Client[T]) Get(ctx context.Context, id string, parentIDs ...string) (T, error)
- func (c *Client[T]) GetAll(ctx context.Context, query url.Values, parentIDs ...string) (*ResourceList[T], error)
- func (c *Client[T]) MakeRequest(req *http.Request, expectedStatusCode int) (*http.Response, error)
- func (c *Client[T]) MakeRequestWithResponse(req *http.Request, expectedStatusCode int) (T, error)
- func (c *Client[T]) NewRequestWithParentIDs(ctx context.Context, method string, body io.Reader, id string, ...) (*http.Request, error)
- func (c *Client[T]) Patch(ctx context.Context, id string, resource T, parentIDs ...string) (T, error)
- func (c *Client[T]) PatchRaw(ctx context.Context, id, body string, parentIDs ...string) (T, error)
- func (c *Client[T]) Post(ctx context.Context, resource T, parentIDs ...string) (T, error)
- func (c *Client[T]) PostRaw(ctx context.Context, body string, parentIDs ...string) (T, error)
- func (c *Client[T]) Put(ctx context.Context, resource T, parentIDs ...string) error
- func (c *Client[T]) PutRaw(ctx context.Context, id, body string, parentIDs ...string) error
- func (c *Client[T]) SetHTTPClient(client *http.Client)
- func (c *Client[T]) SetRequestEditor(requestEditor RequestEditor)
- func (c *Client[T]) URL(id string, parentIDs ...string) (string, error)
- type ContextKey
- type DefaultResource
- type ErrResponse
- type FilterFunc
- type ID
- type MapStorage
- type Patcher
- type RequestEditor
- type Resource
- type ResourceList
- type Storage
Constants ¶
This section is empty.
Variables ¶
var ErrMethodNotAllowedResponse = &ErrResponse{HTTPStatusCode: http.StatusMethodNotAllowed, StatusText: "Method not allowed."}
var ErrNotFound = errors.New("resource not found")
var ErrNotFoundResponse = &ErrResponse{HTTPStatusCode: http.StatusNotFound, StatusText: "Resource not found."}
Functions ¶
func GetIDParam ¶
GetIDParam gets resource ID from the request URL for a resource by name
func GetLoggerFromContext ¶
GetLoggerFromContext returns the structured logger from the context. It expects to use an HTTP request context to get a logger with details from middleware
func GetResourceFromContext ¶
func GetResourceFromContext[T Resource](ctx context.Context, key ContextKey) (T, error)
GetResourceFromContext gets the API resource from request context
func IDParamKey ¶
IDParamKey gets the chi URL param key used for the ID of a resource
func NewContextWithLogger ¶
NewContextWithLogger stores a structured logger in the context
func Test ¶
Test is meant to be used in external tests to automatically handle setting up routes and using httptest
Types ¶
type API ¶
type API[T Resource] struct { // contains filtered or unexported fields }
API encapsulates all handlers and other pieces of code required to run the CRUID API based on the provided Resource type
func NewAPI ¶
NewAPI initializes an API using the provided name, base URL path, and function to create a new instance of the resource with defaults
func (*API[T]) AddCustomIDRoute ¶
AddCustomIDRoute appends a custom API route to the base path after the ID URL parameter: /base/{ID}/custom-route. The handler for this route can access the requested resource using GetResourceFromContext
func (*API[T]) AddCustomRoute ¶
AddCustomRoute appends a custom API route to the base path: /base/custom-route
func (*API[T]) AddMiddlewares ¶
func (a *API[T]) AddMiddlewares(m chi.Middlewares)
AddMiddlewares appends chi.Middlewares to existing middlewares
func (*API[T]) AddNestedAPI ¶
func (a *API[T]) AddNestedAPI(childAPI relatedAPI)
AddNestedAPI adds a child API to this API and initializes the parent relationship on the child's side
func (*API[T]) AnyClient ¶
func (a *API[T]) AnyClient(addr string) *Client[*AnyResource]
AnyClient returns a new Client based on the API's configuration. It is a shortcut for NewClient
func (*API[T]) Client ¶
Client returns a new Client based on the API's configuration. It is a shortcut for NewClient
func (*API[T]) Delete ¶
func (a *API[T]) Delete(w http.ResponseWriter, r *http.Request)
Delete is used to delete the resource at /base/{ID}
func (*API[T]) Get ¶
func (a *API[T]) Get(w http.ResponseWriter, r *http.Request)
Get is the handler for /base/{ID} and returns a requested resource by ID
func (*API[T]) GetAll ¶
func (a *API[T]) GetAll(w http.ResponseWriter, r *http.Request)
GetAll is the handler for /base and returns an array of resources
func (*API[T]) GetFromRequest ¶
func (a *API[T]) GetFromRequest(r *http.Request) (T, *ErrResponse)
GetFromRequest will read the API's resource type from the request body or request context
func (*API[T]) GetIDParam ¶
GetIDParam gets resource ID from the request URL for this API's resource
func (*API[T]) GetParentIDParam ¶
GetParentIDParam reads the URL param from the request to get the ID of the parent resource
func (*API[T]) GetRequestBodyFromContext ¶
GetRequestBodyFromContext gets an API resource from the request context. It can only be used in URL paths that include the resource ID
func (*API[T]) GetRequestedResource ¶
func (a *API[T]) GetRequestedResource(r *http.Request) (T, *ErrResponse)
GetRequestedResource reads the API's resource from storage based on the ID in the request URL
func (*API[T]) GetRequestedResourceAndDo ¶
func (a *API[T]) GetRequestedResourceAndDo(do func(*http.Request, T) (render.Renderer, *ErrResponse)) http.HandlerFunc
GetRequestedResourceAndDo is a wrapper that handles getting a resource from storage based on the ID in the request URL and rendering the response. This is useful for imlementing a CustomIDRoute
func (*API[T]) GetResourceFromContext ¶
GetResourceFromContext gets the API resource from request context
func (*API[T]) IDParamKey ¶
IDParamKey gets the chi URL param key used for this API
func (*API[T]) NewContextWithRequestBody ¶
NewContextWithRequestBody stores the API resource in the context
func (*API[T]) ParentContextKey ¶
func (a *API[T]) ParentContextKey() ContextKey
ParentContextKey returns the context key for the direct parent's resource
func (*API[T]) Patch ¶
func (a *API[T]) Patch(w http.ResponseWriter, r *http.Request)
Put is used to modify resources at /base/{ID}
func (*API[T]) Post ¶
func (a *API[T]) Post(w http.ResponseWriter, r *http.Request)
Post is used to create new resources at /base
func (*API[T]) Put ¶
func (a *API[T]) Put(w http.ResponseWriter, r *http.Request)
Put is used to idempotently create or modify resources at /base/{ID}
func (*API[T]) ReadRequestBodyAndDo ¶
func (a *API[T]) ReadRequestBodyAndDo(do func(*http.Request, T) (T, *ErrResponse)) http.HandlerFunc
ReadRequestBodyAndDo is a wrapper that handles decoding the request body into the resource type and rendering a response
func (*API[T]) ResponseWrapper ¶
ResponseWrapper sets a function that returns a new Renderer before responding with T. This is used to add more data to responses that isn't directly from storage
func (*API[T]) RunWithArgs ¶
func (*API[T]) SetAfterDelete ¶
func (a *API[T]) SetAfterDelete(after func(*http.Request) *ErrResponse)
SetAfterDelete sets a function that is executed after deleting a resource. It is useful for additional cleanup or other actions that should be done after deleting
func (*API[T]) SetBeforeDelete ¶
func (a *API[T]) SetBeforeDelete(before func(*http.Request) *ErrResponse)
SetBeforeDelete sets a function that is executing before deleting a resource. It is useful for additional validation before completing the delete
func (*API[T]) SetGetAllFilter ¶
func (a *API[T]) SetGetAllFilter(f func(*http.Request) FilterFunc[T])
SetGetAllFilter sets a function that can use the request context to create a filter for GetAll. Use this to introduce query parameters for filtering resources
func (*API[T]) SetOnCreateOrUpdate ¶
func (a *API[T]) SetOnCreateOrUpdate(onCreateOrUpdate func(*http.Request, T) *ErrResponse)
SetOnCreateOrUpdate runs on POST, PATCH, and PUT requests before saving the created/updated resource. This is useful for adding more validations or performing tasks related to resources such as initializing schedules or sending events
func (*API[T]) SetStorage ¶
SetStorage sets a custom storage interface for the API
type AnyResource ¶
AnyResource is intended to create a "generic" Client
func (AnyResource) GetID ¶
func (ar AnyResource) GetID() string
func (*AnyResource) Render ¶
func (*AnyResource) Render(w http.ResponseWriter, r *http.Request) error
type Client ¶
type Client[T Resource] struct { // contains filtered or unexported fields }
Client is used to interact with the provided Resource's API
func NewSubClient ¶
NewSubClient creates a Client as a child of an existing Client. This is useful for accessing nested API resources
func (*Client[T]) GetAll ¶
func (c *Client[T]) GetAll(ctx context.Context, query url.Values, parentIDs ...string) (*ResourceList[T], error)
GetAll gets all resources from the API
func (*Client[T]) MakeRequest ¶
MakeRequest generically sends an HTTP request after calling the request editor and checks the response code
func (*Client[T]) MakeRequestWithResponse ¶
MakeRequestWithResponse calls MakeRequest and decodes the response body into the Resource type
func (*Client[T]) NewRequestWithParentIDs ¶
func (c *Client[T]) NewRequestWithParentIDs(ctx context.Context, method string, body io.Reader, id string, parentIDs ...string) (*http.Request, error)
NewRequestWithParentIDs uses http.NewRequestWithContext to create a new request using the URL created from the provided ID and parent IDs
func (*Client[T]) Patch ¶
func (c *Client[T]) Patch(ctx context.Context, id string, resource T, parentIDs ...string) (T, error)
Patch makes a PATCH request to modify a resource by ID
func (*Client[T]) PatchRaw ¶
PatchRaw makes a PATCH request to modify a resource by ID. It uses the provided string as the request body
func (*Client[T]) PutRaw ¶
PutRaw makes a PUT request to create/modify a resource by ID. It uses the provided string as the request body
func (*Client[T]) SetHTTPClient ¶
SetHTTPClient allows overriding the Clients HTTP client with a custom one
func (*Client[T]) SetRequestEditor ¶
func (c *Client[T]) SetRequestEditor(requestEditor RequestEditor)
SetRequestEditor sets a request editor function that is used to modify all requests before sending. This is useful for adding custom request headers or authorization
type ContextKey ¶
type ContextKey string
ContextKey is used to store API resources in the request context
type DefaultResource ¶
type DefaultResource struct {
ID ID `json:"id"`
}
DefaultResource implements Resource and uses the provided ID type. Extending this type is the easiest way to implement a Resource based around the provided ID type
func NewDefaultResource ¶
func NewDefaultResource() DefaultResource
NewDefaultResource creates a DefaultResource with a new random ID
func (*DefaultResource) GetID ¶
func (dr *DefaultResource) GetID() string
func (*DefaultResource) Render ¶
func (*DefaultResource) Render(w http.ResponseWriter, r *http.Request) error
type ErrResponse ¶
type ErrResponse struct {
Err error `json:"-"`
HTTPStatusCode int `json:"-"`
StatusText string `json:"status"` // user-level status message
AppCode int64 `json:"code,omitempty"` // application-specific error code
ErrorText string `json:"error,omitempty"` // application-level error message, for debugging
}
ErrResponse is an error that implements Renderer to be used in HTTP response
func ErrInvalidRequest ¶
func ErrInvalidRequest(err error) *ErrResponse
func ErrRender ¶
func ErrRender(err error) *ErrResponse
func InternalServerError ¶
func InternalServerError(err error) *ErrResponse
func (*ErrResponse) Error ¶
func (e *ErrResponse) Error() string
func (*ErrResponse) Render ¶
func (e *ErrResponse) Render(_ http.ResponseWriter, r *http.Request) error
type FilterFunc ¶
FilterFunc is used for GetAll to filter resources that are read from storage
type ID ¶
ID is a type that can be optionally used to improve Resources and their APIs. It uses xid to create unique identifiers and implements a custom Bind method to:
- Disallow POST requests with IDs
- Automatically set new ID on POSTed resources
- Enforce that ID is set
- Do not allow changing ID with PATCH
type MapStorage ¶
MapStorage is the default implementation of the Storage interface that just uses a map
func (MapStorage[T]) Delete ¶
func (m MapStorage[T]) Delete(id string) error
func (MapStorage[T]) Get ¶
func (m MapStorage[T]) Get(id string) (T, error)
func (MapStorage[T]) GetAll ¶
func (m MapStorage[T]) GetAll(filter FilterFunc[T]) ([]T, error)
func (MapStorage[T]) Set ¶
func (m MapStorage[T]) Set(resource T) error
type Patcher ¶
type Patcher[T Resource] interface { Patch(T) *ErrResponse }
Patcher is used to optionally-enable PATCH endpoint. Since the library cannot generically modify resources without using reflection, implement Patch function to use the input to modify the receiver
type RequestEditor ¶
RequestEditor is a function that can modify the HTTP request before sending
type Resource ¶
type Resource interface {
comparable
// Renderer is used to control the output behavior when creating a response.
// Use this for any after-request logic or response modifications
render.Renderer
// Binder is used to control the input behavior, after decoding the request.
// Use it for input validation or additional modification of the resource using request headers or other params
render.Binder
GetID() string
}
Resource is an interface/constraint used for API resources. In order to use API, you must have types that implement this. It enables HTTP request/response handling and getting resources by ID
type ResourceList ¶
ResourceList is used to automatically enable the GetAll endpoint that returns an array of Resources
func (*ResourceList[T]) Render ¶
func (rl *ResourceList[T]) Render(w http.ResponseWriter, r *http.Request) error
type Storage ¶
type Storage[T Resource] interface { // Get a single resource by ID Get(string) (T, error) // GetAll will return all resources that match the provided FilterFunc GetAll(FilterFunc[T]) ([]T, error) // Set will save the provided resource Set(T) error // Delete will delete a resource by ID Delete(string) error }
Storage defines how the API will interact with a storage backend
