Search code examples
phpswaggeropenapiswagger-codegenndjson

What's the right way to handle chunked json response with swagger and autogenerated php client?


I'm a bit lost between definitions, generated code and many things that are a bit of a black box, even after debugging.

But let's start at the beginning. I have an API written with node using NestJS as framework. NestJS automatically creates the swagger/openapi json file. Using swagger-codegen I create a PHP class to access the API from another server. Works like a charm for simple API request.

Now the problem are API request returning a bigger response, i.e. >1000 rows from one or more DBs. Do make the client not wait a long time and create a big JSON response on the server I've switched to NDJSON, which splits the reponse in chunks of smaller JSON parts, each on it's own line. This also works when I create my request by hand with curl or a HTTP wrapper in PHP using fopen and fread. The response type is application/x-ndjson.

But the code generated by swagger-codegen is always waiting until the whole response has been received. It's even worse, because it fails to decode NDJSON with json_decode() and just returns null. Underneath Guzzle is used, which uses PSR7 streams for the response.

Now I could just skip the autogenerated code for the NDJSON endpoints. But I'd prefer not to add special handling and lose all the useful generated checks.

So is it somehow possible to make swagger-codegen give access to the stream of the response? Am I missing a parameter to codegen or something in the swagger JSON? It does have a produce with application/x-ndjson.


Solution

  • Do answer my own question it is possible, but not easily - meaning there is no option or parameter.

    First create a class that extends the autogenerated API class. There you have access to all of the protected methods. We assume the endpoint or api method is called testMethodGet. The only thing that can be reused is the request method, i.e. testMethodGetRequest, but it does all of the client side validation and transforming of input data, so that's already a big win. Do also get some of the boilerplate result validation you can copy the "http info method", i.e. testMethodGetWithHttpInfo (use async version if you wish). Remove the if/else block after $responseBody = $response->getBody(); and in the return replace the ObjectSearializer line with just $responseBody.

    What you are now getting back is the body as PSR7 stream, but there is one last catch. By default the whole response is drained into a temporary file. To really stream the result body from the server in the created method add an option to $options: $options['stream'] = true; Now the method returns as soon as the body is started. No need to wait for the whole body content.

    One more thing. As the result is a PSR7 stream you can use $stream->detach() to get the PHP stream resource if you prefer to use the normal file and stream methods.