Developing a RESTful API With Go. See how my Go-zero project makes it… | by Kevin Wan | May, 2022

See how my Go-zero challenge makes it even simpler

Photograph by Douglas Lopes on Unsplash

For many startups, we must always focus extra on delivering the merchandise within the early stage of enterprise. The monolithic companies have the benefits of easy structure, simple deployment, and higher improvement productiveness, which can assist us obtain the product necessities shortly. Whereas we use monolithic companies to ship merchandise shortly, we additionally want to order the chance for enterprise increment, so we normally break up completely different enterprise modules clearly in monolithic companies.

We take the mall for example to construct a monolithic service. The mall service is mostly comparatively advanced and consists of a number of modules, the extra vital modules embrace account, product and order modules, and many others. Every module can have its personal impartial enterprise logic, and every module will even depend upon some others. For instance, the order module and the product module will depend upon the account module. Within the monolithic utility this type of dependency is normally achieved by technique calls between modules. Monolithic companies typically share storage sources, corresponding to MySQL and Redis.

The general structure of monolithic companies is comparatively easy, which can also be the benefit of monolithic companies. Buyer requests are parsed by means of DNS and forwarded to the mall’s backend companies by means of Nginx. Mall companies are deployed on cloud hosts. With a view to obtain better throughput and excessive availability, the service will typically deployed with a number of copies. This straightforward structure can carry excessive throughput if properly optimized.

For instance, a request for order particulars interface /order/element is routed to the order module, which depends on the account module and the product module to compose the whole order particulars again to the person, and a number of modules in a single service typically share the database and cache.

The following part describes how one can shortly implement a mall monolithic service based mostly on go-zero. Devs who’ve used go-zero know that we offer an API format file to explain the Restful API, after which we are able to generate the corresponding code by goctl with one command, we simply have to fill within the corresponding enterprise logic within the logic information. The mall service comprises a number of modules, and with a view to make the modules impartial from one another, completely different modules are outlined by separate APIs, however all of the APIs are outlined for a similar service (mall-api).

Create person.api, order.api, product.api and mall.api within the api listing, the place mall.api is the aggregated api file. Different api information are imported by way of import directives.

api
|-- mall.api
|-- order.api
|-- product.api
|-- person.api

mall.api is outlined as follows, the place syntax = "v1" signifies that that is the v1 syntax of zero-api.

syntax = "v1"import "person.api"
import "order.api"
import "product.api"
  • View person particulars
  • Get all orders for a person
syntax = "v1"kind (
UserRequest
ID int64 `path:"id"`
UserReply
ID int64 `json:"id"`
Title string `json:"identify"`
Stability float64 `json:"stability"`
UserOrdersRequest
ID int64 `path:"id"`
UserOrdersReply
ID string `json:"id"`
State uint32 `json:"state"`
CreateAt string `json:"create_at"`

)
service mall-api
@handler UserHandler
get /person/:id (UserRequest) returns (UserReply)
@handler UserOrdersHandler
get /person/:id/orders (UserOrdersRequest) returns (UserOrdersReply)
  • Get order particulars
  • Generate orders
syntax = "v1"kind (
OrderRequest
ID string `path:"id"`
OrderReply
ID string `json:"id"`
State uint32 `json:"state"`
CreateAt string `json:"create_at"`
OrderCreateRequest
ProductID int64 `json:"product_id"`
OrderCreateReply
Code int `json:"code"`

)
service mall-api
@handler OrderHandler
get /order/:id (OrderRequest) returns (OrderReply)
@handler OrderCreateHandler
publish /order/create (OrderCreateRequest) returns (OrderCreateReply)
syntax = "v1"kind ProductRequest 
ID int64 `path:"id"`
kind ProductReply
ID int64 `json:"id"`
Title string `json:"identify"`
Worth float64 `json:"worth"`
Depend int64 `json:"depend"`
service mall-api
@handler ProductHandler
get /product/:id (ProductRequest) returns (ProductReply)

With the API already outlined, producing a service with the API turns into quite simple, we use goctl to generate the monolithic service code.

$ goctl api go -api api/mall.api -dir .

The generated code is structured as follows.

.
├── api
│ ├── mall.api
│ ├── order.api
│ ├── product.api
│ └── person.api
├── and many others
│ └── mall-api.yaml
├─ inner
│ ├── config
│ │ └── config.go
│ ├── handler
│ │ ├── ordercreatehandler.go
│ │ ├── orderhandler.go
│ │ ├── producthandler.go
│ │ ├── routes.go
│ │ ├── userhandler.go
│ │ └─ userordershandler.go
│ ├─ logic
│ │ ├─ ordercreatelogic.go
│ │ ├── orderlogic.go
│ │ ├── productlogic.go
│ │ ├── userlogic.go
│ │ └── userorderslogic.go
│ ├── svc
│ │ └── servicecontext.go
│ └── varieties
│ └── varieties.go
└── mall.go

Let’s clarify the generated information.

  • api: holds the API description file
  • and many others: used to outline the challenge configuration, all configuration gadgets might be written in mall-api.yaml
  • inner/config: the configuration definition of the service
  • inner/handler: the implementation of the handler akin to the routes outlined within the API file
  • inner/logic: used to place the enterprise logic corresponding to every route, the rationale for the excellence between handler and logic is to make the enterprise processing half as much less dependent as doable, to separate HTTP requests from the logic processing code, and to facilitate the next splitting into RPC service
  • inner/svc: used to outline the dependencies of the enterprise logic processing, we are able to create the dependent sources within the primary operate and cross them to handler and logic by way of ServiceContext
  • inner/varieties: defines the API request and response knowledge constructions
  • mall.go: the file the place the primary operate is situated, with the identical identify because the service within the API definition, minus the -api suffix

The generated service might be run with none modification: `

$ go run mall.go
Beginning server at 0.0.0.0:8888...

Subsequent, let’s implement the enterprise logic. The logic shall be easy for demonstration functions, not actual enterprise logic.

First, let’s implement the logic of getting all orders for customers. Since there isn’t any order-related info within the person module, we have to depend on the order module to question the orders of customers, so we add a dependency on OrderLogic in UserOrdersLogic.

kind UserOrdersLogic struct 
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
orderLogic *OrderLogic
func NewUserOrdersLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserOrdersLogic
return &UserOrdersLogic
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
orderLogic: NewOrderLogic(ctx, svcCtx),

Implement a way in OrderLogic to question all orders based mostly on person id

func (l *OrderLogic) ordersByUser(uid int64) ([]*varieties.OrderReply, error) 
if uid == 123
// It ought to really be queried from database or cache
return []*varieties.OrderReply

ID: "236802838635",
State: 1,
CreateAt: "2022-5-12 22:59:59",
,

ID: "236802838636",
State: 1,
CreateAt: "2022-5-10 20:59:59",
,
, nil
return nil, nil

Name the ordersByUser technique within the UserOrders technique of UserOrdersLogic.

func (l *UserOrdersLogic) UserOrders(req *varieties.UserOrdersRequest) (*varieties.UserOrdersReply, error) 
orders, err := l.orderLogic.ordersByUser(req.ID)
if err ! = nil
return nil, err
return &varieties.UserOrdersReply
Orders: orders,
, nil

At this level we restart the mall-api service and request all of the person’s orders within the browser.

http://localhost:8888/user/123/orders

The return result’s as follows, as we anticipated


"orders": [

"id": "236802838635",
"state": 1,
"create_at": "2022-5-12 22:59:59"
,

"id": "236802838636",
"state": 1,
"create_at": "2022-5-10 20:59:59"

]

Subsequent we’ll implement the logic for creating an order. To create an order we first have to see if the merchandise in inventory is sufficient, so we have to depend on the merchandise module within the order module.

kind OrderCreateLogic struct 
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
productLogic *ProductLogic
productLogic *ProductLogic
func NewOrderCreateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *OrderCreateLogic
return &OrderCreateLogic
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
productLogic: NewProductLogic(ctx, svcCtx),

The logic for creating an order is as follows.

const (
success = 0
failure = -1
)
func (l *OrderCreateLogic) OrderCreate(req *varieties.OrderCreateRequest) (*varieties.OrderCreateReply, error)
product, err := l.productLogic.productByID(req.ProductID)
if err ! = nil
return nil, err
if product.Depend > 0
return &varieties.OrderCreateReplyCode: success, nil
return &varieties.OrderCreateReplyCode: failure, nil

The logic of the dependent product module is as follows.

func (l *ProductLogic) Product(req *varieties.ProductRequest) (*varieties.ProductReply, error) 
return l.productByID(req.ID)
func (l *ProductLogic) productByID(id int64) (*varieties.ProductReply, error)
return &varieties.ProductReply
ID: id,
Title: "apple watch 3",
Worth: 3333.33,
Depend: 99,
, nil

The above exhibits that utilizing go-zero to develop a monolithic service may be very easy, which helps us to develop shortly. And we additionally separated modules, which additionally gives the potential for altering to microservices later.

The above instance exhibits that it is extremely easy to make use of go-zero to develop monolithic companies. You solely have to outline the api file, after which the goctl software can robotically generate the challenge code. We solely have to fill within the enterprise logic code within the logic package deal. On this article, we simply demonstrated how one can shortly develop monolithic companies based mostly on go-zero, which doesn’t contain databases. In actual fact, goctl also can generate CRUD and cache code with one command.

And for various enterprise situations, customization may also be achieved by means of customizing templates. And customised templates might be shared throughout the staff by means of distant git repositories, which might be very environment friendly for staff collaboration.

Wish to Join?Welcome to make use of go-zero and star to assist us!

More Posts