---
title: "Requests and Responses"
description: "Requests and Responses"
type: "docs"
category: "doc"
tags: []
authors: [Anonymous]
date: "2026-10-08"
last_update: "2026-10-08"
time_minutes: 4
draft: false
unlisted: false
url: "https://www.derafu.dev/docs/core/http/requests-responses"
---

# Requests and Responses

`Derafu\Http\Request` and `Derafu\Http\Response` wrap PSR-7 messages and add shortcuts for the things an application does all the time: reading input, negotiating the format and building common responses.

> [!WARNING] They are mutable
> PSR-7 messages are immutable, but these two are not: every `withX()` method modifies the object and returns **the same instance**. Do not rely on keeping the original unchanged after calling one.

## Request

`Request` implements PSR-7's `ServerRequestInterface`, so everything from PSR-7 is available. It is created by `RequestFactoryMiddleware`, which means a handler or any middleware after it receives a `Derafu\Http\Request`.

### Reading input

```php
$id = $request->query('id', 0);              // From the query string.
$name = $request->post('name');              // From the parsed body (form).
$token = $request->header('X-Token', '');    // First value of a header.

if ($request->isJson()) {
    $data = $request->json();                // Decoded array, or null.
}

$all = $request->all();
$email = $request->input('email', '');
$some = $request->only(['name', 'email']);
```

| Method | Returns |
|---|---|
| `query($key, $default)` | A value from the query string |
| `post($key, $default)` | A value from the parsed body, or the default when the body is not an array |
| `header($name, $default)` | The first value of a header, or the default |
| `isJson()` | Whether `Content-Type` contains `application/json` |
| `json()` | The decoded JSON body as an array, or `null` when the request is not JSON, the body is invalid, or it does not decode to an array |
| `all()` | Query, parsed body and JSON body merged, in that order: a later source wins on the same key |
| `input($key, $default)` | A value from `all()` |
| `only($keys)` | The given keys from `all()` |

### Files

```php
if ($request->hasFile('document')) {
    $file = $request->file('document');   // PSR-7 UploadedFileInterface.
}
$files = $request->files();
```

`hasFile()` is `true` only when the file was uploaded without error. `file()` returns `null` when there is no such file.

### Application context

| Method | Returns |
|---|---|
| `route()` | The `RouteMatchInterface` stored by `RouterMiddleware` |
| `session()` | The session, stored by the session middleware |
| `flash()` | The flash messages, stored by the flash middleware |
| `user()` | The authenticated user, or `null` |

`route()`, `session()` and `flash()` throw a `LogicException` when the middleware that provides them did not run before. `user()` never throws.

### Content negotiation

The request decides which format the client wants, and the response, errors included, follow it. `getPreferredContentType()` checks, in this order:

1. The extension in the URL: `/report.json` means JSON, `/page.html` means HTML.
2. Whether it is an API request, that is the path is `/api` or starts with `/api/`: JSON.
3. Whether it is an XHR request (`X-Requested-With: XMLHttpRequest`): JSON.
4. The `Accept` header, ordered by its `q` values. The first of `application/json`, `text/html` or `text/plain` that appears wins.
5. HTML, when there is no `Accept` header or none of those is in it.

`getPreferredFormat()` returns the same as a subtype string (`json`, `html`, ...). `isApiRequest()` and `isXmlHttpRequest()` expose the individual checks.

## Response

```php
use Derafu\Http\Enum\HttpStatus;
use Derafu\Http\Response;

return (new Response())
    ->asJson(['id' => 42])
    ->withHttpStatus(HttpStatus::CREATED);
```

| Method | What it does |
|---|---|
| `asJson($data, $flags)` | JSON body with `Content-Type: application/json`. Throws a `JsonException` if the data can not be encoded. The default flags do not escape slashes or unicode |
| `asText($data, $type)` | Text body; `$type` is a `ContentType`, `ContentType::PLAIN` by default |
| `asHtml($html)` | HTML body |
| `redirect($url, $status)` | `Location` header and empty body. `HttpStatus::FOUND` (302) by default |
| `withHttpStatus($status)` | Sets the status code and reason phrase from an `HttpStatus` |
| `withContentType($type)` | Sets `Content-Type`, adding `charset=UTF-8` to text types |

### Returning data instead of a response

A handler does not have to build a `Response`. If it returns an array, a string or an object, `ResponseNormalizerMiddleware` turns it into one using the preferred content type of the request. See [Middleware](https://www.derafu.dev/docs/core/http/middleware#how-the-response-is-normalized).

```php
public function show(string $id): array
{
    // Sent as JSON to an API client.
    return ['id' => $id];
}
```

Build the `Response` yourself when you need control over the status or the headers.

## Enums

### ContentType

`Derafu\Http\Enum\ContentType` lists the media types the library knows. Besides its `value` (the media type string) it provides:

| Method | Purpose |
|---|---|
| `fromFilename($name)`, `fromExtension($ext)` | Find the type from a file name or extension; `OCTET_STREAM` when unknown |
| `withCharset($charset)` | The media type with `; charset=...` appended |
| `getMainType()`, `getSubType()` | `application` and `json` for `application/json` |
| `isText()`, `isImage()`, `isAudio()`, `isVideo()`, `isFont()` | Group checks |
| `isStatic()` | Whether it is a known file type: everything except `OCTET_STREAM`. It is what `StaticFilesMiddleware` uses |

### HttpStatus

`Derafu\Http\Enum\HttpStatus` has a case for each status code, for example `HttpStatus::NOT_FOUND`. It provides `getReasonPhrase()` and the checks `isInformational()`, `isSuccessful()`, `isRedirection()`, `isClientError()`, `isServerError()` and `isError()`.



---
Last updated on 08/10/2026

