The toolkit of Go kit.
- Just write code for your business logic, generate everything else.
- Implement the service once, consume it in various ways (in-process function call or RPC).
-
Code Generation Tool
- Endpoint
- Transport
- HTTP
- HTTP Server
- HTTP Test
- HTTP Client
- OAS-v2 Documentation
- gRPC
- HTTP
-
Useful Packages
- appx: Application framework for HTTP and CRON applications (a wrapper of appx).
- prometheus: Prometheus metrics utilities.
- trace: A thin wrapper of x/net/trace for Go kit.
- werror: Classified business errors.
$ go get -u github.com/RussellLuo/kok/cmd/kokgen
Usage
$ kokgen -h
kokgen [flags] source-file interface-name
-fmt
whether to make code formatted (default true)
-out string
output directory (default ".")
-pkg string
package name (default will infer)
-test string
the YAML file that provides test-cases for HTTP (default "./http.test.yaml")
-trace
whether to enable tracing
NOTE: The following code is located in helloworld.
-
Define the interface
type Service interface { SayHello(ctx context.Context, name string) (message string, err error) }
-
Implement the service
type Greeter struct{} func (g *Greeter) SayHello(ctx context.Context, name string) (string, error) { return "Hello " + name, nil }
-
Add HTTP annotations
type Service interface { // @kok(op): POST /messages SayHello(ctx context.Context, name string) (message string, err error) }
-
Generate the HTTP code
$ cd examples/helloworld $ kokgen ./service.go Service
-
Consume the service
Run the HTTP server:
$ go run cmd/main.go 2020/09/15 18:06:22 transport=HTTP addr=:8080
Consume by HTTPie:
$ http POST :8080/messages name=Tracey HTTP/1.1 200 OK Content-Length: 27 Content-Type: application/json; charset=utf-8 Date: Tue, 15 Sep 2020 10:06:34 GMT { "message": "Hello Tracey" }
-
See the OAS documentation
(Click to show details)
$ http GET :8080/api HTTP/1.1 200 OK Content-Length: 848 Content-Type: text/plain; charset=utf-8 Date: Tue, 15 Sep 2020 10:08:24 GMT swagger: "2.0" info: version: "1.0.0" title: "Swagger Example" description: "" license: name: "MIT" host: "example.com" basePath: "/api" schemes: - "https" consumes: - "application/json" produces: - "application/json" paths: /messages: post: description: "" operationId: "SayHello" parameters: - name: body in: body schema: $ref: "#/definitions/SayHelloRequestBody" produces: - application/json; charset=utf-8 responses: 200: description: "" schema: $ref: "#/definitions/SayHelloResponse" definitions: SayHelloRequestBody: type: object properties: name: type: string SayHelloResponse: type: object properties: message: type: string
See more examples here.
Define the HTTP request operation
-
Key:
@kok(op)
-
Value:
<method> <pattern>
- method: The request method
- pattern: The request URL
- NOTE: All variables (snake-case or camel-case) in pattern will automatically be bound to their corresponding method arguments (matches by name), as path parameters, if the variables are not specified as path parameters explicitly by
@kok(param)
.
- NOTE: All variables (snake-case or camel-case) in pattern will automatically be bound to their corresponding method arguments (matches by name), as path parameters, if the variables are not specified as path parameters explicitly by
-
Example:
type Service interface { // @kok(op): DELETE /users/{id} DeleteUser(ctx context.Context, id int) (err error) } // HTTP request: // $ http DELETE /users/101
Define the HTTP request parameters
- Key:
@kok(param)
- Value:
<argName> < in:<in>,name:<name>,type:<type>,required:<required>
- argName: The name of the method argument.
- Argument aggregation: By specifying the same argName, multiple request parameters (each one is of basic type or repeated basic type) can be aggregated into one method argument (of any type).
- You do not need to repeat the argName, only the first one is required.
- Argument aggregation: By specifying the same argName, multiple request parameters (each one is of basic type or repeated basic type) can be aggregated into one method argument (of any type).
- in:
- path: The method argument is sourced from a path parameter.
- Optional: All variables (snake-case or camel-case) in pattern will automatically be bound to their corresponding method arguments (matches by name), as path parameters.
- query: The method argument is sourced from a query parameter.
- To receive values from a multi-valued query parameter, the method argument can be defined as a slice of basic type.
- header: The method argument is sourced from a header parameter.
- cookie: The method argument is sourced from a cookie parameter.
- Not supported yet.
- body: The method argument is sourced from the request body.
- Deprecated: Use
@kok(body)
instead.
- Deprecated: Use
- request: The method argument is sourced from a property of Go's http.Request.
- This is a special case, and only one property
RemoteAddr
is available now. - Note that parameters located in request have no relationship with OAS.
- This is a special case, and only one property
- path: The method argument is sourced from a path parameter.
- name: The name of the corresponding request parameter.
- Optional: Defaults to argName if not specified.
- type: The type of the corresponding request parameter.
- Optional: Defaults to the type of the method argument, if not specified.
- Required for Argument aggregation for generating correct OAS documentation.
- required: Determines whether this parameter is mandatory.
- Optional: Defaults to
false
, if not specified. - If the parameter location is path, this property will be set to
true
internally, whether it's specified or not.
- Optional: Defaults to
- argName: The name of the method argument.
- Example:
-
Simple argument:
type Service interface { // @kok(op): PUT /users/{id} // @kok(param): name < in:header,name:X-User-Name // @kok(param): age < in:header,name:X-User-Age UpdateUser(ctx context.Context, id int, name string, age int) (err error) } // HTTP request: // $ http PUT /users/101 X-User-Name:tracey X-User-Age:1
-
Argument aggregation:
type User struct { Name string `kok:"query.name"` Age int `kok:"query.age"` Hobbies []string `kok:"query.hobby"` } type Service interface { // @kok(op): POST /users // @kok(param): user < in:query,name:name,type:string // @kok(param): user < in:query,name:age,type:int // @kok(param): user < in:query,name:hobby,type:[]string CreateUser(ctx context.Context, user User) (err error) } // The equivalent annotations. type Service interface { // @kok(op): POST /users // @kok(param): user < in:query,name:name,type:string // @kok(param): < in:query,name:age,type:int // @kok(param): < in:query,name:hobby,type:[]string CreateUser(ctx context.Context, user User) (err error) } // HTTP request: // $ http POST /users?name=tracey&age=1&hobby=music&hobby=sport
-
Define the HTTP request body
- Key:
@kok(body)
- Value:
<field>
- field: The name of the method argument whose value is mapped to the HTTP request body.
- Optional: When omitted, a struct containing all the arguments, which are not located in path/query/header, will automatically be mapped to the HTTP request body.
- The special name
-
can be used, to define that there is no HTTP request body. As a result, every argument, which is not located in path/query/header, will automatically be mapped to one or more query parameters.
- field: The name of the method argument whose value is mapped to the HTTP request body.
- Example:
-
Omitted:
type Service interface { // @kok(op): POST /users CreateUser(ctx context.Context, name string, age int) (err error) } // HTTP request: // $ http POST /users name=tracey age=1
-
Specified as a normal argument:
type User struct { Name string `json:"name"` Age int `json:"age"` } type Service interface { // @kok(op): POST /users // @kok(body): user CreateUser(ctx context.Context, user User) (err error) } // HTTP request: // $ http POST /users name=tracey age=1
-
Specified as
-
:type User struct { Name string `kok:"query.name"` Age int `kok:"query.age"` Hobbies []string `kok:"query.hobby"` } type Service interface { // @kok(op): POST /users // @kok(body): - CreateUser(ctx context.Context, user User) (err error) } // HTTP request: // $ http POST /users?name=tracey&age=1&hobby=music&hobby=sport
-
Define the success HTTP response
-
Key:
@kok(success)
-
Value:
statusCode:<statusCode>,body:<body>
- statusCode: The status code of the success HTTP response.
- Optional: Defaults to 200 if not specified.
- body: The name of the response field whose value is mapped to the HTTP response body.
- Optional: When omitted, a struct containing all the results (except error) will automatically be mapped to the HTTP response body.
- statusCode: The status code of the success HTTP response.
-
Example:
type User struct { Name string `json:"name"` Age int `json:"age"` } type Service interface { // @kok(op): POST /users // @kok(success): statusCode:201,body:user CreateUser(ctx context.Context) (user User, err error) }
See the HTTP Codec interface.
See the OAS Schema interface.
Checkout the Godoc.