Support RFC6570-style arrays and objects in query and header
Sorting
-
I'm submitting a ...
- [ ] bug report
- [x] feature request
- [ ] support request
-
I confirm that I
- [x] used the search to make sure that a similar issue hasn't already been submit
There are closed issues, none that offer a potential solution afaik.
Expected Behavior
@Route('')
export class TestController {
@Get('test')
public async test(
@Header('tokens') tokens: string[]
): Promise<string> {
return 'Hello, World';
}
}
could automatically generate:
2.0:
name: tokens
in: header
required: true
type: array
items:
type: string
collectionFormat: csv
3.0:
name: tokens
in: header
required: true
schema:
type: array
items:
type: string
style: simple
Current Behavior
It throws:
Error: @Header('inUid') Can't support 'array' type.
in 'TestControllerController.test'
Possible Solution
According to the OpenAPI 3 Spec, it's possible to pass complex data types [array and object] as a standardized format.
Some examples (from the Spec):
Assume a parameter named color has one of the following values:
string -> "blue"
array -> ["blue","black","brown"]
object -> { "R": 100, "G": 200, "B": 150 }
The following table shows 2 examples of rendering different formats that would give support for query, path, (cookie) and header. Explode is false (default setting) for both:
style |
empty |
string |
array |
object |
usable in |
|---|---|---|---|---|---|
| form | color= | color=blue | color=blue,black,brown | color=R,100,G,200,B,150 | query,cookie |
| simple | n/a | blue | blue,black,brown | R,100,G,200,B,150 | path, header |
Swagger 2 afaik only supports schema objects in body. Arrays should be fine, objects without references might work, but I'm not sure.
form should be collectionFormat: csv in OpenAPI 2.0.
simple should be collectionFormat: csv in 2.0
Context
OpenAPI 3.0.2 Parameter Object
Detailed Description
Ideally, we would document and eventually extract arrays and objects from header, query and path strings before passing the param to the ValidationService.
I have not fully fleshed out this idea, but I'd say it's ok to discuss this at least in theory for now.
Breaking change?
Not sure, I'd say since we would not support validating these, this is probably safer to do in the context of a major release.
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days
OpenAPI 3 Serialization spec, notably (for Query) the spaceDelimited and pipeDelimited styles have corresponding Swagger 2 collectionFormats.
Object construction & validation:
To (re)use exisiting validation,
- generate the model (
refObject/refAlias/...) for the Query/Header param type, same way as it would be for body parameters - when the request is received, construct (deserialize) the object from parameters entirely
- then validate it against the model
This would allow tsoa to reuse existing model validation for validating objects in query parameters, but it'd have to be altered to generate validation errors in the relevant format.
One potential problem i see with this is impossibility at the time of object construction to determine whether numerical-indexed path refers to array or numerical object property. I.e. for query deepObject style with parameter id[articles][0][name]=Potatoes, is id.articles an array or an object with numerical properties(?).
This isn't the problem if type information from TS is retained for parameter object deserialization, ...or with the assumption that numerical keys necessarily deserialize into an array.
But then the question arises on how to handle partially received arrays (unless it was already clarified in the OpenAPI spec), i.e. with id[articles][5][topic]
received for parameter id, should tsoa fill id.articles[0..4] with undefined? unshift 6th element to 1st?...
As TSOA currently does with the parsing bodies, deserializing object parameters in query can/should be done by the user with a middleware. This would effectively free TSOA of writing & maintaining deserialization code.
~~Notably, qs is "the go-to" for parsing nested objects withing query strings.~~
Ok, so, express itself uses qs to parse query parameters, meaning that
- with using express (but probably other middleware too)
- discarding TSOA type checks :( with
any -
@Query()parameter can and will receive constructed object, when such is sent - obviously without any validation, as it had to be discarded
Example:
@Get('users/find')
public async findUsers(@Query() filter: any){
console.log(JSON.stringify(foo));
}
Requesting .../users/find?filter[bar][baz]=potato&filter[bar]
Receives, and therefore prints
{"bar":{"baz":"potato","boo":"21"},"nyee":"woooooo"}
So in the end, to [properly] support object query parameters, "the only" thing tsoa should do is generate docs and validation.
I'm not sure if koa/hapi use qs, that's definitely something we should check.
E: https://github.com/koajs/qs
For Queries:
Arrays work fine as they are, for Objects, we should use one style, I'd prefer deepObject aswell, which makes sense with the array format tsoa uses (form).
Right now, you can grab the query off the request and add
"specMerging": "recursive",
"spec": {
"paths": {
"/path/to/endpoint": {
"parameters": [
{
"name": "myDeepObjectQueryParam",
"in": "query",
"style": "deepObject",
"explode": true,
"description": "Accepts an object",
"examples": {
"one": {
"description": "Add a deep object,
"value": { "deep": 1 }
}
},
"schema": {
"type": "object",
"additionalProperties": true
}
}
],
// ...
Not sure if we should apply that format to headers aswell, but seems reasonable.
Hi, Any updates on this?
Hi. Any plans for this? Thanks.