Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes to the incremental responses #204

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
martinnabhan marked this conversation as resolved.
Show resolved Hide resolved
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