# Request Body Best Practices in REST API Design

# Sending request data

Understanding how to properly structure and handle request data is crucial for building robust APIs. This guide covers best practices and common patterns for working with API request data.

## URL structure

Every API request starts with a URL that identifies the resource and any query parameters:

```http
GET /api/v1/places?lat=40.759211&lon=-73.984638 HTTP/1.1
Host: api.example.org
```

Key components:

- Resource path: Identifies what you're interacting with.
- Query parameters: Filter, sort, or modify the request.
- API version: Often included in the path.

## HTTP request bodies

Requests can also have a "request body", which is a payload of data being sent
the API for processing. It is very frowned upon to use a request body for the
HTTP method `GET`, but expected for `POST`, `PUT`, `PATCH`, and `QUERY`.

The request body can be in a variety of formats, but the most common are JSON,
XML, and form data.

```http
POST /places HTTP/1.1
Host: api.example.org
Content-Type: application/json

{
  "name": "High Wood",
  "lat": 50.464569783,
  "lon": -4.486597585
}
```

This `POST` request to the `/places` endpoint is trying to add a new place to
the API, once again using a JSON body. The `Content-Type` header lets the server
know to expect JSON data, so it can parse the body and create a new place with
the name "High Wood" and the coordinates `50.464569783, -4.486597585`.

So far the examples of HTTP requests and responses have been using text, but in
reality they are just a series of bytes. The text is just a human-readable
representation of the bytes, and the tools that interact with the API will
convert the text to bytes before sending it, and convert the bytes back to text
when receiving it.

Most of you will interact with APIs using a programming language, and the code
to send a request will look something like this:

<CodeWithTabs client:visible
  tabs={[
    {
      label: "main.js",
      language: "typescript",
      code: ` import axios from 'axios';

  const response = await axios.post('https://api.example.org/places', {
    name: 'High Wood',
    lat: 50.464569783,
    lon: -4.486597585,
  });`,
    },
    {
      label: "main.py",
      language: "python",
      code: `  import json

  $headers = [
      'Content-Type' => 'application/json',
  ];
  $payload = [
      'name' => 'High Wood',
      'lat' => 50.464569783,
      'lon' => -4.486597585,
  ];
  $req = $client->post('/places', [
    'headers' => $headers,
    'json' => $payload,
  ]);`,
    },
    {
      label: "main.rb",
      language: "ruby",
      code: `conn = Faraday.new(
    url: 'https://api.example.org',
    headers: { 'Content-Type' => 'application/json' }
  )

  response = conn.post('/places') do |req|
    req.body = {
      name: 'High Wood',
      lat: 50.464569783,
      lon: -4.486597585,
    }.to_json
  end`,
}

]}
/>

HTTP tooling is essentially the same thing no matter the language. It's all
about URLs, methods, body, and headers. This makes REST API design a lot easier,
as you have a "uniform interface" to work with, whether everything is following
all these set conventions already.

Requests like `POST`, `PUT`, and `PATCH` typically include data in their body.

```http
POST /places HTTP/1.1
Host: api.example.org
Content-Type: application/json

{
  "name": "High Wood",
  "lat": 50.464569783,
  "lon": -4.486597585
}
```

An example of a PATCH request following the [RFC 7386: JSON Merge
Patch](https://datatracker.ietf.org/doc/html/rfc7386) format, which accepts just
the fields the user wants to change:

```http
PATCH /places/123 HTTP/1.1
Host: api.example.org
Content-Type: application/merge-patch+json

{
  "name": "Highwood"
}
```

This request body is yours to do with as you want. There's a lot of freedom in how you
structure your data, but there are some best practices to follow which you can
learn more about in the [API Collections & Resources](/api-design
api-collections) guide.

## Data formats

This is generally where all of the domain-specific modelling will happen for
data and workflows the API is meant to handle, but generally its not just
spitting raw data around. The understand more about structuring request bodies,
its important to understand [data formats](/api-design/data-formats).

## File uploads

Instead of sending JSON data, files can be sent in the request body.

Learn more about file uploads in the [File Uploads](/api-design/file-uploads) guide.

## Best Practices

### 1. Keep request & response data consistent

Maintain the same structure for data going in and out of your API. You want to
strive for predictability and consistency in your API design. When a user sends
a `POST` request to create a new resource, they should get back a response that
looks like the resource they just created. If a user updates a resource, the
response should return the new state of the updated resource.

```json
// POST Request
{
  "name": "High Wood",
  "lat": 50.464569783,
  "lon": -4.486597585
}

// Response
{
  "id": 123,
  "name": "High Wood",
  "lat": 50.464569783,
  "lon": -4.486597585,
  "created_at": "2024-10-24T12:00:00Z"
}
```

More on this in the [API Responses](/api-design/responses) guide.

### 2. Do not use request bodies for DELETE requests

Some API designers use a request body for a DELETE request to capture more
information about the deletion of a resource.

Doing so is recommended against in both [RFC9110: HTTP
Semantics](https://www.rfc-editor.org/rfc/rfc9110.html#name-delete) which states
the semantics are undefined, and is warned against further in the OpenAPI
specification. Some tooling will support it some wont.

```http
DELETE /drinks/1 HTTP/1.1
Host: api.example.org
Content-Type: application/json

{
  "reason": "Weird combination of ingredients that proved unpopular with... everyone."
}
```

Instead it would be more appropriate to create a new resource called `POST
/drinks/1/cancellation` and essentially create a DrinkCancellation resource.
This resource can then be used to track the reason for the cancellation, and any
other information that might be useful, as well as avoiding the need for a
request body on the DELETE request.

### 3. Use the correct content type

The `Content-Type` header is used to indicate the media type of the resource
being sent in the request body. The most common content types are:

- `application/json`: JSON data
- `application/xml`: XML data
- `application/x-www-form-urlencoded`: Form data
- `multipart/form-data`: File uploads

Make sure to remind the user to set the `Content-Type` header in their request
to the correct value, and if they send the wrong thing then return a `415
Unsupported Media Type` response.

### 4. Avoid optional request bodies

When designing your API, avoid making request bodies optional. It's a little
confusing, and harder for the user to understand what they need to send, harder
to document, harder to validate, harder to generate code for, and harder to
test.

For example, if you have a `POST /loans/{id}/renewal` endpoint with an optional renewal date, instead of making the whole request body optional make it required and make the renewal date property optional.

```http
POST /loans/123/renewal HTTP/1.1
Host: api.example.org
Content-Type: application/json

{
  "renewal_date": "2024-10-24"
}
```

This way, the user always knows what to expect in the request body, and you can
validate the request body more easily. If the user doesn't send a renewal date,
you can just use the current date as the default value. This is a lot easier to
understand and work with than having to check if the request body is present or
not, and then check if the renewal date is present or not.
