# Process a request body

Read the body a client sends and answer from the function, without forwarding the request to an origin. The `Request` object exposes one parsing method per format, and the `Content-Type` header of the request says which one to call. Use this pattern for endpoints that accept a payload: a webhook receiver, a form handler, or an upload endpoint that inspects what it receives before storing it.

This handler uses the ES Modules pattern, which Azion recommends. The rest of this collection is written in the Service Worker pattern.

```js
async function handleBodyAsJSON(request) {
  const data = await request.json();

  data.received = true;
  data.timestamp = new Date().toISOString();

  return new Response(JSON.stringify(data), {
    headers: { 'Content-Type': 'application/json' }
  });
}

async function handleBodyAsFormData(request) {
  const form = await request.formData();

  form.append('new-field-1', '1');

  return new Response(form);
}

async function handleBodyAsText(request) {
  const text = await request.text();

  return new Response(text);
}

async function handleBodyAsBlob(request) {
  const blob = await request.blob();

  return new Response(await blob.text());
}

async function handleBodyAsStream(request) {
  const reader = request.body.getReader();
  const decoder = new TextDecoder('utf-8');
  let body = '';

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    body += decoder.decode(value);
  }

  return new Response(body);
}

export default {
  async fetch(request, env, ctx) {
    if (!request.body) {
      return new Response('No request body was sent');
    }

    const contentType = request.headers.get('Content-Type') || '';

    try {
      if (contentType.includes('application/json')) {
        return await handleBodyAsJSON(request);
      }
      if (contentType.includes('multipart/form-data') || contentType.includes('application/x-www-form-urlencoded')) {
        return await handleBodyAsFormData(request);
      }
      if (contentType.includes('text/plain')) {
        return await handleBodyAsText(request);
      }
      if (contentType.includes('application/octet-stream')) {
        return await handleBodyAsBlob(request);
      }
      return await handleBodyAsStream(request);
    } catch (error) {
      return new Response(`Error processing request: ${error.message}`, { status: 400 });
    }
  }
};
```

## How it works

The `fetch` method checks `request.body` first. A request that carries no body, such as a `GET`, answers with `No request body was sent` and never reaches a parser.

When there is a body, the handler reads the `Content-Type` header and routes to one parsing method. Each method consumes the body once and returns it in the shape that format implies:

| `Content-Type`                                               | Method                     | What the handler does                                                                               |
| ------------------------------------------------------------ | -------------------------- | --------------------------------------------------------------------------------------------------- |
| `application/json`                                           | `request.json()`           | Parses the payload into an object, adds a `received` flag and a `timestamp`, and returns it as JSON |
| `multipart/form-data` or `application/x-www-form-urlencoded` | `request.formData()`       | Reads the fields into a `FormData` object and appends a field before returning it                   |
| `text/plain`                                                 | `request.text()`           | Reads the body as a string and echoes it                                                            |
| `application/octet-stream`                                   | `request.blob()`           | Reads the body as binary and returns its text                                                       |
| Anything else                                                | `request.body.getReader()` | Reads the stream chunk by chunk, decoding each chunk as UTF-8                                       |

The stream branch is the fallback, so an unrecognized content type is still read rather than dropped. It is also the branch to use when the body is large enough that holding the whole payload in memory matters: the reader hands over one chunk at a time, and the loop ends when `done` is true.

A body can be read only once. Calling two parsing methods on the same request throws, which is why the handler picks exactly one branch and returns it.

Every parsing method is awaited inside the `try` block, so a malformed payload is caught rather than escaping the handler. A body that does not match its declared type, such as `{not json` sent as `application/json`, answers with `400` and the parser's own message. Returning the parser's promise without awaiting it would put the rejection outside the `try` and the client would receive a runtime error instead.

## Related resources

- [JavaScript examples](/en/documentation/platform/functions/javascript-examples.md): Browse the rest of the snippets in this collection.
- [Request](/en/documentation/devtools/runtime/api-reference/request.md): The parsing methods this handler calls, and what each one returns.
- [Response](/en/documentation/devtools/runtime/api-reference/response.md): Check the status, headers, and body options the constructor accepts.
- [Migrate handler patterns in Functions](/en/documentation/guides/application-development/functions-and-runtime/migrate-handler-patterns.md): The ES Modules handler this example uses, and its Service Worker equivalent.
- [Functions limits](/en/documentation/platform/functions/limits.md): The request and response body ceilings a single invocation runs inside.
- [Develop and test a function locally](/en/documentation/platform/functions/local-development.md): Run this handler on your own machine and send it a request before deploying it.
