Skip to content

Commit

Permalink
Changes to the incremental responses (#204)
Browse files Browse the repository at this point in the history
Co-authored-by: Martin Nabhan <[email protected]>
  • Loading branch information
jer-k and martinnabhan authored Jul 31, 2024
1 parent 72185b5 commit e7b02be
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 27 deletions.
5 changes: 5 additions & 0 deletions .changeset/witty-pens-nail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@as-integrations/next': minor
---

Add support for incremental delivery
16 changes: 6 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ An Apollo Server integration for use with Next.js.
First create a Next.js API route by creating a file at for example `pages/api/graphql.js`.
This API route will be accessible at `/api/graphql`.

Next create an Apollo Server instance and pass it to `startServerAndCreateNextHandler`:
Next, create an Apollo Server instance and pass it to `startServerAndCreateNextHandler`:

```js
import { ApolloServer } from '@apollo/server';
Expand Down Expand Up @@ -46,15 +46,11 @@ The Next.js `req` and `res` objects are passed along to the context function.

## App Router (Route Handlers)

This integration has experimental support for [Next.js' App Router](https://nextjs.org/docs/app/building-your-application/routing/router-handlers), which is now the stable and default project structure for Next.js.
First create a Next.js Route Handler by creating a file at for example `app/api/graphql/route.js`.
This Route Handler will be accessible at `/api/graphql`.

Make sure you're on recent version of Next.js (13.4+), then create a new Route
Handler file, for example at `app/api/graphql/route.js`.

This file's route handlers will be accessible at URI path `/api/graphql`.

Next create an Apollo Server instance, pass it to `startServerAndCreateNextHandler` and
finally pass the handler to both a GET and a POST route handler:
Next, create an Apollo Server instance, pass it to `startServerAndCreateNextHandler` and
finally export the handler both as GET and POST:

```js
import { startServerAndCreateNextHandler } from '@as-integrations/next';
Expand Down Expand Up @@ -83,7 +79,7 @@ const handler = startServerAndCreateNextHandler(server);
export { handler as GET, handler as POST };
```

## Typescript
## TypeScript

When using this integration with Route Handlers you will have to specify the type of the incoming request object (`Response` or `NextResponse`) for the context function to receive the correct type signature:

Expand Down
1 change: 0 additions & 1 deletion src/__tests__/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ describe('nextHandler', () => {
};
},
{
noIncrementalDelivery: true,
serverIsStartedInBackground: true,
},
);
Expand Down
37 changes: 21 additions & 16 deletions src/startServerAndCreateNextHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { isNextApiRequest } from './lib/isNextApiRequest';
import { ApolloServer, BaseContext, ContextFunction } from '@apollo/server';
import { NextApiRequest, NextApiResponse } from 'next';
import { NextRequest } from 'next/server';
import { Readable } from 'stream';
import { parse } from 'url';

type HandlerRequest = NextApiRequest | NextRequest | Request;
Expand Down Expand Up @@ -49,34 +50,38 @@ function startServerAndCreateNextHandler<

if (httpGraphQLResponse.body.kind === 'complete') {
res.send(httpGraphQLResponse.body.string);
res.end();
} else {
for await (const chunk of httpGraphQLResponse.body.asyncIterator) {
res.write(chunk);
}
res.send(Readable.from(httpGraphQLResponse.body.asyncIterator));
}

res.end();
return;
}

const body = [];

if (httpGraphQLResponse.body.kind === 'complete') {
body.push(httpGraphQLResponse.body.string);
} else {
for await (const chunk of httpGraphQLResponse.body.asyncIterator) {
body.push(chunk);
}
}

const headers: Record<string, string> = {};

for (const [key, value] of httpGraphQLResponse.headers) {
headers[key] = value;
}

// eslint-disable-next-line consistent-return
return new Response(body.join(''), { headers, status: httpGraphQLResponse.status || 200 });
return new Response(
httpGraphQLResponse.body.kind === 'complete'
? httpGraphQLResponse.body.string
: new ReadableStream({
async pull(controller) {
if (httpGraphQLResponse.body.kind === 'chunked') {
const { value, done } = await httpGraphQLResponse.body.asyncIterator.next();

if (done) {
controller.close();
} else {
controller.enqueue(value);
}
}
},
}),
{ headers, status: httpGraphQLResponse.status || 200 },
);
}

return handler;
Expand Down

0 comments on commit e7b02be

Please sign in to comment.