Skip to content

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

License

Notifications You must be signed in to change notification settings

loveyourstack/lys

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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) (stmt string, err error) {
	return lyspg.DeleteUnique(ctx, s.Db, schemaName, tableName, pkColName, id)
}

func (s Store) Insert(ctx context.Context, input Input) (newItem Model, stmt string, err error) {
	return lyspg.Insert[Input, Model](ctx, s.Db, schemaName, tableName, viewName, pkColName, gDbTags, input)
}

func (s Store) Select(ctx context.Context, params lyspg.SelectParams) (items []Model, unpagedCount lyspg.TotalCount, stmt string, 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, corecategory.Model](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
  • Support for getting and filtering enum values
  • Database creation function from embedded SQL files
  • 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)

About

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

Resources

License

Stars

Watchers

Forks

Packages

No packages published