Description
Uri Templates in TypeSpec
Proposal is to use Uri Templates spec to define encoding, optional parameters, and validation of the input and output of the API.
TypeSpec of course does have its own way of defining part of those (optionality, if a param is a path or query param, etc.) so goal is to unify those.
Reserved expensions Spec
Skipping encoding of certain characters can be done by using +
in the param interpolation
@route("{+path}/here") op a(@path path: string): void; // path: /foo/bar -> route: /foo/bar/here
Equivalent to passing allowReserved: true
to @path
or @query
Multiple segments
Multiple segments can be specified with the *
suffix. By default it should be joined with a comma but a different prefix can be used to specify the separator
@route("blobs/{path*}") op a(@path path: string[]): void; // /blobs/foo,bar
@route("blobs{/path*}") op a(@path path: string[]): void; // /blobs/foo/bar
When using *
we should error if the param type is not an array.
The equivalent option would be passing expode: true
(same name as openapi3) to @path
or @query
Other expansions:
The uri template allows you to specify other ways to expand path and query parameters. Part of this proposal is we support uri template fully. So it means we need equivalent config in TypeSpec.
Path expansion
Style | Explode | Uri Template | Primitive value id = 5 | Array id = [3, 4, 5] | Object id = {"role": "admin", "firstName": "Alex"} |
---|---|---|---|---|---|
simple | false | /users/{id} |
/users/5 |
/users/3,4,5 |
/users/role,admin,firstName,Alex |
simple | true | /users/{id*} |
/users/5 |
/users/3,4,5 |
/users/role=admin,firstName=Alex |
label | false | /users/{.id} |
/users/.5 |
/users/.3,4,5 |
/users/.role,admin,firstName,Alex |
label | true | /users/{.id*} |
/users/.5 |
/users/.3.4.5 |
/users/.role=admin.firstName=Alex |
matrix(path) | true | /users/{;id} |
/users/;id=5 |
/users/;id=3,4,5 |
/users/;id=role,admin,firstName,Alex |
matrix(path) | true | /users/{;id*} |
/users/;id=5 |
/users/;id=3;id=4;id=5 |
/users/;role=admin;firstName=Alex |
Query expansion
Style | Explode | Uri Template | Primitive value id = 5 | Array id = [3, 4, 5] | Object id = {"role": "admin", "firstName": "Alex"} |
---|---|---|---|---|---|
simple | false | /users{?id} |
/users?id=5 |
/users?id=3,4,5 |
/users?id=role,admin,firstName,Alex |
simple | true | /users{?id*} |
/users?id=5 |
/users?id=3&id=4&id=5 |
/users?role=admin&firstName=Alex |
Change to the Http library API
Currently each operation as a path: string
property which reference the path. This will remain as it is but a new uriTemplate: string
that represent the exact template uri that should be able to be used to generate the uri given all teh path and query parameters.
Example you should be able to do the following given uriTemplateExpander
is a spec compliant function that takes a uri template and a map of values and returns the uri
uriTemplateExpander(route.uritemplate, {
...pathParametersValues,
...queryParametersValues,
});
Examples
TypeSpec | Uri Template |
---|---|
@route("blobs/{path*}") op a(@path path: string[]): void; |
/blobs/foo,bar |
@route("blobs{/path*}") op a(@path path: string[]): void; |
/blobs/foo/bar |
@route("blobs{/path*}") op a(@path path: string): void; |
/blobs/foo |
Things that uri Template don't cover
In the case of explode: false
when dealing with arrays or object, openapi2 and openapi3 had some additional styles to serialize those:
pipeDelimited
for arrays?foo=bar|baz
spaceDelimited
for arrays?foo=bar baz
In the case of explode: true
for query parameters there is also
deepObject
which is/users?id[role]=admin&id[firstName]=Alex
OpenAPI2 also had things that were removed in openapi3:
tsv
tab separated format
Proposal on that
- Deperecate
format:
on@query
and@header
- Migrate to some
@encode
-op list(@query({format: "ssv"}) id: string[])
+op list(@query @encode(ArrayEncoding.spaceDelimited) id: string[])
@route("blobs{/path*}") op a(@path path: string): void;
@route("blobs") op a(@path({explode: true, style: "path"}) path: string): void;
@route("blobs{/path}") op a(@path({explode: true, style: "path"}) path: string): void;
^ error using both uri template and options
Uri template modifier | Typespec option |
---|---|
* |
explode: true |
+ |
allowReserved: true |
; |
style: "matrix" |
/ |
style: "path" |
. |
style: "label" |
@route("blobs{?filter*}") op a(@query filter: string[]): void;
@route("blobs") op a(@query({explode: true}) filter: string[]): void;
// uriTemplate: blobs{?filter*}