Skip to content

Commit 6a8c692

Browse files
committed
refactor: add custom agent to graphql client (#3603)
refactor: add custom agent to graphql client
1 parent 83ba1a7 commit 6a8c692

File tree

14 files changed

+142
-26
lines changed

14 files changed

+142
-26
lines changed

apps/web/app/components/subscriptions.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type {
55
DomainUpdatedSubscription,
66
} from '@codelab/shared-domain-module/domain'
77

8-
import { browserApolloClient } from '@codelab/shared/infra/gql-client'
8+
import { browserApolloClient } from '@codelab/frontend/infra/gql-client'
99
import {
1010
DomainCreatedDocument,
1111
DomainUpdatedDocument,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"extends": ["../../../../.eslintrc.json"],
3+
"ignorePatterns": ["!**/*"],
4+
"overrides": [
5+
{
6+
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7+
"parserOptions": {
8+
"project": ["libs/frontend/infra/gql-client/tsconfig.*?.json"]
9+
},
10+
"rules": {}
11+
},
12+
{
13+
"files": ["*.ts", "*.tsx"],
14+
"rules": {}
15+
},
16+
{
17+
"files": ["*.js", "*.jsx"],
18+
"rules": {}
19+
}
20+
]
21+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# frontend-infra-gql-client
2+
3+
This library was generated with [Nx](https://nx.dev).
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "frontend-infra-gql-client",
3+
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
4+
"sourceRoot": "libs/frontend/infra/gql-client/src",
5+
"projectType": "library",
6+
"tags": [
7+
"projectName:frontend-infra-gql-client",
8+
"projectType:library",
9+
"type:concrete",
10+
"layer:infra",
11+
"scope:frontend"
12+
],
13+
"// targets": "to see all targets run: nx show project frontend-infra-gql-client --web",
14+
"targets": {}
15+
}

libs/shared/infra/gql-client/src/apollo.client.ts renamed to libs/frontend/infra/gql-client/src/apollo.client.ts

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,8 @@ import { GraphQLWsLink } from '@apollo/client/link/subscriptions'
33
import { getMainDefinition } from '@apollo/client/utilities'
44
import { env } from '@codelab/shared/config/env'
55
import { createClient } from 'graphql-ws'
6-
import WebSocket from 'ws'
76

8-
enum Environment {
9-
Browser = 'browser',
10-
Node = 'node',
11-
}
12-
13-
interface CreateApolloClientOptions {
14-
environment?: Environment
15-
}
16-
17-
export const createApolloClient = ({
18-
environment = Environment.Browser,
19-
}: CreateApolloClientOptions = {}) => {
7+
export const createApolloClient = () => {
208
// Move port getter to a function to avoid early evaluation
219
const getApiPort = () =>
2210
env.get('NEXT_PUBLIC_API_PORT').required().asPortNumber()
@@ -28,7 +16,6 @@ export const createApolloClient = ({
2816
const wsLink = new GraphQLWsLink(
2917
createClient({
3018
url: `ws://127.0.0.1:${getApiPort()}/api/v1/graphql`,
31-
webSocketImpl: environment === Environment.Node ? WebSocket : undefined,
3219
}),
3320
)
3421

@@ -54,12 +41,4 @@ export const createApolloClient = ({
5441
/**
5542
* Lazy load so we create the client when the environment is known
5643
*/
57-
export const nodeApolloClient = () =>
58-
createApolloClient({
59-
environment: Environment.Node,
60-
})
61-
62-
export const browserApolloClient = () =>
63-
createApolloClient({
64-
environment: Environment.Browser,
65-
})
44+
export const browserApolloClient = () => createApolloClient()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './apollo.client'
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"extends": "../../../../tsconfig.base.json",
3+
"compilerOptions": {
4+
"module": "commonjs",
5+
"forceConsistentCasingInFileNames": true,
6+
"strict": true,
7+
"importHelpers": true,
8+
"noImplicitOverride": true,
9+
"noImplicitReturns": true,
10+
"noFallthroughCasesInSwitch": true,
11+
"noPropertyAccessFromIndexSignature": true
12+
},
13+
"files": [],
14+
"include": [],
15+
"references": [
16+
{
17+
"path": "./tsconfig.lib.json"
18+
}
19+
]
20+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"compilerOptions": {
4+
"outDir": "../../../../dist/out-tsc",
5+
"declaration": true,
6+
"types": ["node"]
7+
},
8+
"include": ["src/**/*.ts"]
9+
}

libs/shared/infra/gql-client/src/graphql.client.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,32 @@
11
import type { GraphQLClientResponse } from 'graphql-request/build/legacy/helpers/types'
22

3+
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client/core'
34
import { getEnv } from '@codelab/shared/config/env'
45
import { GraphQLClient } from 'graphql-request'
56

67
const graphqlUrl = getEnv().endpoint.apiGraphqlUrl
78

9+
/**
10+
* In Node.js, the keepalive option you pass into graphql-request (or even the standard Fetch API)
11+
* is a browser-specific feature—part of the web spec that allows "keepalive" requests (such as
12+
* sending analytics beacons when a user closes a page). Node's underlying HTTP layer doesn't
13+
* use this parameter at all.
14+
*/
815
export const graphqlClient = new GraphQLClient(graphqlUrl.toString(), {
916
errorPolicy: 'all',
17+
fetch: async (...args) => {
18+
// Only import node-fetch on the server side
19+
if (typeof window === 'undefined') {
20+
const { nodeFetch } = await import('./node-fetch')
21+
22+
return nodeFetch(...args)
23+
}
24+
25+
// Use native fetch on the client side
26+
return fetch(...args)
27+
},
1028
headers: {
11-
Connection: 'keep-alive',
29+
// Connection: 'keep-alive',
1230
// 'Keep-Alive': 'timeout=60, max=1000',
1331
},
1432
keepalive: true,
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
export * from './apollo.client'
21
export * from './graphql.client'
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import type { RequestInit as NodeRequestInit } from 'node-fetch'
2+
3+
import http from 'node:http'
4+
import https from 'node:https'
5+
import fetch from 'node-fetch'
6+
7+
/**
8+
* Use custom keep-alive agents to reduce 'socket hang up' errors
9+
*/
10+
const httpAgent = new http.Agent({
11+
keepAlive: true,
12+
maxSockets: 100,
13+
})
14+
15+
const httpsAgent = new https.Agent({
16+
keepAlive: true,
17+
maxSockets: 100,
18+
})
19+
20+
type Fetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>
21+
22+
/**
23+
* `fetch` from node doesn't support agent, but `node-fetch` does
24+
*/
25+
export const nodeFetch: Fetch = (input, init): Promise<Response> => {
26+
const actualUrl =
27+
typeof input === 'string'
28+
? new URL(input)
29+
: input instanceof URL
30+
? input
31+
: new URL(input.toString())
32+
33+
const agent = actualUrl.protocol === 'http:' ? httpAgent : httpsAgent
34+
35+
// The important part: cast to node-fetch's RequestInit
36+
// so that 'agent' is recognized, avoiding TS errors
37+
/**
38+
* Convert from native fetch to node-fetch
39+
*/
40+
return fetch(actualUrl, {
41+
...(init as NodeRequestInit),
42+
agent,
43+
}) as unknown as Promise<Response>
44+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@
210210
"next-plugin-svgr": "1.1.12",
211211
"next-seo": "6.6.0",
212212
"next-themes": "0.4.4",
213+
"node-fetch": "^3.3.2",
213214
"object-typed": "1.0.0",
214215
"p-min-delay": "4.0.2",
215216
"passport": "0.7.0",

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tsconfig.base.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,9 @@
897897
"@codelab/frontend/infra/context": [
898898
"libs/frontend/infra/context/src/index.ts"
899899
],
900+
"@codelab/frontend/infra/gql-client": [
901+
"libs/frontend/infra/gql-client/src/index.ts"
902+
],
900903
"@codelab/frontend/infra/logger": [
901904
"libs/frontend/infra/logger/src/index.ts"
902905
],

0 commit comments

Comments
 (0)