API syntax
[!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR
API IDL example
/**
* api syntax example and syntax description
*/
// api syntax version
syntax = "v1"
// import literal
import "foo.api"
// import group
import (
"bar.api"
"foo/bar.api"
)
info(
author: "anqiansong"
date: "2020-01-08"
desc: "api syntax example and syntax description"
)
// type literal
type Foo{
Foo int `json:"foo"`
}
// type group
type(
Bar{
Bar int `json:"bar"`
}
)
// service block
@server(
jwt: Auth
group: foo
)
service foo-api{
@doc "foo"
@handler foo
post /foo (Foo) returns (Bar)
}
API syntax structure
- syntax statement
- import syntax block
- info syntax block
- type syntax block
- service syntax block
- hidden channel
[!TIP] In the above grammatical structure, grammatically speaking, each grammar block can be declared anywhere in the .api file according to the grammatical block.> But in order to improve reading efficiency, we suggest to declare in the above order, because it may be in the future Strict mode is used to control the order of syntax blocks.
syntax statement
syntax is a newly added grammatical structure, the introduction of the grammar can solve:
- Quickly locate the problematic grammatical structure of the api version
- Syntax analysis for the version
- Prevent the big version upgrade of api syntax from causing backward compatibility
**[!WARNING] The imported api must be consistent with the syntax version of the main api.
Grammar definition
'syntax'={checkVersion(p)}STRING
Grammar description
syntax: Fixed token, marking the beginning of a syntax structure
checkVersion: Custom go method to detect whether STRING
is a legal version number. The current detection logic is that STRING must meet the regularity of (?m)"v[1-9][0-9]"
.
STRING: A string of English double quotes, such as “v1”
An api grammar file can only have 0 or 1 syntax statement. If there is no syntax, the default version is v1
Examples of correct syntax ✅
eg1: Irregular writing
syntax="v1"
eg2: Standard writing (recommended)
syntax = "v2"
Examples of incorrect syntax ❌
eg1:
syntax = "v0"
eg2:
syntax = v1
eg3:
syntax = "V1"
Import syntax block
As the business scale increases, there are more and more structures and services defined in the api. All the grammatical descriptions are in one api file. This is a problem, and it will greatly increase the difficulty of reading and maintenance. Import The grammar block can help us solve this problem. By splitting the api file, different api files are declared according to certain rules, which can reduce the difficulty of reading and maintenance.
**[!WARNING] Import here does not include package declarations like golang, it is just the introduction of a file path. After the final analysis, all the declarations will be gathered into a spec.Spec. You cannot import multiple identical paths, otherwise it will cause parsing errors.
Grammar definition
'import' {checkImportValue(p)}STRING
|'import' '(' ({checkImportValue(p)}STRING)+ ')'
Grammar description
import: fixed token, marking the beginning of an import syntax
checkImportValue: Custom go method to detect whether STRING
is a legal file path. The current detection logic is that STRING must satisfy (?m)"(?[az AZ 0-9_-])+\. api"
regular.
STRING: A string of English double quotes, such as “foo.api”
Examples of correct syntax ✅
eg:
import "foo.api"
import "foo/bar.api"
import(
"bar.api"
"foo/bar/foo.api"
)
Examples of incorrect syntax ❌
eg:
import foo.api
import "foo.txt"
import (
bar.api
bar.api
)
info syntax block
The info grammar block is a grammar body that contains multiple key-value pairs. Its function is equivalent to the description of an api service. The parser will map it to spec.Spec for translation into other languages (golang, java, etc.) Is the meta element that needs to be carried. If it is just a description of the current api, without considering its translation to other languages, you can use simple multi-line comments or java-style documentation comments. For comment descriptions, please refer to the hidden channels below.
**[!WARNING] Duplicate keys cannot be used, each api file can only have 0 or 1 info syntax block
Grammar definition
'info' '(' (ID {checkKeyValue(p)}VALUE)+ ')'
Grammar description
info: fixed token, marking the beginning of an info syntax block
checkKeyValue: Custom go method to check whether VALUE
is a legal value.
VALUE: The value corresponding to the key, which can be any character after a single line except’\r’,’\n’,’’. For multiple lines, please wrap it with “”, but it is strongly recommended that everything be wrapped with “”
Examples of correct syntax ✅
eg1:Irregular writing
info(
foo: foo value
bar:"bar value"
desc:"long long long long
long long text"
)
eg2:Standard writing (recommended)
info(
foo: "foo value"
bar: "bar value"
desc: "long long long long long long text"
)
Examples of incorrect syntax ❌
eg1:No key-value
info()
eg2:Does not contain colon
info(
foo value
)
eg3:key-value does not wrap
info(foo:"value")
eg4:No key
info(
: "value"
)
eg5:Illegal key
info(
12: "value"
)
eg6:Remove the old version of multi-line syntax
info(
foo: >
some text
<
)
type syntax block
In the api service, we need to use a structure (class) as the carrier of the request body and the response body. Therefore, we need to declare some structures to accomplish this. The type syntax block evolved from the type of golang. Of course It also retains some of the characteristics of golang type, and the following golang characteristics are used:
- Keep the built-in data types of golang
bool
,int
,int8
,int16
,int32
,int64
,uint
,uint8
,uint16
,uint32
,uint64
,uintptr
,float32
,float64
,complex64
,complex128
,string
,byte
,rune
, - Compatible with golang struct style declaration
- Keep golang keywords
**[!WARNING]️
- Does not support alias
- Does not support
time.Time
data type- Structure name, field name, cannot be a golang keyword
Grammar definition
Since it is similar to golang, it will not be explained in detail. Please refer to the typeSpec definition in ApiParser.g4 for the specific syntax definition.
Grammar description
Refer to golang writing
Examples of correct syntax ✅
eg1:Irregular writing
type Foo struct{
Id int `path:"id"` // ①
Foo int `json:"foo"`
}
type Bar struct{
// Non-exported field
bar int `form:"bar"`
}
type(
// Non-derived structure
fooBar struct{
FooBar int
}
)
eg2: Standard writing (recommended)
type Foo{
Id int `path:"id"`
Foo int `json:"foo"`
}
type Bar{
Bar int `form:"bar"`
}
type(
FooBar{
FooBar int
}
)
Examples of incorrect syntax ❌
eg
type Gender int // not support
// Non-struct token
type Foo structure{
CreateTime time.Time // Does not support time.Time
}
// golang keyword var
type var{}
type Foo{
// golang keyword interface
Foo interface
}
type Foo{
foo int
// The map key must have the built-in data type of golang
m map[Bar]string
}
[!NOTE] ① The tag definition is the same as the json tag syntax in golang. In addition to the json tag, go-zero also provides some other tags to describe the fields, See the table below for details.
- tag table
tag key Description Provider Effective Coverage Example json Json serialization tag golang request、response json:”fooo”
path Routing path, such as /foo/:id
go-zero request path:”id”
form Mark that the request body is a form (in the POST method) or a query (in the GET method) /search?name=keyword
)go-zero request form:”name”
tag modifier
Common parameter verification description
tag key Description Provider Effective Coverage Example optional Define the current field as an optional parameter go-zero request json:”name,optional”
options Define the enumeration value of the current field, separated by a vertical bar |
go-zero request json:”gender,options=male”
default Define the default value of the current field go-zero request json:”gender,default=male”
range Define the value range of the current field go-zero request json:”age,range=[0:120]”
[!TIP] The tag modifier needs to be separated by a quotation comma after the tag value
service syntax block
The service syntax block is used to define api services, including service name, service metadata, middleware declaration, routing, handler, etc.
**[!WARNING]️
- The service name of the main api and the imported api must be the same, and there must be no ambiguity in the service name.
- The handler name cannot be repeated
- The name of the route (request method + request path) cannot be repeated
- The request body must be declared as a normal (non-pointer) struct, and the response body has been processed for forward compatibility. Please refer to the following description for details
Grammar definition
serviceSpec: atServer? serviceApi;
atServer: '@server' lp='(' kvLit+ rp=')';
serviceApi: {match(p,"service")}serviceToken=ID serviceName lbrace='{' serviceRoute* rbrace='}';
serviceRoute: atDoc? (atServer|atHandler) route;
atDoc: '@doc' lp='('? ((kvLit+)|STRING) rp=')'?;
atHandler: '@handler' ID;
route: {checkHttpMethod(p)}httpMethod=ID path request=body? returnToken=ID? response=replybody?;
body: lp='(' (ID)? rp=')';
replybody: lp='(' dataType? rp=')';
// kv
kvLit: key=ID {checkKeyValue(p)}value=LINE_VALUE;
serviceName: (ID '-'?)+;
path: (('/' (ID ('-' ID)*))|('/:' (ID ('-' ID)?)))+;
Grammar description
serviceSpec: Contains an optional syntax block atServer
and serviceApi
syntax block, which follow the sequence mode (the service must be written in order, otherwise it will be parsed incorrectly)
atServer: Optional syntax block, defining server metadata of the key-value structure,’@server’ indicates the beginning of this server syntax block, which can be used to describe serviceApi or route syntax block, and it has some special keys when it is used to describe different syntax blocks key needs attention,see atServerKey Key Description。
serviceApi: Contains one or more serviceRoute
syntax blocks
serviceRoute: Contains atDoc
, handler and route
in sequence mode
atDoc: Optional syntax block, a key-value description of a route, which will be passed to the spec.Spec structure after parsing. If you don’t care about passing it to spec.Spec, it is recommended to use a single-line comment instead.
handler: It is a description of the handler layer of routing. You can specify the handler name by specifying the handler
key by atServer, or you can directly use the atHandler syntax block to define the handler name
atHandler: @handler
fixed token, followed by a value following the regularity [_a-zA-Z][a-zA-Z_-]
, used to declare a handler name
route: Routing consists of httpMethod
, path
, optional request
, optional response
, and httpMethod
must be lowercase.
body: api request body grammar definition, it must be an optional ID value wrapped by ()
replyBody: api response body grammar definition, must be a struct wrapped by ()、array(Forward compatible processing, it may be discarded in the future, it is strongly recommended to wrap it in struct instead of using array directly as the response body)
kvLit: Same as info key-value
serviceName: There can be multiple’-‘join ID values
path: The api request path must start with /
or /:
, and must not end with /
. The middle can contain ID or multiple ID strings with -
join
atServerKey Key Description
When modifying the service
key | Description | Example |
jwt | Declare that all routes under the current service require jwt authentication, and code containing jwt logic will be automatically generated | jwt: Auth |
group | Declare the current service or routing file group | group: login |
middleware | Declare that the current service needs to open the middleware | middleware: AuthMiddleware |
When modifying the route
key | Description | Example |
handler | Declare a handler | - |
Examples of correct syntax ✅
eg1:Irregular writing
@server(
jwt: Auth
group: foo
middleware: AuthMiddleware
)
service foo-api{
@doc(
summary: foo
)
@server(
handler: foo
)
// Non-exported body
post /foo/:id (foo) returns (bar)
@doc "bar"
@handler bar
post /bar returns ([]int)// Array is not recommended as response body
@handler fooBar
post /foo/bar (Foo) returns // You can omit 'returns'
}
eg2:Standard writing (recommended)
@server(
jwt: Auth
group: foo
middleware: AuthMiddleware
)
service foo-api{
@doc "foo"
@handler foo
post /foo/:id (Foo) returns (Bar)
}
service foo-api{
@handler ping
get /ping
@doc "foo"
@handler bar
post /bar/:id (Foo)
}
Examples of incorrect syntax ❌
// Empty server syntax block is not supported
@server(
)
// Empty service syntax block is not supported
service foo-api{
}
service foo-api{
@doc kkkk // The short version of the doc must be enclosed in English double quotation marks
@handler foo
post /foo
@handler foo // Duplicate handler
post /bar
@handler fooBar
post /bar // Duplicate routing
// @handler and @doc are in the wrong order
@handler someHandler
@doc "some doc"
post /some/path
// handler is missing
post /some/path/:id
@handler reqTest
post /foo/req (*Foo) // Data types other than ordinary structures are not supported as the request body
@handler replyTest
post /foo/reply returns (*Foo) // Does not support data types other than ordinary structures and arrays (forward compatibility, later considered to be discarded) as response bodies
}
Hidden channel
Hidden channels are currently mainly blank symbols, newline symbols and comments. Here we only talk about comments, because blank symbols and newline symbols are currently useless.
Single line comment
Grammar definition
'//' ~[\r\n]*
Grammar description
It can be known from the grammatical definition that single-line comments must start with //
, and the content cannot contain newline characters
Examples of correct syntax ✅
// doc
// comment
Examples of incorrect syntax ❌
// break
line comments
java style documentation comments
Grammar definition
'/*' .*? '*/'
Grammar description
It can be known from the grammar definition that a single line comment must start with any character that starts with /*
and ends with */
.
Examples of correct syntax ✅
/**
* java-style doc
*/
Examples of incorrect syntax ❌
/*
* java-style doc */
*/
Doc&Comment
If you want to get the doc or comment of a certain element, how do you define it?
Doc
We stipulate that the number of lines in the previous grammar block (non-hidden channel content)
line+1 to all comments (the current line, or multiple lines) before the first element of the current grammar block are doc,
And retain the original mark of //
, /*
, */
.
Comment
We specify that a comment block (the current line, or multiple lines) at the beginning of the line where the last element of the current syntax block is located is comment,
And retain the original mark of //
, /*
, */
.
Syntax block Doc and Comment support situation
Syntax block | Parent Syntax Block | Doc | Comment |
syntaxLit | api | ✅ | ✅ |
kvLit | infoSpec | ✅ | ✅ |
importLit | importSpec | ✅ | ✅ |
typeLit | api | ✅ | ❌ |
typeLit | typeBlock | ✅ | ❌ |
field | typeLit | ✅ | ✅ |
key-value | atServer | ✅ | ✅ |
atHandler | serviceRoute | ✅ | ✅ |
route | serviceRoute | ✅ | ✅ |
The following is the writing of doc and comment after the corresponding syntax block is parsed
// syntaxLit doc
syntax = "v1" // syntaxLit commnet
info(
// kvLit doc
author: songmeizi // kvLit comment
)
// typeLit doc
type Foo {}
type(
// typeLit doc
Bar{}
FooBar{
// filed doc
Name int // filed comment
}
)
@server(
/**
* kvLit doc
* Enable jwt authentication
*/
jwt: Auth /**kvLit comment*/
)
service foo-api{
// atHandler doc
@handler foo //atHandler comment
/*
* Route doc
* Post request
* Route path: foo
* Request body: Foo
* Response body: Foo
*/
post /foo (Foo) returns (Foo) // route comment
}