microsoft / microsoft/typespec

[Bug]: query params with nested objects are encoded wrong

Open
#10,973 1 comment 1 reaction 0 assignees View on GitHub
bug emitter:client:all emitter:client:csharp emitter:client:js
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.