# Api
Generates API Routing Map
and type-safe static invoker function from interface
,
without external framework dependencies.
# Usage
# Annotation
Add annotations on interface
and method fields
package x
// +zz:api:[filename][:options...]
type T interface {
// +zz:api:[resource][:options...]
Method(param Param) (result Result, err error)
// method without annotations would be ignored
InternalMethod(param Param) (result Result, err error)
// anonymous field would be ignored
AnonymousT
}
Example:
package x
// +zz:api:./:prefix=book:public
type BookService interface {
// +zz:api:get:{id}
GetBook(id int) (book Book, err error)
}
# Exact Arguments For interface
# filename
Specify generate filename.
Example:
package x
// +zz:api:./
type T interface{}
# Exact Arguments For interface
method
# resource
would be main property in API Routing Map
Example:
package x
// +zz:api:./
type T interface {
// +zz:api:get|detail
Method()
}
# Optional Arguments
You can use any Key-Value
pairs options in this plugin.
They would be collect into API Routing Map
's field options
as a map[string]string
.
options on interface
would try override field
options:
package x
// +zz:api:./:prefix=book:role=read
type BookService interface {
// +zz:api:get
ListBook() (book []Book, err error)
// +zz:api:get|{id}
GetBook(id int) (book Book, err error)
// +zz:api:post|{id}:role=write
NewBook(book Book) (id int, err error)
// +zz:api:put|{id}:role=write
UpdateBook(book Book) (err error)
}
is equal to:
package x
// +zz:api:./
type BookService interface {
// +zz:api:get:prefix=book:role=read
ListBook() (book []Book, err error)
// +zz:api:get|{id}:prefix=book:role=read
GetBook(id int) (book Book, err error)
// +zz:api:post|{id}:prefix=book:role=write
NewBook(book Book) (id int, err error)
// +zz:api:put|{id}:prefix=book:role=write
UpdateBook(book Book) (err error)
}
# Convention
# Invoke Function Generation
Type-safe invoke
static calling functions can be automatically generated for methods that meet the conditions:
- Methods have at most two params and two returns
- If there are two params, the first param type must be
context.Context
, and the second must be different - If there are two returns, the second return type must be
error
, and the first must be different
# Examples
# Example-01
Example Project (opens new window)
/api01/
├── go.mod -> module github.com/go-zing/gozz-doc-examples/api01
└── types.go
package api01
import (
"context"
)
type (
QueryBook struct{}
ListBook struct{}
DataBook struct{}
FormBook struct{}
QueryUser struct{}
ListUser struct{}
DataUser struct{}
FormUser struct{}
)
//go:generate gozz run -p "api" ./
// +zz:api:./:prefix={{ snake .Name }}:id={{ .Name }}.{{ .FieldName }}
type (
BookService interface {
// +zz:api:get
List(ctx context.Context, query QueryBook) (ret ListBook, err error)
// +zz:api:get|{id}
Get(ctx context.Context, query QueryBook) (data DataBook, err error)
// +zz:api:post
Create(ctx context.Context, form FormBook) (data DataBook, err error)
// +zz:api:put|{id}
Edit(ctx context.Context, form FormBook) (data DataBook, err error)
}
UserService interface {
// +zz:api:get
List(ctx context.Context, query QueryUser) (ret ListUser, err error)
// +zz:api:get|{id}
Get(ctx context.Context, query QueryUser) (data DataBook, err error)
// +zz:api:post
Create(ctx context.Context, query FormUser) (data DataBook, err error)
// +zz:api:put|{id}
Edit(ctx context.Context, form FormUser) (data DataBook, err error)
}
)
This Example has two interface
, provide entity basic CRUD
for User
and Book
.
Execute gozz run -p "api" ./
, and it generates file zzgen.api.go
and template file.
// Code generated by gozz:api github.com/go-zing/gozz. DO NOT EDIT.
package api01
import (
"context"
)
var _ = context.Context(nil)
type Apis struct {
BookService BookService
UserService UserService
}
func (s Apis) Iterate(fn func(interface{}, []map[string]interface{})) {
for _, f := range []func() (interface{}, []map[string]interface{}){
s._BookService,
s._UserService,
} {
fn(f())
}
}
func (s Apis) _BookService() (interface{}, []map[string]interface{}) {
t := s.BookService
return &t, []map[string]interface{}{
{
"name": "List",
"resource": "get",
"options": map[string]string{
"id": "BookService.List",
"prefix": "book_service",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in QueryBook
if err := dec(&in); err != nil {
return nil, err
}
return t.List(ctx, in)
},
},
{
"name": "Get",
"resource": "get|{id}",
"options": map[string]string{
"id": "BookService.Get",
"prefix": "book_service",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in QueryBook
if err := dec(&in); err != nil {
return nil, err
}
return t.Get(ctx, in)
},
},
{
"name": "Create",
"resource": "post",
"options": map[string]string{
"id": "BookService.Create",
"prefix": "book_service",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in FormBook
if err := dec(&in); err != nil {
return nil, err
}
return t.Create(ctx, in)
},
},
{
"name": "Edit",
"resource": "put|{id}",
"options": map[string]string{
"id": "BookService.Edit",
"prefix": "book_service",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in FormBook
if err := dec(&in); err != nil {
return nil, err
}
return t.Edit(ctx, in)
},
},
}
}
func (s Apis) _UserService() (interface{}, []map[string]interface{}) {
t := s.UserService
return &t, []map[string]interface{}{
{
"name": "List",
"resource": "get",
"options": map[string]string{
"id": "UserService.List",
"prefix": "user_service",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in QueryUser
if err := dec(&in); err != nil {
return nil, err
}
return t.List(ctx, in)
},
},
{
"name": "Get",
"resource": "get|{id}",
"options": map[string]string{
"id": "UserService.Get",
"prefix": "user_service",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in QueryUser
if err := dec(&in); err != nil {
return nil, err
}
return t.Get(ctx, in)
},
},
{
"name": "Create",
"resource": "post",
"options": map[string]string{
"id": "UserService.Create",
"prefix": "user_service",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in FormUser
if err := dec(&in); err != nil {
return nil, err
}
return t.Create(ctx, in)
},
},
{
"name": "Edit",
"resource": "put|{id}",
"options": map[string]string{
"id": "UserService.Edit",
"prefix": "user_service",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in FormUser
if err := dec(&in); err != nil {
return nil, err
}
return t.Edit(ctx, in)
},
},
}
}
- We could use generated
invoke
function to connect with types of protocol library and do request binding, then invoke the interface method call and get return values in these type-safe static codes. - With help of DI of plugin wire, we could do automatic interface and api binding to provide API services.
- With help of comments mapping table generated by plugin doc,
we could also generate API docs when
interface
and types were designed. - Use optional arguments
options
to do flexibly middlewares and middlewares params management, to do more functions like permission control, or OpenAPI.
# Example-02
Example Project (opens new window)
This example would show wide scope supporting of invoke
static function generating.
package api02
import (
"context"
)
//go:generate gozz run -p "api" ./
// +zz:api:./:path={{ snake .FieldName }}
type (
T interface {
// +zz:api:get
Empty()
// +zz:api:get
Ret() (ret int)
// +zz:api:get
Error() (err error)
// +zz:api:get
RetError() (ret int, err error)
// +zz:api:get
Context(ctx context.Context)
// +zz:api:get
ContextRet(ctx context.Context) (ret int)
// +zz:api:get
ContextError(ctx context.Context) (err error)
// +zz:api:get
ContextRetError(ctx context.Context) (ret int, err error)
// +zz:api:get
Param(param int)
// +zz:api:get
ParamRet(param int) (ret error)
// +zz:api:get
ParamError(param int) (err error)
// +zz:api:get
ParamRetError(param int) (ret int, err error)
// +zz:api:get
ContextParam(ctx context.Context, param int)
// +zz:api:get
ContextParamRet(ctx context.Context, param int) (ret int)
// +zz:api:get
ContextParamError(ctx context.Context, param int) (err error)
// +zz:api:get
ContextParamRetError(ctx context.Context, param int) (ret int, err error)
// +zz:api:get
ComplexParam(param map[context.Context][]struct {
Field []func(context.Context) interface {
context.Context
}
})
// +zz:api:get
PtrParam(*int)
}
)
Execute gozz run -p "api" ./
, and it generates file zzgen.api.go
and template file.
// Code generated by gozz:api github.com/go-zing/gozz. DO NOT EDIT.
package api02
import (
"context"
)
var _ = context.Context(nil)
type Apis struct {
T T
}
func (s Apis) Iterate(fn func(interface{}, []map[string]interface{})) {
for _, f := range []func() (interface{}, []map[string]interface{}){
s._T,
} {
fn(f())
}
}
func (s Apis) _T() (interface{}, []map[string]interface{}) {
t := s.T
return &t, []map[string]interface{}{
{
"name": "Empty",
"resource": "get",
"options": map[string]string{
"path": "empty",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
t.Empty()
return nil, nil
},
},
{
"name": "Ret",
"resource": "get",
"options": map[string]string{
"path": "ret",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
return t.Ret(), nil
},
},
{
"name": "Error",
"resource": "get",
"options": map[string]string{
"path": "error",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
return nil, t.Error()
},
},
{
"name": "RetError",
"resource": "get",
"options": map[string]string{
"path": "ret_error",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
return t.RetError()
},
},
{
"name": "Context",
"resource": "get",
"options": map[string]string{
"path": "context",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
t.Context(ctx)
return nil, nil
},
},
{
"name": "ContextRet",
"resource": "get",
"options": map[string]string{
"path": "context_ret",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
return t.ContextRet(ctx), nil
},
},
{
"name": "ContextError",
"resource": "get",
"options": map[string]string{
"path": "context_error",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
return nil, t.ContextError(ctx)
},
},
{
"name": "ContextRetError",
"resource": "get",
"options": map[string]string{
"path": "context_ret_error",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
return t.ContextRetError(ctx)
},
},
{
"name": "Param",
"resource": "get",
"options": map[string]string{
"path": "param",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in int
if err := dec(&in); err != nil {
return nil, err
}
t.Param(in)
return nil, nil
},
},
{
"name": "ParamRet",
"resource": "get",
"options": map[string]string{
"path": "param_ret",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in int
if err := dec(&in); err != nil {
return nil, err
}
return nil, t.ParamRet(in)
},
},
{
"name": "ParamError",
"resource": "get",
"options": map[string]string{
"path": "param_error",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in int
if err := dec(&in); err != nil {
return nil, err
}
return nil, t.ParamError(in)
},
},
{
"name": "ParamRetError",
"resource": "get",
"options": map[string]string{
"path": "param_ret_error",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in int
if err := dec(&in); err != nil {
return nil, err
}
return t.ParamRetError(in)
},
},
{
"name": "ContextParam",
"resource": "get",
"options": map[string]string{
"path": "context_param",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in int
if err := dec(&in); err != nil {
return nil, err
}
t.ContextParam(ctx, in)
return nil, nil
},
},
{
"name": "ContextParamRet",
"resource": "get",
"options": map[string]string{
"path": "context_param_ret",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in int
if err := dec(&in); err != nil {
return nil, err
}
return t.ContextParamRet(ctx, in), nil
},
},
{
"name": "ContextParamError",
"resource": "get",
"options": map[string]string{
"path": "context_param_error",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in int
if err := dec(&in); err != nil {
return nil, err
}
return nil, t.ContextParamError(ctx, in)
},
},
{
"name": "ContextParamRetError",
"resource": "get",
"options": map[string]string{
"path": "context_param_ret_error",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in int
if err := dec(&in); err != nil {
return nil, err
}
return t.ContextParamRetError(ctx, in)
},
},
{
"name": "ComplexParam",
"resource": "get",
"options": map[string]string{
"path": "complex_param",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in map[context.Context][]struct {
Field []func(context.Context) interface {
context.Context
}
}
if err := dec(&in); err != nil {
return nil, err
}
t.ComplexParam(in)
return nil, nil
},
},
{
"name": "PtrParam",
"resource": "get",
"options": map[string]string{
"path": "ptr_param",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in int
if err := dec(&in); err != nil {
return nil, err
}
t.PtrParam(&in)
return nil, nil
},
},
}
}
← How It Works Doc →