1. Overview

In this tutorial, we'll learn how to work with JSON objects as query parameters using OpenAPI.

2. Query Parameters in OpenAPI 2

OpenAPI 2 doesn't support objects as query parameters; only primitive values and arrays of primitives are supported.

Because of that, we'll instead want to define our JSON parameter as a string.

To see this in action, let's define a parameter called params as a string, even though we'll parse it as JSON in our backend:

swagger: "2.0"
...
paths:
  /tickets:
    get:
      tags:
      - "tickets"
      summary: "Send an JSON Object as a query param"
      parameters:
      - name: "params"
        in: "path"
        description: "{\"type\":\"foo\",\"color\":\"green\"}"
        required: true
        type: "string"

Thus, instead of:

GET http://localhost:8080/api/tickets?type=foo&color=green

we'll do:

GET http://localhost:8080/api/tickets?params={"type":"foo","color":"green"}

3. Query Params in OpenAPI 3

OpenAPI 3 introduces support for objects as query parameters.

To specify a JSON parameter, we need to add a content section to our definition that includes the MIME type and schema:

openapi: 3.0.1
...
paths:
  /tickets:
    get:
      tags:
      - tickets
      summary: Send an JSON Object as a query param
      parameters:
      - name: params
        in: query
        description: '{"type":"foo","color":"green"}'
        required: true
        schema:
          type: object
          properties:
            type:
              type: "string"
            color:
              type: "string"

Our request can now look like:

GET http://localhost:8080/api/tickets?params[type]=foo&params[color]=green

And, actually, it can still look like:

GET http://localhost:8080/api/tickets?params={"type":"foo","color":"green"}

The first option allows us to use parameter validations, which will let us know if something is wrong before the request is made.

With the second option, we trade that for greater control on the backend as well as OpenAPI 2 compatibility.

4. URL Encoding

It's important to note that, in making this decision to transport request parameters as a JSON object, we'll want to URL-encode the parameter to ensure safe transport.

So, to send the following URL:

GET /tickets?params={"type":"foo","color":"green"}

We'd actually do:

GET /tickets?params=%7B%22type%22%3A%22foo%22%2C%22color%22%3A%22green%22%7D

5. Limitations

Also, let's keep in mind the limitations of passing a JSON object as a set of query parameters:

  • reduced security
  • limited length of the parameters

For example, the more data we place in a query parameter, the more appears in server logs and the higher the potential for sensitive data exposure.

Also, a single query parameter can be no longer than 2048 characters. Certainly, we can all imagine scenarios where our JSON objects are larger than that. Practically speaking, a URL encoding of our JSON string will actually limit us to about 1000 characters for our payload.

One workaround is to send larger JSON Objects in the body. In this way, we fix both the security issue and the JSON length limitation.

Actually, GET or POST both support this. One reason to send a body over GET is to maintain the RESTful semantics of our API.

Of course, it's a bit unusual and not universally supported. For instance, some JavaScript HTTP libraries don’t allow GET requests to have a request body.

In short, this choice is a trade-off between semantics and universal compatibility.

6. Conclusion

To sum up, in this article we learned how to specify JSON objects as query parameters using OpenAPI. Then, we observed some of the implications on the backend.

The complete OpenAPI definitions for these examples are available over on GitHub.