API sample by using echo and gorm
1.Start
1.1 launch
# Go dependency management tool https://golang.github.io/dep/
dep ensure
# launch DB container
docker-compose up
# Create DB
docker exec echo-db createdb -U postgres echo
# launch App
go run *.go
1.2 send a request
# use httpie
http POST localhost:1323/api/v1/employee name=zhl email=zhanghl@yahoo.co.jp company=echo password=password
# Response
HTTP/1.1 200 OK
Content-Length: 243
Content-Type: application/json; charset=UTF-8
Date: Thu, 29 Mar 2018 09:51:14 GMT
{
"company": "echo",
"created_at": "2018-03-29T18:51:14.984260399+09:00",
"deleted_at": null,
"email": "zhanghl@yahoo.co.jp",
"id": 1,
"name": "zhl",
"password": "password",
"updated_at": "2018-03-29T18:51:14.984260399+09:00"
}
2.See app.go
Use echo and gorm
// define a struct which has an echo pointer and a DB pointer
type app struct {
* echo.Echo
db * gorm.DB
}
Use configor for configuration.configor is Golang Configuration tool that support YAML, JSON, TOML, Shell Environment by writing struct tags.
# define host and port for server
var config = struct {
Host string `default:"localhost"`
Port string `default:"1323"`
}{}
Instead using global variable, wrap a DB pointer into context as a middleware of echo so that we can decoupling from top and it's easy to test.
func main () {
// Load configuration
configor .Load (& config )
app := & app {echo .New (), db .New ()}
app .Debug = true
// Routing setup
app .initRouter ()
defer app .db .Close ()
// Wrap db pointer into echo.context as a middleware
app .Use (func (next echo.HandlerFunc ) echo.HandlerFunc {
return func (context echo.Context ) error {
context .Set ("db" , app .db )
return next (context )
}
})
// launch
app .Logger .Fatal (app .Start (config .Host + ":" + config .Port ))
}
3.See router
Make a route group and register routes for HTTP method.Routes can be registered by specifying HTTP method, path and a matching handler.
func (app * app ) initRouter () {
v1 := app .Group ("/api/v1" )
{
v1 .GET ("/employee" , echo .HandlerFunc (GetEmployees ))
v1 .GET ("/employee/:id" , echo .HandlerFunc (GetEmployee ))
v1 .POST ("/employee" , echo .HandlerFunc (CreateEmployee ))
v1 .PATCH ("/employee/:id" , echo .HandlerFunc (UpdateEmployee ))
v1 .DELETE ("/employee/:id" , echo .HandlerFunc (DeleteEmployee ))
}
}
4.See handler
echo wraps HTTP request and response into context. So you can get request parameter from context, put a HTTP status code and your data into JSON response.
func GetEmployee (context echo.Context ) error {
id , err := strconv .Atoi (context .Param ("id" ))
if err != nil {
echo .NewHTTPError (http .StatusBadRequest , "Employee Id must be int" )
}
employee := & Employee {Model : Model {ID : id }}
if err := employee .Find (context .Get ("db" ).(* gorm.DB )); err != nil {
return echo .NewHTTPError (http .StatusNotFound , "Employee does not exist." )
}
return context .JSON (http .StatusOK , employee )
}
5. See model
Extract the common parts of all model struct such as id, created_at and so on.The struct tag of json means response JSON key.The struct tag of gorm looks like SQL and in fact it really works as DDL.
type Model struct {
ID int `json:"id" gorm:"primary_key"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt * time.Time `json:"deleted_at" grom:"index"`
}
Inherit common parts and write json and gorm struct tags to define your response and your data type.
type Employee struct {
Model
Name string `json:"name" gorm:"type:varchar(255);not null"`
Company string `json:"company" gorm:"type:varchar(255)"`
Email string `json:"email" gorm:"type:varchar(255);not null;unique"`
Password string `json:"password" gorm:"type:varchar(255);not null"`
}
Simple CURD. For Detail check gorm docs .It also support advanced topics like Raw SQL, transaction, migration and so on.
func (e * Employee ) Create (db * gorm.DB ) (err error ) {
err = db .Create (e ).Error
return
}
func (e * Employee ) Find (db * gorm.DB ) (err error ) {
err = db .First (e ).Error
return
}
func (e * Employee ) Update (db * gorm.DB ) (err error ) {
err = db .Model (e ).Update (e ).Error
return
}
func (e * Employee ) Delete (db * gorm.DB ) (err error ) {
err = db .Delete (e ).Error
return
}
6. See db.go
The first import line means that we use PostgreSQL dialect and initialize it. Your can change Mysql or SQLite.
import (
_ "github.com/jinzhu/gorm/dialects/postgres"
"github.com/jinzhu/gorm"
"echo-sample/models"
"github.com/jinzhu/configor"
"fmt"
)
DB configuration by using configor .
var config = struct {
DBName string `default:"echo"`
User string `default:"postgres"`
Host string `default:"localhost"`
Password string `default:"password" env:"DBPassword"`
Port string `default:"5433"`
}{}
Load the DB configuration and create a DB connection. Shut down by throw a panic if DB connection failed. Then make set the DB logger on and auto migrate your data.
func New () (db * gorm.DB ) {
configor .Load (& config )
args := fmt .Sprintf (
"host=%s port=%s user=%s dbname=%s sslmode=disable password=%s" ,
config .Host ,
config .Port ,
config .User ,
config .DBName ,
config .Password )
db , err := gorm .Open ("postgres" , args )
if err != nil {
panic (err )
}
db .LogMode (true )
autoMigrate (db )
return
}
Just pass the model(DB table) to the AutoMigration function to ensure auto migration.
func autoMigrate (db * gorm.DB ) {
db .AutoMigrate (& models.Employee {})
}
7. Add more in future...