Skip to content
Draft
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
7 changes: 7 additions & 0 deletions .changeset/@graphql-hive_gateway-1374-dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@graphql-hive/gateway': patch
---

dependencies updates:

- Added dependency [`uWebSockets.js@uNetworking/uWebSockets.js#v20.52.0` ↗︎](https://www.npmjs.com/package/uWebSockets.js/v/20.52.0) (to `dependencies`)
1 change: 1 addition & 0 deletions packages/gateway/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
"graphql-ws": "^6.0.6",
"graphql-yoga": "^5.15.1",
"tslib": "^2.8.1",
"uWebSockets.js": "uNetworking/uWebSockets.js#v20.52.0",
"ws": "^8.18.3"
},
"devDependencies": {
Expand Down
24 changes: 19 additions & 5 deletions packages/gateway/src/servers/startServerForRuntime.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { GatewayRuntime } from '@graphql-hive/gateway-runtime';
import { MaybePromise } from '@graphql-tools/utils';
import { defaultOptions } from '../cli';
import { startBunServer } from './bun';
import { startNodeHttpServer } from './nodeHttp';
import { ServerForRuntimeOptions } from './types';
import { createUWSStartFn } from './uws';

export function startServerForRuntime<
export async function startServerForRuntime<
TContext extends Record<string, any> = Record<string, any>,
>(
runtime: GatewayRuntime<TContext>,
Expand All @@ -17,7 +17,7 @@ export function startServerForRuntime<
maxHeaderSize = 16_384,
disableWebsockets = false,
}: ServerForRuntimeOptions,
): MaybePromise<void> {
): Promise<void> {
process.on('message', (message) => {
if (message === 'invalidateUnifiedGraph') {
log.info(`Invalidating Supergraph`);
Expand All @@ -34,7 +34,21 @@ export function startServerForRuntime<
...(sslCredentials ? { sslCredentials } : {}),
};

const startServer = globalThis.Bun ? startBunServer : startNodeHttpServer;
let startServer;

return startServer(runtime, serverOpts);
if (globalThis.Bun) {
startServer = startBunServer;
} else {
try {
const uws = await import('uWebSockets.js');
startServer = createUWSStartFn(uws);
} catch (error) {
log.warn(
'uWebSockets.js is not available, falling back to Node.js HTTP server.',
);
startServer = startNodeHttpServer;
}
}

return startServer<TContext>(runtime, serverOpts);
}
81 changes: 81 additions & 0 deletions packages/gateway/src/servers/uws.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import {
GatewayRuntime,
getGraphQLWSOptions,
} from '@graphql-hive/gateway-runtime';
import { Extra } from 'graphql-ws/use/uWebSockets';
import type { TemplatedApp } from 'uWebSockets.js';
import { defaultOptions } from '../cli';
import { ServerForRuntimeOptions } from './types';

export function createUWSStartFn(uws: typeof import('uWebSockets.js')) {
return async function startUwsServer<TContext extends Record<string, any>>(
gwRuntime: GatewayRuntime<TContext>,
opts: ServerForRuntimeOptions,
): Promise<void> {
const {
log,
host = defaultOptions.host,
port = defaultOptions.port,
sslCredentials,
maxHeaderSize,
disableWebsockets,
} = opts;

if (maxHeaderSize) {
process.env['UWS_HTTP_MAX_HEADER_SIZE'] = maxHeaderSize.toString();
}

let app: TemplatedApp;
let protocol: string;

if (sslCredentials) {
protocol = 'https';
app = uws.SSLApp({
key_file_name: sslCredentials.key_file_name,
cert_file_name: sslCredentials.cert_file_name,
ca_file_name: sslCredentials.ca_file_name,
passphrase: sslCredentials.passphrase,
dh_params_file_name: sslCredentials.dh_params_file_name,
ssl_ciphers: sslCredentials.ssl_ciphers,
ssl_prefer_low_memory_usage: sslCredentials.ssl_prefer_low_memory_usage,
});
} else {
protocol = 'http';
app = uws.App();
}

const url = `${protocol}://${host}:${port}`.replace('0.0.0.0', 'localhost');
log.debug(`Starting server on ${url}`);

if (!disableWebsockets) {
log.debug('Setting up WebSocket server');
const { makeBehavior } = await import('graphql-ws/use/uWebSockets');

const wsBehavior = makeBehavior(
getGraphQLWSOptions<TContext, Extra>(gwRuntime, (ctx) => ({
req: ctx.extra?.persistedRequest,
socket: ctx.extra?.socket,
})),
);

app.ws(gwRuntime.graphqlEndpoint, wsBehavior);
}

app.any('/*', gwRuntime);

return new Promise((resolve, reject) => {
app.listen(host, port, (listenSocket) => {
if (listenSocket) {
log.info(`Listening on ${url}`);
} else {
reject(new Error(`Failed to start server on ${url}`));
}
});
gwRuntime.disposableStack.defer(() => {
log.info(`Stopping the server`);
app.close();
resolve();
});
});
};
}
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4181,6 +4181,7 @@ __metadata:
rollup-plugin-tsconfig-paths: "npm:^1.5.2"
tslib: "npm:^2.8.1"
tsx: "npm:4.20.3"
uWebSockets.js: "uNetworking/uWebSockets.js#v20.52.0"
ws: "npm:^8.18.3"
peerDependencies:
graphql: ^15.9.0 || ^16.9.0
Expand Down Expand Up @@ -20875,6 +20876,13 @@ __metadata:
languageName: node
linkType: hard

"uWebSockets.js@uNetworking/uWebSockets.js#v20.52.0":
version: 20.52.0
resolution: "uWebSockets.js@https://github.com/uNetworking/uWebSockets.js.git#commit=cfc9a40d8132a34881813cec3d5f8e3a185b3ce3"
checksum: 10c0/3a852780e99ee16196fd4b027374527f28bdb9509b3d42fcfc85144ef46fa5dd72b6ebf45499f8f0eefadb403e10e6f4605131d0e3992118e2d79d2e769bf68a
languageName: node
linkType: hard

"ufo@npm:^1.6.1":
version: 1.6.1
resolution: "ufo@npm:1.6.1"
Expand Down
Loading