# 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
			},
		},
	}
}
Last Updated: 10/27/2023, 12:58:51 PM