Categorygithub.com/loveyourstack/lys
modulepackage
0.1.36
Repository: https://github.com/loveyourstack/lys.git
Documentation: pkg.go.dev

# README

lys - LoveYourStack

Packages for rapid development of REST APIs handling database CRUD actions.

Only available for PostgreSQL. Most suitable for "database-first" Go developers.

Example usage

Define store package (wiki)

A store package contains database access functions for a specific table or view, in this case the "category" table in the "core" schema.

Boilerplate is minimized through the optional use of generic database CRUD functions.

package corecategory

// define constants for this database table, which get passed to generic database functions below
const (
	schemaName     string = "core"
	tableName      string = "category"
	viewName       string = "category"
	pkColName      string = "id"
	defaultOrderBy string = "name"
)

// columns required when creating or updating a record
type Input struct {
	Name string `db:"name" json:"name,omitempty" validate:"required"`
}

// columns outputted when selecting a record. Note that Input is embedded
type Model struct {
	Id    int64 `db:"id" json:"id"`
	Input
}

type Store struct {
	Db *pgxpool.Pool
}

// define functions for this table as methods of the Store struct
// use lyspg generic functions if possible, but can also write custom implementations

func (s Store) Delete(ctx context.Context, id int64) error {
	return lyspg.DeleteUnique(ctx, s.Db, schemaName, tableName, pkColName, id)
}

func (s Store) Insert(ctx context.Context, input Input) (newId int64, err error) {
	return lyspg.Insert[Input, int64](ctx, s.Db, schemaName, tableName, pkColName, input)
}

func (s Store) Select(ctx context.Context, params lyspg.SelectParams) (items []Model, unpagedCount lyspg.TotalCount, err error) {
	return lyspg.Select[Model](ctx, s.Db, schemaName, tableName, viewName, defaultOrderBy, gDbTags, params)
}

// etc

Create routes (wiki)

Pass the store package to generic GET, POST, etc handlers to get full REST API CRUD functionality for this table with minimal boilerplate.

package main

func (srvApp *httpServerApplication) getRoutes(apiEnv lys.Env) http.Handler {

	endpoint := "/core-categories"

	// get full CRUD functionality using lys generic handlers, passing the store defined above
	// no framework: free to write custom handlers when needed

	categoryStore := corecategory.Store{Db: srvApp.Db}
	r.HandleFunc(endpoint, lys.Get[corecategory.Model](apiEnv, categoryStore)).Methods("GET")
	r.HandleFunc(endpoint+"/{id}", lys.GetById[corecategory.Model](apiEnv, categoryStore)).Methods("GET")
	r.HandleFunc(endpoint, lys.Post[corecategory.Input, int64](apiEnv, categoryStore)).Methods("POST")
	r.HandleFunc(endpoint+"/{id}", lys.Put[corecategory.Input](apiEnv, categoryStore)).Methods("PUT")
	r.HandleFunc(endpoint+"/{id}", lys.Patch(apiEnv, categoryStore)).Methods("PATCH")
	r.HandleFunc(endpoint+"/{id}", lys.Delete(apiEnv, categoryStore)).Methods("DELETE")
}

Use routes

We can now start the HTTP server app and use the routes above.

curl localhost:8010/core-categories?name=Seafood
curl localhost:8010/core-categories/1
curl --header "Content-Type: application/json" --request POST --data '{"name":"Fruit"}' localhost:8010/core-categories
# etc

See the Northwind sample application for a complete application using these packages.

Features

  • Library only: is not a framework, and does not use code generation, so can be overriden at every step to deal with exceptional cases
  • Support for GET many, GET single, POST, PUT, PATCH and DELETE
  • Support for sorting, paging and filtering GET results via customizable URL params
  • Uses pgx for database access and only uses parameterized SQL queries
  • Support for Excel and CSV output
  • Uses generics and reflection to minimize boilerplate
  • Custom date/time types with zero default values and sensible JSON formats
  • Fast rowcount function, including estimated count for large tables with query conditions
  • Struct validation using validator
  • Distinction between user errors (unlogged, reported to user) and application errors (logged, hidden from user)
  • Provides useful bulk insert (COPY) wrapper, and bulk update/delete (batch) wrappers
  • Support for getting and filtering enum values
  • Database creation function from embedded SQL files
  • Archive (soft delete) + restore functions
  • and more. See the wiki

Current limitations

  • Only supports PostgreSQL
  • No database obfuscation. Struct "db" tags must be added and must be identical to the "json" tag, unless the latter is "-"
  • Limited support for database date/time arrays

Testing

See CONTRIBUTING.md for setup instructions.

Supported Go and PostgreSQL Versions

Preliminary values:

Go 1.16+ (due to embed.FS)

PostgreSQL 13+ (due to gen_random_uuid)

# Packages

Package lysclient contains types and functions to help test a REST API which was created using lys functions.
No description provided by the author
Package lyserr contains structs related to error handling used in lys and lyspg.
No description provided by the author
Package lysgen contains experimental functions to generate code from Postgres database tables.
Package lysmeta contains functions that analyze structs.
Package lyspg contains structs and functions providing generic CRUD operations on a Postgres database.
Package lyspgdb contains functions for creating and monitoring Postgres databases.
No description provided by the author
Package lysstring contains string functions.
Package lystype contains date/time types used in lys and lyspg.

# Functions

Archive handles moving a record from the supplied store into its archived table.
Archive handles moving a record from the supplied store into its archived table.
AuthorizeRole is middleware that checks that the user has one of the supplied allowedRoles is intended for use in subroutes.
DecodeJsonBody decodes the supplied json body into dest and checks for a variety of error conditions adapted from https://www.alexedwards.net/blog/how-to-properly-parse-a-json-request-body.
Delete handles deletion of a single item using the supplied store.
ExtractFields returns a slice of strings parsed from the request's fields param.
ExtractFilters returns a slice of conditions parsed from the request's params to get urlValues from a request: r.Url.Query().
ExtractFormat returns.
ExtractGetRequestModifiers reads the Url params of the supplied GET request and converts them into a GetReqModifiers.
ExtractJsonBody reads and validates the body of the supplied request.
ExtractPaging returns paging variables parsed from a request's paging params page defaults to 1, perPage defaults to defaultPerPage.
ExtractSorts returns an array of SQL sorting statements parsed from the request's sort param.
FileResponse opens the supplied file and streams it to w as a file.
FillGetOptions returns input GetOptions if they are passed, and sets any unset fields to a sensible default value.
FillPostOptions returns input PostOptions if they are passed, and sets any unset fields to a sensible default value.
Get handles retrieval of multiple items from the supplied store.
GetById handles retrieval of a single item from the supplied store using an integer id.
GetByUuid handles retrieval of a single item from the supplied store using a text id.
GetEnumValues returns enum values from the supplied schema and enum type name.
GetSimple handles retrieval of all items returned by selectFunc, which may only take ctx as param.
GetUserFromCtx returns the user from ctx.
GetUserNameFromCtx returns the username if it can be obtained from ctx, otherwise the supplied default value.
GetWithLastSync is a wrapper for Get which adds the lastSyncAt timestamp from the supplied func to the JSON response.
HandleDbError returns a generic error message to the API user and includes the failing statement in the error log.
HandleError is the general method for handling API errors where err could contain wrapped errors of other types.
HandleExtError returns the external message to the API user and logs the error.
HandleInternalError returns a generic error message to the API user and logs the error.
HandleUserError returns a helpful message to the API user, but does not log the error.
JsonResponse marshals the supplied StdResponse to json and writes it to w.
Message returns the supplied msg in the Data field.
MoveRecordsById handles moving record(s) back and forth between the main table and its corresponding archived table.
MoveRecordsById handles moving record(s) back and forth between the main table and its corresponding archived table.
NotFound provides a response informing the user that the requested route was not found.
Patch handles changing some of an item's fields using the supplied store.
PgSleep creates an artifical longrunning query in the db which can be viewed using pg_stat_activity used for testing context cancelation.
Post
Post handles creating a new item using the supplied store and returning an output (the new item or its ID) in the response.
ProcessSlice extracts a slice from the req body and passes it into the supplied processFunc.
Put handles changing an item using the supplied store.
Restore handles moving a record from the store's archived table back to the main table.
Restore handles moving a record from the store's archived table back to the main table.

# Constants

data.
response constants.
response constants.
response constants.
validation user errors.
validation user errors.
validation user errors.
validation user errors.
the handling func was expecting id to be unique, but it is not.
validation user errors.
the Id sent is not present in the relevant table.
validation user errors.
authorization failed.
validation user errors.
failed to get ReqUserInfo from context.
output format consts.
output format consts.
output format consts.
response constants.
status.
ReqUserInfoCtxKey is the key to be used when binding a ReqUserInfo to a request via context.

# Variables

No description provided by the author

# Structs

Env (environment) contains objects and options needed by API calls.
No description provided by the author
No description provided by the author
GetOptions contains the options used when processing GET requests, such as paging param names and default values Since the json field names are used as filters, param names should be chosen which will never appear as json field names.
GetReqModifiers contains data from a GET request's Url params which is used to modify a database SELECT statement.
PostOptions contains the options used when processing POST or PUT requests.
ReqUserInfo contains data about the API user which is added to request context after authentication.
StdResponse is the return type of all API routes.
SubRoute contains a Url path and the function returning the subrouter to process that path.

# Type aliases

RouteAdderFunc is a function returning a subrouter.