microsoft / microsoft/typespec
[Bug]: query params with nested objects are encoded wrong
- Dominant language
- Java
- Stars
- 5.9k
- Forks
- 394
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 104
Description
### Describe the bug
When a `@query` parameter uses `@query(#{explode: true})` on a model type containing complex objects, the generated JS/C# client produces `[object Object]` in the URL instead of bracket-expanded key-value pairs.
### Reproduction
## TypeSpec
[Playground link](https://typespec.io/playground/?e=%40typespec%2Fhttp-client-js&c=aW1wb3J0ICJAdHlwZXNwZWMvaHR0cCI7CnVzaW5nIFR5cGVTcGVjLkh0dHA7Cgptb2RlbCBDb25kaXRpb24gewogIGZpZWxkOiBzdHJpbmc7CiAgdmFsdWXKEX3IN1NlYXJjaEZpbHRlcsU6aXRlbXM%2FOspQW13LL0l0ZW1Db2xsZWPJa2RhdGE6IHVua25vd8gtQHNlcnZpY2UKQHJvdXRlKCIvYXBpL3YxIikKbmFtZXNwYWNlIEV4YW1wbGXFRckoxX8iKeQAiW50ZXJmxCnEdXPFJyAgQGdldCBsaXN0KEBxdWVyeSgjeyBleHBsb2RlOiB0cnVlIH0pIGblAM8%2FOu0A3ik67wC%2B5AEXfQp9Cg%3D%3D&options=%7B%7D&vs=%7B%7D)
```typespec
import "@typespec/http";
using TypeSpec.Http;
model Condition {
field: string;
value: string;
}
model SearchFilter {
items?: Condition[];
}
model ItemCollection { data: unknown[]; }
@service
@route("/api/v1")
namespace Example {
@route("/items")
interface Items {
@get list(
@query(#{explode: true}) filter?: SearchFilter,
): ItemCollection;
}
}
```
## Steps to reproduce
```bash
npx tsp compile main.tsp
```
Inspect the URI template expansion in the generated client:
```js
import { parse } from "uri-template";
const filter = {
items: [
{ field: "status", value: "active" },
{ field: "type", value: "admin" }
]
};
console.log(parse("/api/v1/items{?filter*}").expand({ filter }));
// Output: /api/v1/items?items=%5Bobject%20Object%5D&items=%5Bobject%20Object%5D
```
## Expected behavior
The same output I would receive by using `qs.stringify`:
```js
import qs from "qs";
const queryString = qs.stringify(
{ filter: { items: [{ field: "status", value: "active" }] } },
{ encode: false }
);
// filter[items][0][field]=status&filter[items][0][value]=active
```
```
GET /api/v1/items?filter[items][0][field]=status&filter[items][0][value]=active&filter[items][1][field]=type&filter[items][1][value]=admin
```
## Actual behavior
```
GET /api/v1/items?items=%5Bobject%20Object%5D&items=%5Bobject%20Object%5D
```
## http-client-csharp
The C# emitter has the same issue. The generated `RestClient` calls:
```csharp
uri.AppendQuery("filter", TypeFormatters.ConvertToString(filter), true);
```
outputs
```
GET /api/v1/items?filter=Example.SearchFilter
```
### Checklist
- [x] Follow our [Code of Conduct](https://github.com/microsoft/typespec/blob/main/CODE_OF_CONDUCT.md)
- [x] Check that there isn't already an issue that request the same bug to avoid creating a duplicate.
- [x] Check that this is a concrete bug. For Q&A open a [GitHub Discussion](https://github.com/Microsoft/typespec/discussions).
- [x] The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of the bug.
Contributor guide
Assessment
This issue has not been assessed yet.