Skip to content

Commit ed8aa60

Browse files
committed
fix(graphql): use react.cache() to prevent apollo client memory leak
1 parent da00a47 commit ed8aa60

2 files changed

Lines changed: 33 additions & 16 deletions

File tree

src/js/auth/initializeUserInDb.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { getClient } from '../graphql/ServerClient'
1+
import { getGlobalClient } from '../graphql/ServerClient'
22
import { MUTATION_UPDATE_PROFILE, QUERY_GET_USERNAME_BY_UUID } from '../graphql/gql/users'
33
import { updateUser } from './ManagementClient'
44
import { Username } from '../types'
5+
56
export interface UpdateUsernameInput {
67
userUuid: string
78
username: string
@@ -14,18 +15,17 @@ interface InitializeUserInDBParams extends UpdateUsernameInput {
1415
auth0UserId: string
1516
}
1617

17-
const serverClient = getClient()
18-
1918
/**
2019
* Look up in our db (not Auth0) to see whether a user by uuid exists. If it doesn't then insert a new user profile.
2120
*/
2221
export const initializeUserInDB = async (params: InitializeUserInDBParams): Promise<boolean> => {
2322
const { auth0UserId, accessToken, userUuid, username, email, avatar } = params
24-
const existed = await doesUserByUuidExist(userUuid)
23+
const client = getGlobalClient()
24+
const existed = await doesUserByUuidExist(client, userUuid)
2525
if (existed != null) {
2626
return false
2727
}
28-
const res = await serverClient.mutate<{ updateUserProfile?: boolean }, UpdateUsernameInput>({
28+
const res = await client.mutate<{ updateUserProfile?: boolean }, UpdateUsernameInput>({
2929
mutation: MUTATION_UPDATE_PROFILE,
3030
variables: {
3131
userUuid,
@@ -52,8 +52,8 @@ export const initializeUserInDB = async (params: InitializeUserInDBParams): Prom
5252
return success
5353
}
5454

55-
const doesUserByUuidExist = async (userUuid: string): Promise<Username | null> => {
56-
const res = await serverClient.query<{ getUsername?: Username }, { userUuid: string }>({
55+
const doesUserByUuidExist = async (client: ReturnType<typeof getGlobalClient>, userUuid: string): Promise<Username | null> => {
56+
const res = await client.query<{ getUsername?: Username }, { userUuid: string }>({
5757
query: QUERY_GET_USERNAME_BY_UUID,
5858
variables: {
5959
userUuid

src/js/graphql/ServerClient.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { ApolloClient, HttpLink, InMemoryCache, from } from '@apollo/client'
2-
import { registerApolloClient } from '@apollo/experimental-nextjs-app-support'
1+
import { ApolloClient, HttpLink, InMemoryCache, ApolloLink } from '@apollo/client'
2+
import { cache } from 'react'
33
import { dynamicTagsLink } from './dynamicTagsLink'
44

55
const uri: string = process.env.OPENBETA_API_SERVER ?? ''
@@ -12,14 +12,31 @@ console.log('###################################################################
1212
console.log(' API Server', uri)
1313
console.log('#######################################################################')
1414

15-
const httpLink = new HttpLink({ uri })
15+
const httpLink = new HttpLink({
16+
uri,
17+
fetchOptions: {
18+
signal: AbortSignal.timeout(30000) // 30 second timeout
19+
}
20+
})
1621

17-
/**
18-
* Apollo client to be used in React Server Components.
19-
*/
20-
export const { getClient } = registerApolloClient(() => {
22+
const makeClient = (): ApolloClient<any> => {
2123
return new ApolloClient({
2224
cache: new InMemoryCache(),
23-
link: from([dynamicTagsLink, httpLink])
25+
link: ApolloLink.from([dynamicTagsLink, httpLink]),
26+
ssrMode: true
2427
})
25-
})
28+
}
29+
30+
/**
31+
* Apollo client for React Server Components.
32+
* Uses React.cache() to share one client instance per request.
33+
*/
34+
export const getClient = cache(makeClient)
35+
36+
/**
37+
* Apollo client for non-RSC contexts (API routes, auth callbacks).
38+
* Creates a fresh client per call.
39+
*/
40+
export function getGlobalClient (): ApolloClient<any> {
41+
return makeClient()
42+
}

0 commit comments

Comments
 (0)