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

Added whatwg-node-server adapter for grafserv #2288

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

kzlar
Copy link
Contributor

@kzlar kzlar commented Dec 23, 2024

Description

@whatwg-node/server is an adapter that helps create generic servers across runtimes (https://www.npmjs.com/package/@whatwg-node/server). It could, at least in theory, replace all of the servers currently implemented, while supporting several more. If this all works out it should help integrations while having to maintain less code.

Note: This doesn't support web-sockets, I'm still looking for a way to do that in a generic fashion, but I see some of the other adapters don't support web-sockets either so I believe this is good enough for now.

Performance impact

unknown, but this PR won't affect anything by itself, it's just adding the adapter.

Security impact

None

Checklist

  • My code matches the project's code style and yarn lint:fix passes.
  • I've added tests for the new feature, and yarn test passes (Well, sort of - I ran the grafserv tests with it and it passed)
  • I have detailed the new feature in the relevant documentation.
  • I have added this feature to 'Pending' in the RELEASE_NOTES.md file (if one exists).
  • If this is a breaking change I've explained why.

Copy link

changeset-bot bot commented Dec 23, 2024

⚠️ No Changeset found

Latest commit: c2d085f

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Member

@benjie benjie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for starting work on this; it would be great to add @whatwg-node/server as a supported server framework!

Please be sure to format your code; you can run yarn lint:fix in the root. May be worth installing a prettier extension into your editor of choice and configuring it so that it happens automatically on save.

We will also need some tests before this can be merged. Probably makes sense for makeExampleServer() in this file:

https://github.com/graphile/crystal/blob/36c41e80bfe312f584af42545688fb78d3adaf3f/grafast/grafserv/__tests__/graphql-http.test.ts

to accept an argument choosing the server framework to use - then we can test it on each different adaptor easily using the same test suite.

@@ -4,7 +4,7 @@ import type { AddressInfo } from "node:net";
import { constant, error, makeGrafastSchema } from "grafast";
import { resolvePreset } from "graphile-config";

import { grafserv } from "../src/servers/node/index.js";
import { grafserv } from "../src/servers/whtatwg-node-server";
Copy link
Member

@benjie benjie Dec 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo? Should be whatwg?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, fixed

@@ -36,8 +36,7 @@ export async function makeExampleServer(
});

const serv = grafserv({ schema, preset });
const server = createServer();
serv.addTo(server);
const server = createServer(serv.createHandler());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't make this change, it does not handle websockets and other concerns. It means that serv is not passed an instance of server and thus cannot add any event listeners.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this would work, wouldn't that make the WHATWG adapter reliant on node types?
I also see that the lambda adapter does not have an addTo implementation.

For now I changed the makeExampleServer function to receive a type parameter to revert the node behavior to addTo while keeping the different behavior for whatwg. Let me know if that works.

@@ -115,6 +115,7 @@
"@types/koa": "^2.13.8",
"@types/koa-bodyparser": "^4.3.10",
"@whatwg-node/fetch": "^0.9.10",
"@whatwg-node/server": "^0.9.64",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the code, this should also be an optional peer dependency, otherwise createServerAdaptor cannot be imported? (The other adaptors do not require anything but types be imported.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 28 to 29
httpVersionMajor: 1,
httpVersionMinor: 0,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1.0 seems unlikely, that's ancient! Are we not able to determine the HTTP version from a WHATWG server?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I didn't like it either but couldn't find a way to get the HTTP version from a WHATWG Request object. Changed it to 1.1 but still hardcoded unfortunately

return {
httpVersionMajor: 1,
httpVersionMinor: 0,
isSecure: url.protocol === 'https://',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure this is correct? The protocol is actually https:, the // is not part of the protocol itself (see for example location.protocol in a browser).

https://developer.mozilla.org/en-US/docs/Web/API/URL/protocol

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, fixed.

Comment on lines 38 to 42
const text = await request.text()
return {
type: "text",
text,
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we certain the body will always be text?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried changing this based on the other adapters, let me know if this looks better.

};
},
requestContext: {
whatwg: {request}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should include a version number: whatwgv1 or similar.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

requestContext: {
whatwg: {request}
},
preferJSON: true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disappointing, this opts out of some performance enhancements.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm honestly not sure about this. How can I tell if this can be set to false?

case "buffer": {
const { statusCode, headers, buffer } = response;
const respHeaders = new Headers(headers)
return new Response(buffer.toString('utf8'), {status: statusCode, headers:respHeaders})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no guarantee that the buffer can safely be represented as a utf8 string; is it not possible to return binary data directly to the response?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, returning buffer as is.

),
);
})
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also implement an addTo method to follow convention; e.g.:

async addTo(
server: HTTPServer | HTTPSServer,
addExclusiveWebsocketHandler = true,
) {
const handler = this._createHandler();
server.on("request", handler);
this.onRelease(() => {
server.off("request", handler);
});

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See this comment #2288 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: 🌱 In Progress
Development

Successfully merging this pull request may close these issues.

2 participants