In the api
directory of the project, we begin defining our CRUD
APIs.
- We use the
RESTful
style for API design, fully utilizing theGET/POST/PUT/DELETE
HTTP Methods. This standardized design results in very elegant APIs. - Similarly, we start with version
v1
. Using version numbers is a good development habit, which helps in maintaining compatibility in future APIs.
Create
type CreateReq struct {
g.Meta `path:"/user" method:"post" tags:"User" summary:"Create user"`
Name string `v:"required|length:3,10" dc:"user name"`
Age uint `v:"required|between:18,200" dc:"user age"`
}
type CreateRes struct {
Id int64 `json:"id" dc:"user id"`
}
Brief Introduction:
- In API definitions,
g.Meta
is used to manage API metadata information, which are defined as tags on theg.Meta
property. These metadata includepath
(route address),method
(request method),tags
(API group for generating API documentation), andsummary
(API description). These metadata are part ofOpenAPIv3
, which we won't go into detail here. For those interested, refer to the chapter: API Document - OpenAPIv3. - The
Name
andAge
attributes here are the parameter definitions for our API. Thedc
tag is a shorthand fordescription
, indicating the meaning of the parameter; thev
tag is a shorthand forvalid
, indicating the validation rules for the parameter. We use three built-in validation rules here:required
: The parameter is mandatory.length
: Validates the parameter's length.between
: Validates the parameter's range. Learn about these here, and refer to the section Data Validation - Rules for more validation rules.
- The request parameter structure
CreateReq
does not specify parameter reception methods because theGoFrame
framework supports very flexible parameter reception methods, automatically recognizingQuery String/Form/Json/Xml
submission methods and mapping the submitted parameters to the request parameter receiving objects. - Only the return parameter structures have
json
tags because the returned data usually needs to be converted tojson
format for use by the frontend, and parameter naming insnake
style is more in line with frontend naming conventions.
In a RESTful
style API design, we typically use POST
from the HTTP Method
to denote write operations and PUT
to denote update operations.
Delete
type DeleteReq struct {
g.Meta `path:"/user/{id}" method:"delete" tags:"User" summary:"Delete user"`
Id int64 `v:"required" dc:"user id"`
}
type DeleteRes struct{}
The route tag path
uses /user/{id}
, where {id}
indicates a field-matching route, passed through the URL Path
, with the parameter name id
. As seen, we define an Id
parameter in the request parameter object, and the id
parameter from the route will map directly without case sensitivity to this Id
.
For example: In the route /user/1
, the id
parameter value is 1
; in the route /user/100
, the id
parameter value is 100
.
Update
// Status marks user status.
type Status int
const (
StatusOK Status = 0 // User is OK.
StatusDisabled Status = 1 // User is disabled.
)
type UpdateReq struct {
g.Meta `path:"/user/{id}" method:"put" tags:"User" summary:"Update user"`
Id int64 `v:"required" dc:"user id"`
Name *string `v:"length:3,10" dc:"user name"`
Age *uint `v:"between:18,200" dc:"user age"`
Status *Status `v:"in:0,1" dc:"user status"`
}
type UpdateRes struct{}
Here:
- We define a user status type
Status
, using the conventionalenums
definition style inGolang
. Just for understanding here. - The validation for the
Status
parameter uses thein:0,1
rule, which checks that the passedStatus
value must be one of the two constants we defined,StatusOK/StatusDisabled
, i.e.,0/1
. - The API parameters use pointers to avoid default type values affecting our update API. For example, if
Status
is not defined as a pointer, it will be affected by the default value0
. During processing logic, it's hard to determine whether the caller has passed the parameter and whether to actually change the value to0
. By using pointers, when users don't pass the parameter, its default value isnil
, making it easy to judge in processing logic.
GetOne
type GetOneReq struct {
g.Meta `path:"/user/{id}" method:"get" tags:"User" summary:"Get one user"`
Id int64 `v:"required" dc:"user id"`
}
type GetOneRes struct {
*entity.User `dc:"user"`
}
Here, the return result uses the *entity.User
structure, which was generated by the make dao
command earlier. This data structure corresponds to the database table fields one-to-one.
GetList
type GetListReq struct {
g.Meta `path:"/user" method:"get" tags:"User" summary:"Get users"`
Age *uint `v:"between:18,200" dc:"user age"`
Status *Status `v:"in:0,1" dc:"user age"`
}
type GetListRes struct {
List []*entity.User `json:"list" dc:"user list"`
}
This API can query by Age
and Status
, returning multiple records List []*entity.User
.
Learning Summary
Sample code for this chapter: https://github.com/gogf/quick-demo/blob/main/api/user/v1/user.go
As shown, defining api
APIs in the GoFrame
framework's scaffolding project is quite elegant, with support for automatic data validation, metadata injection, flexible route configuration, and other practical features. This method of API definition allows for the automatic generation of API documentation, where the code serves as documentation, ensuring consistency between code and documentation.
Moreover, this is not the full charm of GoFrame
, just a single petal on the rose. Next, we will use scaffold tools to automatically generate the corresponding controller
control code for us.