Swagger, now OpenAPI, can describe both JSON and form-encoded request bodies, and it’s surprisingly flexible about how you structure them.

Let’s say you’re building an API endpoint to create a new user. You might want to accept this data as JSON:

{
  "username": "jane.doe",
  "email": "jane.doe@example.com",
  "isActive": true
}

Or, you might want to accept it as form data, especially if it’s coming from a traditional HTML form:

username=jane.doe&email=jane.doe@example.com&isActive=true

OpenAPI can describe both of these scenarios clearly.

Here’s how you’d define the JSON request body in an OpenAPI specification:

paths:
  /users:
    post:
      summary: Create a new user
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                username:
                  type: string
                  example: "jane.doe"
                email:
                  type: string
                  format: email
                  example: "jane.doe@example.com"
                isActive:
                  type: boolean
                  default: true
                  example: true
              required:
                - username
                - email
      responses:
        '201':
          description: User created successfully

The key here is the requestBody object. Inside content, we specify the application/json media type. The schema under that describes the structure of the JSON object itself, detailing its properties, their types, and which ones are mandatory.

Now, if you wanted to support the form-encoded version, you’d add another entry under content:

paths:
  /users:
    post:
      summary: Create a new user
      requestBody:
        required: true
        content:
          application/json: # Existing JSON definition
            schema:
              type: object
              properties:
                username:
                  type: string
                  example: "jane.doe"
                email:
                  type: string
                  format: email
                  example: "jane.doe@example.com"
                isActive:
                  type: boolean
                  default: true
                  example: true
              required:
                - username
                - email
          application/x-www-form-urlencoded: # New form data definition
            schema:
              type: object
              properties:
                username:
                  type: string
                  example: "jane.doe"
                email:
                  type: string
                  format: email
                  example: "jane.doe@example.com"
                isActive:
                  type: boolean
                  default: true
                  example: true
              required:
                - username
                - email
      responses:
        '201':
          description: User created successfully

Notice that for application/x-www-form-urlencoded, the schema is still an object with properties. OpenAPI intelligently understands that for form data, these properties map directly to key-value pairs in the application/x-www-form-urlencoded payload. The required fields also apply to the form data keys.

You can even define multipart/form-data for file uploads or complex form submissions. The structure is similar, but you’ll often use type: string with format: base64 or reference external file schemas.

The real power comes from defining these multiple content types. A client can then inspect the OpenAPI spec and choose the format it prefers or is capable of sending. If an API supports both application/json and application/x-www-form-urlencoded for the same endpoint, the requestBody section will list both under content.

When you’re defining these schemas, remember that example values are incredibly useful for documentation and for clients trying to generate sample requests. They show exactly what a valid payload looks like for each media type.

The choice between JSON and form data often depends on the context of the client. Browser-based forms typically submit application/x-www-form-urlencoded or multipart/form-data. Modern JavaScript clients or server-to-server communication often prefer application/json. By supporting both, you make your API more versatile.

What’s often overlooked is how deeply nested structures can be represented in form data. While simple key-value pairs are common, complex, nested objects can be flattened into a single string using dot notation or similar conventions. OpenAPI’s schema for application/x-www-form-urlencoded can describe this, though the actual encoding convention needs to be understood by both client and server.

Want structured learning?

Take the full Swagger course →