apollo-server-integration-fastify
apollo-server-integration-fastify copied to clipboard
Add support for fastify 5
fastify@5 is released :smiley:.
@as-integrations/fastify peerDependencies include fastify@^4.4.0. Please consider releasing a new version supporting fastify@5.
Thanks for the heads up! Should be able to start on this soon.
Any news about this ?
I'm joining the crowd wishing for a swift resolution to this. Considering there's two open pr's trying to solve the same thing I hope the maintainers push for one of them soon.
I see, should we fork the repo, merge the PR and publish it under another org? It's been a while since I opened the issue
@olyop @trevor-scheer Could you please take a gander at the two prs?
Any update on this? I'm stuck on my Fastify/Nest server updates as well due to this.
Hello! Any update on support for Fastify version 5?
any news on this?
Workaround. Use yarn patch or something similar:
package.json:
"@as-integrations/fastify": "patch:@as-integrations/fastify@npm%3A2.1.1#~/.yarn/patches/@as-integrations-fastify-npm-2.1.1-6c130ea02e.patch",
.yarn/patches/@as-integrations-fastify-npm-2.1.1-6c130ea02e.patch:
diff --git a/build/cjs/plugin.js b/build/cjs/plugin.js
index 9c6d1536b0a72a0bfee7b1cf0481eeb3f726ccf8..a48242f19512288919dbdbc496ec18f3698ea2ca 100644
--- a/build/cjs/plugin.js
+++ b/build/cjs/plugin.js
@@ -5,7 +5,7 @@ const fastify_plugin_1 = require("fastify-plugin");
const handler_js_1 = require("./handler.js");
const utils_js_1 = require("./utils.js");
const pluginMetadata = {
- fastify: "^4.4.0",
+ fastify: "^5.1.0",
name: "@as-integrations/fastify",
};
function fastifyApollo(apollo) {
diff --git a/build/esm/plugin.js b/build/esm/plugin.js
index 6bfb4b052e2fd83e16ce642e800b46b201b8a53a..efd5c85ed40d7ec08ab3326865597e30f372e922 100644
--- a/build/esm/plugin.js
+++ b/build/esm/plugin.js
@@ -2,7 +2,7 @@ import { fastifyPlugin } from "fastify-plugin";
import { fastifyApolloHandler } from "./handler.js";
import { isApolloServerLike } from "./utils.js";
const pluginMetadata = {
- fastify: "^4.4.0",
+ fastify: "^5.1.0",
name: "@as-integrations/fastify",
};
export function fastifyApollo(apollo) {
This integration seems unmaintained and dead. Forking it is likely the solution, but for me this means I'll stay with the official express integration instead.
Until an update is released, here's something I cooked up that might be of use;
import { Readable } from 'node:stream'
import {
type ApolloServer,
HeaderMap,
type HTTPGraphQLRequest,
type HTTPGraphQLResponse,
} from '@apollo/server'
import { type FastifyReply, type FastifyRequest } from 'fastify'
import plugin from 'fastify-plugin'
import { type Context, createContext } from './server/context'
export function fastifyApollo(apollo: ApolloServer<Context>) {
return plugin(
(fastify) => {
fastify.route({
async handler(request, reply) {
const response = await apollo.executeHTTPGraphQLRequest({
context: () => createContext(request, reply),
httpGraphQLRequest: createRequest(request),
})
return handleResponse(reply, response)
},
method: ['get', 'post', 'options'],
url: '/graphql',
})
},
{
fastify: '5.x',
name: 'apollo',
},
)
}
function createRequest(request: FastifyRequest): HTTPGraphQLRequest {
const url = new URL(request.url, `${request.protocol}://${request.hostname}`)
const headers = new HeaderMap()
for (const [key, value] of Object.entries(request.headers)) {
if (value) {
headers.set(key, Array.isArray(value) ? value.join(', ') : value)
}
}
return {
body: request.body,
headers,
method: request.method.toUpperCase(),
search: url.search,
}
}
function handleResponse(
reply: FastifyReply,
{ body, headers, status }: HTTPGraphQLResponse,
) {
for (const [key, value] of headers) {
void reply.header(key, value)
}
void reply.code(status ?? 200)
if (body.kind === 'complete') {
return body.string
}
const readable = Readable.from(body.asyncIterator)
return reply.send(readable)
}
Usage;
await fastify.register(fastifyApollo(apollo))
Most of the code is copied from this package and refactored a bit.
Does anybody know if reply.header and reply.code needs to be awaited?
Hey y'all. Sorry for the silence here, unfortunately priorities have taken my attention elsewhere.
- @olyop would you be open to adding other contributors to this project? I see 2 open PRs for this. We should get them reviewed and released.
- Unless v5 is backward-compatible in the ways we depend on it, we should be bumping the fastify peer deps to
^5and bumping the major version of this package tov3. Ideally we open a newversion-2branch in order to backport fixes to if necessary. - We should drop support for EOL node versions and generally update other dependencies if possible.
I can't commit to helping with this work but I would like to enable you folks to do it.
Hey @trevor-scheer, sorry to prod, but just wanted to check in. Is there anything we can do to help here?
It looks like @olyop is no longer active: https://github.com/olyop?tab=overview&from=2024-12-01&to=2024-12-31. His last activity was in August of last year.
Is anyone available to at least review and accept pull requests? That would be great. Alternatively, bringing in new contributors could help keep this official plugin—mentioned on https://www.apollographql.com/docs/apollo-server—alive.
Thanks for taking action!
Here is the updated version of the plugin (including fastifyApolloDrainPlugin), which is based on the original one, but less overloaded with generic parameters. I've dropped unnecessary generic parameters since they were using defaults anyway and were of little significance.
P.S. Just as the original repo it requires fastify-plugin to be installed.
// --- Types
interface FastifyApolloPluginContext<Context extends BaseContext> {
context?: ApolloFastifyContextFunction<Context>;
}
type ApolloFastifyContextFunctionArgument = [request: FastifyRequest, reply: FastifyReply];
type ApolloFastifyContextFunction<Context extends BaseContext> = ContextFunction<
ApolloFastifyContextFunctionArgument,
Context
>;
type FastifyApolloPluginOptions<Context extends BaseContext> = FastifyRegisterOptions<
FastifyApolloPluginContext<Context>
>;
// --- Plugins
// Inspired by outdated fastify apollo plugin:
// https://github.com/apollo-server-integrations/apollo-server-integration-fastify/blob/main/src/plugin.ts
const fastifyApollo = <Context extends BaseContext = BaseContext>(
apollo: ApolloServer<Context>,
): FastifyPluginAsync => {
const apolloServerProvided = (value: unknown): boolean => value instanceof ApolloServer;
if (!apolloServerProvided(apollo)) throw new Error('Apollo server instance is not provided');
apollo.assertStarted('fastifyApollo()');
// See: https://github.com/fastify/fastify-plugin?tab=readme-ov-file#metadata
const pluginMetadata: PluginMetadata = {
fastify: '^5.2', // This may be different for you based on your installed version of fastify
name: 'fastifyApollo',
};
const fastifyRequestToGraphQLRequest = (request: FastifyRequest): HTTPGraphQLRequest => {
const httpHeadersToMap = (headers: IncomingHttpHeaders): HeaderMap => {
const map = new HeaderMap();
for (const [key, value] of Object.entries(headers)) {
if (value) {
map.set(key, Array.isArray(value) ? value.join(', ') : value);
}
}
return map;
};
return {
body: request.body,
method: request.method.toUpperCase(),
headers: httpHeadersToMap(request.headers),
search: new URL(request.url, `${request.protocol}://${request.hostname}/`).search,
};
};
const fastifyApolloHandler = <Context extends BaseContext>(
apollo: ApolloServer<Context>,
options: FastifyApolloPluginContext<Context>,
): RouteHandlerMethod => {
const defaultContext: ApolloFastifyContextFunction<Context> = () => Promise.resolve({} as Context);
const contextFunction = options?.context ?? defaultContext;
return async (request: FastifyRequest, reply: FastifyReply): Promise<string> => {
const httpGraphQLResponse = await apollo.executeHTTPGraphQLRequest({
httpGraphQLRequest: fastifyRequestToGraphQLRequest(request),
context: () => contextFunction(request, reply),
});
const {headers, body, status} = httpGraphQLResponse;
for (const [headerKey, headerValue] of headers) {
void reply.header(headerKey, headerValue);
}
void reply.code(status === undefined ? 200 : status);
if (body.kind === 'complete') {
return body.string;
}
const readable = Readable.from(body.asyncIterator);
return reply.send(readable);
};
};
return fastifyPlugin(async (fastify: FastifyInstance, options: FastifyApolloPluginContext<Context>): Promise<void> => {
const path = '/graphql';
const method = ['GET', 'POST', 'OPTIONS'];
fastify.route({
method,
url: path,
handler: fastifyApolloHandler<Context>(apollo, options),
});
}, pluginMetadata);
};
// Inspired by outdated fastify apollo drain plugin:
// https://github.com/apollo-server-integrations/apollo-server-integration-fastify/blob/main/src/drain-plugin.ts
const fastifyApolloDrainPlugin = (fastify: FastifyInstance): ApolloServerPlugin => {
return {
async serverWillStart(): Promise<GraphQLServerListener> {
return {
async drainServer(): Promise<void> {
// `closeAllConnections` was added in v18.2 - @types/node are v16.
if ('closeAllConnections' in fastify.server) {
// If fastify.close() takes longer than 10 seconds - run the logic to force close all connections
const timeout = setTimeout(() => {
fastify.server.closeAllConnections();
}, 10_000);
await fastify.close();
clearTimeout(timeout);
}
},
};
},
};
};
// --- Usage
interface MyContext {
// Here I use an auth token as a part of the context, you may have a different context
authToken: string;
}
const extractAuthToken: ApolloFastifyContextFunction<MyContext> = async (request: FastifyRequest) => {
// ... Your logic goes here
};
const app = fastify();
// Apollo server initialization logic, you may have it different
const server = new ApolloServer<MyContext>({
schema,
plugins: [fastifyApolloDrainPlugin(app)],
});
// Spin up the Apollo server
await server.start();
// Register the plugin
await app.register<FastifyApolloPluginOptions<MyContext>>(fastifyApollo(server), {
context: extractAuthToken
});
// Spin up the fastify server
await app.listen({
port: 4000 // ... or whatever your port number is
});
Thanks @vslipchenko, your code works well with some adjustment of the type, just one correction it's critical There is a change in the FastiifyRequest following this PR in Fastify v5
In fastifyRequestToGraphQLRequest it's better to use request.host instead of request.hostname
@trevor-scheer @olyop is there any news please?
I'm also curious for updates here - trying to upgrade to the latest version of Nest.js / Fastify plugins and ran into this issue. Thanks to all the maintainers here!
Prepared temporary fork @nitra/as-integrations-fastify
Tried the temporary fork but the Nest.js GraphQL integration throws an error that @as-integrations/fastify isn't found. I need Fastify 5 for the latest version of Nest.js but can't upgrade my application until @as-integrations/fastify is also upgraded:
ERROR [PackageLoader] The "@as-integrations/fastify" package is missing. Please, make sure to install it to take advantage of GraphQLModule.
I might hack around to make it work instead, but looking forward to an official version bump on the main package.
Hi! Looks like there are three PRs opened to add support for Fastify v5 to this community-maintained package (#297, #299, and #300).
Because Apollo's team does not actively use many different web frameworks, Apollo Server 4 split off the responsibility for maintaining framework-specific integrations to the community. The Fastify integration was originally maintained by @olyop.
If @olyop is no longer interested in maintaining this package (which appears to be the case as these PRs have been open for more than half a year), we'd be happy to add other Fastify users as maintainers of this package on GitHub and npm. We don't possess the Fastify-specific expertise to evaluate these PRs ourselves nor do we have anywhere we could dogfood them. Let us know here if you're interested in maintaining this package!
Hey @glasser I'm happy to contribute to getting this package back on track. I have a lot a open source maintenance experience with large projects
@xzyfer thanks for raising your hand! Would you be open to a DM or email to talk through a few details and hopefully get you set up as a maintainer?
@bignimbus sure thing. I go by the same handle on Gmail, twitter, and blue sky.
For anyone struggling to use one of the forked packages as an interim fix, this is what worked for me. The key was specifying the forked dependency in both the dependencies and overrides sections of package.json.
Without both edits, I was hitting the same error on server startup: The "@as-integrations/fastify" package is missing.
{
"dependencies": {
"@as-integrations/fastify": "npm:[email protected]"
},
[...]
"overrides": {
"@as-integrations/fastify": "npm:[email protected]"
}
}
Any idea when the main package @as-integrations/fastify may see an update? Thanks!
Any news about the PR review/release ?
Update, I've been on-boarded as a contributor to keep this package up with fastify releases. We're just waiting for my npm publishing permissions to get sorted about by the Apollo team.
The open PR has been moved from my fork into the repo at #302. The currently blocker is getting circle ci access. If all else fails the repo can be converted to github actions for CI.
This should be fixed in v3.0.0. A major version bump was required because the fastify v5 required us to raise the minimum Node version to 20, and Typescript version to 5.4.0
Awesome ! Thanks for the update @xzyfer ! :)