Symfony how to stream JSON response with StreamedJsonResponse [Doctrine example included]

Symfony how to stream JSON response with StreamedJsonResponse [Doctrine example included] banner

To stream large JSON responses to the client, you should use the StreamedJsonResponse Response object, which is available in Symfony versions >= 6.3

StreamedJsonResponse expects the first argument to be an array with the JSON structure. To minimize memory usage, generators are recommended, but any PHP Traversable will work.

Here is a simple example with PHP generators.

use Symfony\Component\HttpFoundation\StreamedJsonResponse;

// return generator to save memory
function loadPosts(): \Generator {
     yield ['title' => 'Post 1'];
     yield ['title' => 'Post 2'];
     yield ['title' => 'Post 3'];
};

$response = new StreamedJsonResponse(
    // JSON structure with generators, which will be streamed as a list
    [
        '_embedded' => [
            'posts' => loadPosts(),
        ],
    ],
);

How to use StreamedJsonResponse with Doctrine entities?

You can use the toIterable Doctrine method to fetch rows one by one, this will keep the memory usage low and you'll be able to stream millions of rows to the client. Let's see how we can load posts from the database using Doctrine.

public function loadPosts(): \Generator
{
    // get the $entityManager somehow (e.g. via constructor injection)
    $entityManager = ...

    $queryBuilder = $entityManager->createQueryBuilder();
    $queryBuilder->from(Post::class, 'article');
    $queryBuilder->select('post.id')
        ->addSelect('post.title')
        ->addSelect('post.description');

    $count = 0;
    foreach ($queryBuilder->getQuery()->toIterable() as $post) {
        yield $post;

        if (0 === ++$count % 100) {
            flush();
        }
    }
}

Using flush() the contents will be sent to the browser after each 100th post is fetched.

Do you need to stream other file extensions, not only JSON? Check Symfony StreamedResponse example.