
typical-rest-server
The project status is WIP (Work in progress) which means the author continously evaluate and improve the project.
Pragmatic Golang RESTful Server Implementation. The project using typical-go as its build-tool.
- Application
- Go-Standards Project Layout
- Environment Variable Configuration
- Health-Check and Debug API
- Graceful Shutdown
- Layered architecture
- SOLID Principle
- Dependency Injection (using
@ctor annotation)
- ORMHate
- Database Transaction
- HTTP Server
- Echo framework
- Server Side Caching
- Skip Caching (Header
Cache-Control: no-cache)
- Set Expiration Time (Header
Cache-Control: max-age=120)
- Return 304 if not modified (Header
If-Modified-Since: Sat, 31 Oct 2020 10:28:02 GMT)
- Request ID in logger
- RESTful
- Create Resource (
POST verb)
- Update Resource (
UPDATE verb)
- Partially Update Resource (
PATCH verb)
- Find Resource (
GET verb)
- Offset Pagination (Query param
?limit=100&offset=0)
- Sorting (Query param
?sort=-title,created_at)
- Check resource (
HEAD verb)
- Delete resource (
DELETE verb, idempotent)
- Testing
- Table Driven Test
- Mocking (using
@mock annotation)
- Others
- Database migration and seed tool
- Generate code,
.env file and USAGE.md according the configuration (using @envconfig annotation)
- Generate code for repository layer (using
@entity annotation)
- Releaser
Run/Test Project
Copy .env.sample for working configuration
cp .env.sample .env # copy the working .env
Setup the local environment
./typicalw docker up # equivalent with `docker-compose up -d`
./typicalw reset # reset infra: drop, create and migrate postgres database
Run application:
./typicalw run # run the application
Test application:
./typicalw test # run test
Project descriptor at tools/typical-build/typical-build.go
var descriptor = typgo.Descriptor{
ProjectName: "typical-rest-server",
ProjectVersion: "0.9.7",
ProjectLayouts: []string{"internal", "pkg"},
// commands ...
Project Layout
Typical-Rest encourage standard go project layout
Source codes:
internal: private codes for the project
pkg: shareable codes e.g. helper/utitily Library
cmd: the main package
Others directory:
tools Supporting tool for the project e.g. Build Tool
api Any related scripts for API e.g. api-model script (swagger, raml, etc) or client script
databases Any related scripts for Databases e.g. migration scripts and seed data
Layered Architecture
Typical-Rest encourage layered architecture as most adoptable architectural pattern
Dependency Injection
Typical-Rest encourage dependency injection using uber-dig and annotations (@ctor for constructor and @dtor for destructor).
// NewConn ...
// @ctor
func NewConn() *sql.DB{
}
Application Config
Typical-Rest encourage application config with environment variables using envconfig and annotation (@envconfig).
type (
// AppCfg application configuration
// @envconfig (prefix:"APP")
AppCfg struct {
Address string `envconfig:"ADDRESS" default:":8089" required:"true"`
Debug bool `envconfig:"DEBUG" default:"true"`
}
)
Generate usage documentation (USAGE.md) and .env file
// in typical-build
&typcfg.EnvconfigAnnotation{
DotEnv: ".env", // generate .env file
UsageDoc: "USAGE.md", // generate USAGE.md
}
Mocking
Typical-Rest encourage mocking using gomock and annotation(@mock).
type(
// Reader responsible to read
// @mock
Reader interface{
Read() error
}
)
Mock class will be generated in *_mock package
Database Transaction
In Repository layer
func (r *RepoImpl) Delete(ctx context.Context) (int64, error) {
txn, err := dbtxn.Use(ctx, r.DB) // use transaction if begin detected
if err != nil { // create transaction error
return -1, err
}
db := txn.DB // transaction object or database connection
// result, err := ...
if err != nil {
txn.SetError(err) // set the error and plan for rollback
return -1, err
}
// ...
}
In Service layer
func (s *SvcImpl) SomeOperation(ctx context.Context) error{
// begin the transaction
// and commit/rollback in end function
defer dbtxn.Begin(&ctx)()
// ...
}
Server-Side Cache
Use echo middleware to handling cache
cacheStore := &cachekit.Store{
Client: redis.NewClient(&redis.Options{Addr: "localhost:6379"}),
DefaultMaxAge: 30 * time.Second,
PrefixKey: "cache_",
}
e := echo.New()
e.GET("/", handle, cacheStore.Middleware)
References
Golang:
RESTful API:
License
This project is licensed under the MIT License - see the LICENSE.md file for details