Closed glasser closed 1 year ago
I filed https://github.com/cloudflare/workers-graphql-server/issues/38 in the repo which is the main consumer of this package.
For luck (or not) today i was stating to make a POC using apollo-graphql and workers+durable_objects for our company.
If runs nice i can just open source the setup and keep maintaining. I was not planing to used the workers-graphql-server or the apollo-server-cloudflare anyway because both apear to be unmaintained. I dont know what will be the "glue" code of the V4, but im try to make everything works at V3 here.
I did one small gist with a working Apollo + Workers integration.
https://gist.github.com/Wallacy/4ad0d9b1ac8d36be8f731c60b15945c7
Theres no challenge here. The basic problem was to make the esbuild ignore the node modules on the bundle process. After that the runtime was just a basic setup of the ApolloServerBase and then just basic request to executeOperation handler.
Thats only a small starting point anyway. I will update with something more substation latter.
My team is moving from Google Cloud Functions to Cloudflare Workers (we use it for a bunch of other things), and I'd be happy to help maintain it!
Thank you for calling me:) I'm an author of Hono which is a web framework for Cloudflare Workers, Deno, Bun, and others.
It's exciting to run the GraphQL server at the edge! In fact, there is "GraphQL Server" middleware that is using GprahQL.js for Hono. My funny project, "Ramen API 🍜 " is built by this middleware. See: https://github.com/yusukebe/ramen-api
The great point is that the middleware works on both Cloudflare Workers, Deno, and Bun. it's pretty good.
Although, we've just adapted GraphQL.js as the middleware. So, I think it's not impossible to make "Apollo Sever Middleware". But, if we want it, it will be managed in the repository github.com/honojs/apllo-server
and distributed and @honojs
scope as @honojs/apollo-server
. We will be creating our own middleware, not influenced by you, on our own.
Hey everyone! I'm excited to see the recent interest here. We're ready to start working with folks who want to build and maintain these integration packages. We've written some documentation and provided examples for anyone looking to take ownership of one of these projects.
Given that there's possibly a few of you interested, I'd be very happy to see a repo with more than one collaborator on the project! If you have any questions or difficulties, I'm here to help however I'm able.
How-to docs: https://www.apollographql.com/docs/apollo-server/v4/integrations/building-integrations Demos: https://github.com/apollographql/server-v4-integration-demos/tree/main/packages
-- Trevor
Apollo Server v4.0.0
will be published to latest
soon!
We now have a community GH org where people can create an integration repo as well as an NPM scope for publishing integration packages. If you'd like to be involved over there, feel free to get in touch with me. This is not a requirement for building and publishing an integration, but we wanted to offer a sensible place for these community-maintained packages to live.
https://github.com/apollo-server-integrations https://www.npmjs.com/search?q=%40as-integrations
I wrote an implementation based on the Lambda integration.
It kinda works (see the comments at the top of the Gist for what's needed to make it work, and what doesn't work), but ideally it would require changes in Apollo Server itself to make it work properly. Basically Apollo Server can't always assume it will run in Node.js.
IMHO, the integration with Cloudflare Workers will only be worth doing if and only if Apollo is willing to commit to not assume a Node runtime environment for @apollo/server
.
cc @trevor-scheer
@ronny Do you have any thoughts on how we can test to see if our project is workers-compatible?
Definitely happy to remove the uses of promisify and the import of URLSearchParams, though ideally we'd combine that with CI to ensure we don't make similar issues in the future.
Do you understand why other parts of it don't work after that change? eg is it due to implementing fetch with node-fetch, which it looks like you could replace with the Worker's fetch?
Do you have any thoughts on how we can test to see if our project is workers-compatible?
Definitely happy to remove the uses of promisify and the import of URLSearchParams, though ideally we'd combine that with CI to ensure we don't make similar issues in the future.
My two cents: This can be a good start.
@glasser Thanks for your positive response. Glad that you folks are open to making Apollo work in non-Node.js environments like Cloudflare Workers. I think that would also benefit other communities that use Deno, Bun, and more in the future, which looks like a win-win to me. Seems like everyone (including Node.js) is moving towards supporting Web Platform API as a common base. https://blog.cloudflare.com/standards-compliant-workers-api/
Do you have any thoughts on how we can test to see if our project is workers-compatible? Definitely happy to remove the uses of promisify and the import of URLSearchParams, though ideally we'd combine that with CI to ensure we don't make similar issues in the future.
I was going to suggest the same thing: https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-nodejs-modules.md but @cyco130 beat me to it 😄
Do you understand why other parts of it don't work after that change? eg is it due to implementing fetch with node-fetch, which it looks like you could replace with the Worker's fetch?
Oh, it worked after those two changes, it's just that there are still other things that rely on the node-modules-polyfill
shims (importing os
, zlib
, and so on), which is probably not ideal for production workloads. Ideally we'd like to be able to use Apollo in Workers without having to turn on the node modules polyfill.
I don't think just linting the project itself for avoiding Node APIs is good enough, since we can still break things by using other packages, right?
I'm also not sure that "completely avoid Node APIs" is a feasible approach (especially if we're considering all of our transitive dependencies, not just our own code); "avoid Node APIs that aren't commonly shimmed by Workers/Deno/etc" seems more approachable?
I don't think just linting the project itself for avoiding Node APIs is good enough, since we can still break things by using other packages, right?
It won't be perfect but it's a good starting point, at least you can ensure you're not using those node modules directly.
I'm also not sure that "completely avoid Node APIs" is a feasible approach (especially if we're considering all of our transitive dependencies, not just our own code);
Good point. It may be feasible but it probably won't be easy. It really depends on what the runtime dependencies are I guess.
"avoid Node APIs that aren't commonly shimmed by Workers/Deno/etc" seems more approachable?
Yes, even with node shims, if we can get things to work, then it's a good first step IMHO. We can address any issues on a case-by-case basis as they arise.
I'm not sure it's a great starting point; adding or updating a dependency on our end could quietly break any integrations that expect AS to be avoiding specific Node APIs (the ones that aren't polyfilled). Maybe we can run a smoke test in our CI, or some kind of test that mocks all forbidden Node APIs and throws if they're imported? I want to believe there's probably some prior art for this 😄
I want to believe there's probably some prior art for this
Trying to bundle with ESBuild without any externals (or only a few) might be a way. It's very fast and very similar to what happens in the end.
@glasser @trevor-scheer I would be interested as a cloudflare integration
maintainer as well.
The following is mainly Apollo Federation on Cloudflare Workers related, but I thought I make it part of this comment anyways.
I was recently wondering if I could run Apollo Federation v2 on Cloudflare workers, and after hitting a bunch of road blocks I finally got it running. thank you @ronny for the gist, which helped a lot!
For some background, as mentioned above, Cloudflare implements a selection of the WHATWG web api, including the global WHATWG URL. Cloudflare wrangler (the cli tool for building and deploying to Cloudflare) shims/polyfills some node.js modules with esbuild polyfills, which are based on a rollup polyfill package, which is essentially a bundle of some (more or less maintained and unmaintained/outdated) browserify polyfills. The main intend was to make it possible to use modules intended for the node.js runtime in the browser.
Some of those polyfills are more or less fully working (e.g. assert
, url
and others), while others are not implemented or just empty mocks, as it's not possible to polyfill with the [current] browser api (e.g. fs
, http
).
The reason import { URL } from 'url'
is not working is because the url polyfill unfortunately does not include the WHATWG URL, although it could easily be re-exported from the global (if available). The same is true for import { promisify } from 'util'
, it's just missing in the 'util' polyfill.
I believe the global URL is already being used in other @apollo packages and promisify
in node.js is also just a wrapper around the Promise constructor.
The biggest hurdle I ran into was indeed fetch
related, with the default make-fetch-happen
used by @apollo/gateway including transitive dependencies adding another whopping 300kb or more (compressed, if I remember correctly) to the bundle. Unfortunately it turnd out that make-fetch-happen
is not easy to support in Cloudflare Workers, as it uses unsupported node.js built-ins (e.g. http
) under the hood. I had to swap out the import for make-fetch-happen
with node-fetch
, which still would not be enough to work on it's own (as it also uses the http
module as well), but at least it would make it possible to create a bundle.
Another fetch
related problem I ran into is that Cloudflare does not support fetching other workers on the same sub domain, which would render Federation on Cloudflare Workers pretty much useless (or at least would make it heavily crippled). Luckily with the fairly recent introduction of service bindings I was able to write a datasource overriding the fetch(er) implementation of the remote datasource by using service bindings with fetch. That in turn also got rid of the node-fetch
problem mentioned above, which even though it was bundled, was being ignored and unused.
I would be great if Apollo would use the global WHATWG fetch
as well, possibly with a fallback to node-fetch
or similar (even make-fetch-happen
if desired). Since dynamic imports are now available in all supported node.js runtimes, this should be fairly straight forward to implement (assuming all fetch
use cases are already in an async path).
All the above was made easier with the help of the ingenious patch-package. other than the implementation of the integration and the service bindings datasource there wasn't overall much monkey patching needed to make it work. granted, more work is potentially needed for full fletched support (e.g. tracing etc.)
I'm intending to create a repository (or possibly multiple?) for a Cloudflare Worker integration based on @ronny 's gist, a Cloudflare Worker bootstrap project using @apollo/server and the integration, a Cloudflare specific datasource for using service bindings, as well as a bootstrap project repository for Apollo Federation (with a gateway and some subgraphs) using the integration and the datasource.
We could use those to iterate on what changes are needed to support @apollo/server integration as well as to support Federation with @apollo/gateway and @apollo/subgraph.
Hi @glasser and @trevor-scheer !
First of all, Congratulations on new Apollo Server v4 release!
I see many users of Apollo Server asking for support on different JS environments such as CF Workers, Deno or any other. As @TheGuildDev, we created this library(https://github.com/ardatan/whatwg-node/tree/master/packages/server) that helps you create platform agnostic server implementations and it uses Fetch API for abstraction instead of Node.js specific API.
By using this library, you don't need to think of handling different server APIs of these different environments.
I guess if you use this library/approach in Apollo Server's core, you can provide support for CF Workers, Deno and maybe Bun out-of-box without any extra work. So basically, you won't need specific packages for each environment like apollo-server-cloudflare
, apollo-server-deno
or apollo-server-bun
You can see the repo and the blog post. We'd love to help you if you also want to try this approach :)
https://the-guild.dev/blog/fetch-for-servers
I think this addresses most of the points @dnalborczyk already mentioned above.
@ardatan That is attractive, although Apollo Server 4 already has created its own abstraction for HTTP messages, so I don't think we'd want to immediately rework it to be built on top of this (in core). We also do want to ensure that the core doesn't have unnecessary layers of abstraction for performance reasons. But it does seem like perhaps this approach could be used to build an integration that supports all the environments you support.
That said it doesn't feel like the challenge here is "connecting the CloudFlare server API to the Apollo Server API" since the AS API is quite simple and this only takes half a page of code... the challenge is ensuring that Apollo Server core doesn't rely on any APIs that are unavailable in environments like CF/Deno/Bun. I'm not sure that your project helps with that at all? Unless it has some sort of automated test infrastructure set up to help validate those environments?
although Apollo Server 4 already has created its own abstraction for HTTP messages
This is the problem in most server implementations. There is already available abstraction there used by most of JS platforms already which is Fetch API. I am not sure if you read the blog post but this is the point we are trying to explain in that blog post. If everyone tries to implement their abstraction, this ends up flavour packages like apollo-server-bun
, apollo-server-deno
, apollo-server-foo
or whatever. Bun, Deno, Netlify Edge, Sveltekit and many more JS environments and frameworks today are using Fetch API today as HTTP abstraction and Node started to follow that path as we all see.
@whatwg-node/server
and @whatwg-node/fetch
try to fill the gap untill all JS environments are aligned which is not a far future apparently.
That said it doesn't feel like the challenge here is "connecting the CloudFlare server API to the Apollo Server API" since the AS API is quite simple and this only takes half a page of code... I'm not sure that your project helps with that at all? Unless it has some sort of automated test infrastructure set up to help validate those environments?
It can be just one line as you can see from the examples :) You can check the source of @whatwg-node/server
and how we use it in Yoga. It allows us to support many environments without adding specific extensions for specific platforms.
https://the-guild.dev/graphql/yoga-server/docs/integrations/z-other-environments
So as you can see it is really easy to use Fetch API if it is not used by the server handling logic of an environment (AWS Lamdba e.g.) without introducing a new apollo-server-lambda
or something else.
Also another note;
Fetch API is missing only on Node 14 and Node 16 and that's why we introduced @whatwg-node/fetch
that ponyfills missing parts in specific versions of Node. And it will be retired after Node 16's EOL.
All other JS environments have Fetch API and use it as the server abstraction as we have seen so far.
Unless it has some sort of automated test infrastructure set up to help validate those environments? We do have unit, integration and E2E tests on both Yoga and whatwg-node repo, and those are part of our GitHub Actions CI.
https://github.com/ardatan/whatwg-node/tree/master/.github/workflows https://github.com/dotansimha/graphql-yoga/tree/main/e2e
Any way we can develop our own middleware for our own specific framework or any kind of seamless integration exists?
@ardatan It does sound like an integration between AS4 and @whatwg-node/server
would be an easy way to get AS4 supported on many platforms (though again, not CloudFlare if we don't fix other issues about the JS runtime). I don't think we're going to change the actual Apollo Server integration API which we just announced as stable to be based on it though!
@apoorvcodes I'm not sure exactly what you mean, but yes, it's relatively straightforward to build integrations against AS4: https://www.apollographql.com/docs/apollo-server/integrations/building-integrations
@ardatan it seems like a great idea to use the fetch api for framework integrations, which Cloudflare Workers and Deno are using as well. I hope node.js will use the fetch api on the server side eventually as well.
some additional context regarding the usage of the fetch API and the compromises in apollo server v4: https://github.com/wintercg/fetch-standard/issues/2
@dnalborczyk That's why we built @whatwg-node/server
which basically allows you to use Fetch API for server side.
https://the-guild.dev/blog/fetch-for-servers
We think it is unnecessary to build an abstraction layer(which we believe it adds complexity) while there is already Fetch API which fits for most needs.
So we can basically adapt Apollo Server's abstraction to Fetch API but this just defeats the goal of using a common abstraction(Fetch API).
For us, server libraries should not deal with the abstraction. So it will simplify things in the server library's code.
Instead of introducing MyHeadersInterface
, MyRequestInterface
as in Apollo today, we can just use Headers
and Request
.
We think it is unnecessary to build an abstraction layer(which we believe it adds complexity) while there is already Fetch API which fits for most needs.
I agree. I guess the problem is that the (client side) fetch
API was just added to node v18, and I assume it's unlikely being backported to older versions of node.js. on the bright side, node.js v14 and v16 will reach end-of-life fairly quick: https://github.com/nodejs/Release node-fetch
is not fully standard compliant and more light weight, make-fetch-happen
never strived to be compliant, and appears to be an all-batteries-included fetch implementation (including cache and retries), and undici
is the upstream project of node's built-in, unfortunately requiring node.js v16+ (last time I checked). in short, all 3 packages unfortunately don't serve as great polyfills, although node-fetch
is probably good enough for most use cases.
@dnalborczyk
That's why, we have @whatwg-node/fetch
that uses undici
if possible or patched version of node-fetch
.
https://github.com/ardatan/whatwg-node/tree/master/packages/fetch
You can also check the examples and our tests to see how it works very well on server side on Node 14+.
We use it in the projects like GraphQL Yoga, GraphQL Mesh and SOFA.
@glasser @trevor-scheer
Cloudflare Workers integration module, based on @ronny 's gist https://github.com/dnalborczyk/apollo-server-integrations-cloudflare-workers
Apollo Server v4 with Cloudflare Workers example project, currently required patches in the same name folder https://github.com/dnalborczyk/apollo-server-cloudflare-workers
Cloudflare Workers GraphQL datasource module
https://github.com/dnalborczyk/cloudflare-workers-graphql-datasource
extends from RemoteGraphQLDataSource
, uses Service Bindings (fetch-look-alike) to communicate with subgraphs, as Cloudflare Workers do not support fetch from same sub-domains.
Apollo Federation v2 with Cloudflare Workers example project, using Apollo Server v4, Apollo Gateway v2, Apollo Subgraph v2, the Cloudflare Workers integration package, as well as the Cloudflare Workers GraphQL datasource. currently required patches in the same name folder https://github.com/dnalborczyk/apollo-federation-cloudflare-workers
@dnalborczyk If I have this correct, if we merge and release #7229 and #7228, then that means AS will work reasonably well on CloudFlare workers?
Are you interested in moving your integration package into the as-integrations
npm org and/or apollo-server-integrations
GH org?
Also once this is all happy, you can update docs/source/integrations/integration-index.mdx
to point to your integration.
@dnalborczyk v4.3.0 has your two PRs!
If I have this correct, if we merge and release #7229 and #7228, then that means AS will work reasonably well on CloudFlare workers?
v4.3.0 has your two PRs!
yes, it works great, already applied, removed patches, and tested, thank you!
Are you interested in moving your integration package into the
as-integrations
npm org and/orapollo-server-integrations
GH org?
yes, definitey! follow up from: https://github.com/apollographql/apollo-server/pull/7229#issuecomment-1355345067
we can move it right away or apply the couple non-breaking changes + missing tests first?
Also once this is all happy, you can update
docs/source/integrations/integration-index.mdx
to point to your integration.
will do!
@glasser Hello, I built an integration for Cloudflare Workers.
Could I transfer it into the organization @as-integrations/cloudflare-workers
?
Hey @kimyvgy, sorry for the delay - just sent you an invite to the GH organization. Is kimyvgy
your NPM handle as well?
@trevor-scheer that's a bit weird, are we just ignoring everything mentioned in this thread?
to me it feels a bit disrespectful from @kimyvgy to just ignore everything and go ahead with their request with what is essentially pretty much copy/paste.
startServerAndCreateCloudflareHandler
is also not returning a function and starts startInBackgroundHandlingStartupErrorsByLoggingAndFailingAllRequests
on each request. (nm, that seems to have been fixed).
the KV store related code should also not be part of the integration. I assume the same with cors.
@dnalborczyk I’m sorry for my mistake. I didn’t read the discussion above. My integration is made since I created the issue https://github.com/kimyvgy/worker-apollo-server-template/issues/9#issue-1462851938 when upgrading the boilerplate to Apollo Server v4 (before this thread starts). I was busy with my full time job, so the integration have been published in the last week.
I commented because I just want to share my work for people. Please go ahead with your integration then moving it to the official organization of Apollo Server.
when will the integration be available at https://www.apollographql.com/docs/apollo-server/integrations/integration-index/ ?
I don't see any reply from @dnalborczyk since my last mention. I hope to solve this issue as soon as possible. Therefore, I decide to publish an new integration https://github.com/apollo-server-integrations/apollo-server-integration-cloudflare-workers. It will be available in NPM soon. Please feel free to open PR for it.
@dnalborczyk apologies, I agree with you and that's my mistake for not taking in the context of the thread.
@kimyvgy it sounds like you agreed. If you're in a rush, why don't you keep your repo and publish under your own user? There's no requirement that it needs to be in the @as-integrations namespace. Most people are away from their work right now with the holidays so I expect we'll hear from them in a few days.
Hey @dnalborczyk, any update here? The repo / package name are yours if you're still interested. If not please let us know so we can move forward.
I'm content with my efforts and that we've given @dnalborczyk ample time to respond. @kimyvgy if you're still interested in publishing under the namespace just let me know.
@trevor-scheer I got it. Could you invite me (username: kimyvgy
) to NPM organization as-integrations
? After that, I can publish the repo under the namespace and release first version.
@kimyvgy invite sent 👍
@trevor-scheer @as-integrations/cloudflare-workers
v0.1.0 released. I'm going to adding some tests for it in the next release.
Now, we can see it in NPM: https://www.npmjs.com/package/@as-integrations/cloudflare-workers. Try the live demo: https://worker-apollo-server.webee-asia.workers.dev/
Closing this issue out. Further discussion and development on the cloudflare integration should happen here: https://github.com/apollo-server-integrations/apollo-server-integration-cloudflare-workers
I also spun out #7369 to capture the issue that was mentioned further up in the thread about preventing changes in apollo server that would affect packages such as this one.
I'm exciting to see this completed!
I just want to reiterate something that Trevor has expressed earlier. Part of the point of pulling framework integrations out of core and into the community is that in many cases there can be multiple valid ways to choose to integrate AS with a framework, and we don't need to "choose a winner". So while we have given @kimygvy package names within the community integration orgs on GH and npm, that doesn't mean that @dnalborczyk's work is invalid or useless. If your packages differ to the degree that different users may choose one vs the other, then by all means continue moving forward with it! We can also provide links to multiple alternatives in our docs, or even have multiple alternatives within the GH/npm orgs as long as suitable naming can be provided to help folks understand the difference.
As described in the roadmap, Apollo Server 4 will shift from the model of "9 web framework integrations maintained by a team who only uses one of them and nope, you can't create your own" to "a stable web framework integration API that anyone can use for their favorite web framework".
As part of this, the Apollo Server core team is no longer planning to maintain
apollo-server-cloudflare
. We think users of Cloudflare Workers are best served if their framework integration is maintained by a team of people who use Cloudflare Workers every day.This is where you come in! Do you love Cloudflare Workers? Do you use it with Apollo Server? Would you like to be part of a team of community members who maintain
apollo-server-cloudflare
?If so, chime in below! We'd love to find a group of people who will maintain the Apollo Server 4 version of
apollo-server-cloudflare
, and to help us maintain the section of the Apollo Server docs that shows how to use Apollo Server with Cloudflare Workers.We expect that maintaining this package will be more straightforward than maintaining an Apollo Server integration today, because its only responsibility will be translating Cloudflare Workers' HTTP request and response objects to the formats used by Apollo Server's new API.
At the time that this issue is opened, we are actively looking for interested collaborators; however, the API you'll be implementing isn't yet ready. We can use this issue to get a team together who can get started at some point between an initial alpha release of the new
@apollo/server
package and the final v4.0.0 release.Some Cloudflare Workers-specific notes:
apollo-server-cloudflare
is currently marked as experimental and not supported. To be honest, it's more of a prototype that we put together 3+ years ago and hasn't really been actively maintained in any way. It is still a dependency of theworkers-graphql-server
quickstart repository. My guess is that the best path forward would be to just write the relevant integration code directly in that repository, but maybe people more familiar with Cloudflare Workers would have a better idea.