Skip to content

Recipe JSON Response

Muhammet Şafak edited this page May 24, 2026 · 1 revision

Recipe — JSON Response

With the convenience producer

useInitPHP\HTTP\Message\Response;
useInitPHP\HTTP\Emitter\Emitter;
$response = (newResponse())->json(['ok' => true, 'data' => $rows], 200);
(newEmitter())->emit($response);

What this does:

  • json_encode(..., JSON_THROW_ON_ERROR) — unencodable payloads raise InvalidArgumentException, never silently produce false.
  • Content-Type: application/json; charset=utf-8.
  • Replaces the body with a fresh in-memory string-backed Stream carrying the encoded JSON.
  • Sets the status to $status (default 200).
  • Returns a clone — the original Response is untouched.

Pass extra flags as the third argument:

$pretty = (newResponse())->json($data, 200, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

Manual variant — custom Content-Type subtype

For media types like application/vnd.example+json or application/problem+json:

useInitPHP\HTTP\Message\Response;
useInitPHP\HTTP\Message\Stream;
$body = json_encode($data, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
$response = (newResponse(200, [
'Content-Type' => 'application/vnd.example+json; charset=utf-8',
]))->withBody(newStream($body, null));

The null Stream backend is the cheapest option for short bodies — no resource allocation, just a PHP string. See Stream.

RFC 9457 Problem Details

$problem = [
'type' => 'https://example.com/probs/invalid-input',
'title' => 'Invalid input',
'status' => 422,
'detail' => 'Field "email" must be a valid address.',
'instance' => '/users',
];
$response = (newResponse(422, [
'Content-Type' => 'application/problem+json',
]))->withBody(newStream(
json_encode($problem, JSON_THROW_ON_ERROR),
null
));

JSON-streaming large lists

For megabyte-class payloads, prefer streaming over json_encode in one shot — otherwise you're materialising the entire response in memory before the first byte hits the wire.

useInitPHP\HTTP\Message\Response;
useInitPHP\HTTP\Message\Stream;
useInitPHP\HTTP\Emitter\Emitter;
$body = fopen('php://temp', 'w+b');
fwrite($body, '[');
$first = true;
foreach ($pdo->query('SELECT * FROM big_table') as$row) {
if (!$first) fwrite($body, ',');
fwrite($body, json_encode($row, JSON_THROW_ON_ERROR));
$first = false;
}
fwrite($body, ']');
rewind($body);
$response = (newResponse(200, [
'Content-Type' => 'application/json; charset=utf-8',
]))->withBody(newStream($body));
(newEmitter())->emit($response, 65536); // chunked output

CORS preflight + JSON in one handler

usePsr\Http\Message\ServerRequestInterface;
functionjsonHandler(ServerRequestInterface$request): \Psr\Http\Message\ResponseInterface {
$origin = $request->getHeaderLine('Origin') ?: '*';
$cors = [
'Access-Control-Allow-Origin' => $origin,
'Access-Control-Allow-Methods' => 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers' => 'Content-Type, Authorization',
'Vary' => 'Origin',
];
if ($request->getMethod() === 'OPTIONS') {
returnnew \InitPHP\HTTP\Message\Response(204, $cors);
}
return (new \InitPHP\HTTP\Message\Response(200, $cors))
->json(['ok' => true]);
}

See also

Clone this wiki locally