Skip to content
Open
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
6 changes: 3 additions & 3 deletions frontend/src/utils/helpers/apolloClient.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client'
import { setContext } from '@apollo/client/link/context'
import { AppError, handleAppError } from 'app/global-error'

import { GRAPHQL_URL } from 'utils/env.client'
import { getCsrfToken } from 'utils/utility'

const createApolloClient = () => {
if (!GRAPHQL_URL) {
const error = new AppError(500, 'Missing GraphQL URL')
handleAppError(error)
// Only create the error for logging or throwing, do not emit a toast here
return null
}

Expand Down
106 changes: 97 additions & 9 deletions frontend/src/wrappers/provider.tsx
Comment thread
Saurabh-2607 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
'use client'
import { ApolloProvider } from '@apollo/client/react'
import { HeroUIProvider, ToastProvider } from '@heroui/react'
import { addToast } from '@heroui/toast'
import { useDjangoSession } from 'hooks/useDjangoSession'
import { SessionProvider } from 'next-auth/react'
import { ThemeProvider as NextThemesProvider } from 'next-themes'
import React, { Suspense } from 'react'
import { ENVIRONMENT, GRAPHQL_URL } from 'utils/env.client'
import apolloClient from 'utils/helpers/apolloClient'
import { getCsrfToken } from 'utils/utility'

// <AppInitializer> is a component that initializes the Django session.
// It ensures the session is synced with Django when the app starts.
Expand All @@ -22,24 +25,109 @@ export function Providers({
}: Readonly<{
children: React.ReactNode
}>) {
const isProduction = ENVIRONMENT === 'production'
const [graphQLReachability, setGraphQLReachability] = React.useState<
'pending' | 'reachable' | 'unreachable'
>('pending')
const lastToastMessageRef = React.useRef<string | null>(null)

React.useEffect(() => {
if (!apolloClient || !GRAPHQL_URL) {
setGraphQLReachability('unreachable')
return
}
const graphqlUrl = GRAPHQL_URL
const abortController = new AbortController()

const verifyGraphQLEndpoint = async () => {
try {
const csrfToken = await getCsrfToken()
const response = await fetch(graphqlUrl, {
body: JSON.stringify({ query: 'query { __typename }' }),
credentials: 'include',
headers: {
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken,
},
method: 'POST',
signal: abortController.signal,
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if (!response.ok) {
setGraphQLReachability('unreachable')
return
}

setGraphQLReachability('reachable')
} catch {
if (!abortController.signal.aborted) {
setGraphQLReachability('unreachable')
}
}
}

setGraphQLReachability('pending')
void verifyGraphQLEndpoint()

return () => {
abortController.abort()
}
}, [])

let graphQLErrorMessage: string | null = null
if (!apolloClient) {
return (
<div className="flex min-h-screen items-center justify-center text-red-500">
Configuration Error: GraphQL Client failed to initialize
</div>
)
if (isProduction) {
graphQLErrorMessage = 'Something went wrong'
} else {
graphQLErrorMessage =
'GraphQL client setup required. Ensure backend is running and GraphQL environment variables are configured.'
}
} else if (graphQLReachability === 'unreachable') {
if (isProduction) {
graphQLErrorMessage = 'Something went wrong'
} else {
graphQLErrorMessage =
'GraphQL endpoint is unreachable. Ensure the backend service is running and NEXT_PUBLIC_GRAPHQL_URL is correct.'
}
}

React.useEffect(() => {
if (!graphQLErrorMessage || lastToastMessageRef.current === graphQLErrorMessage) {
return
}

addToast({
color: 'danger',
description: graphQLErrorMessage,
shouldShowTimeoutProgress: true,
timeout: 5000,
title: 'Configuration Error',
variant: 'solid',
})

lastToastMessageRef.current = graphQLErrorMessage
}, [graphQLErrorMessage])

return (
<Suspense>
<SessionProvider>
<HeroUIProvider>
<NextThemesProvider attribute="class" defaultTheme="dark">
<ToastProvider />
<ApolloProvider client={apolloClient}>
<AppInitializer />
{children}
</ApolloProvider>
{graphQLReachability === 'pending' && (
<div style={{ padding: 32, textAlign: 'center' }}>Checking GraphQL endpoint…</div>
)}
{graphQLReachability === 'unreachable' && (
<div style={{ padding: 32, textAlign: 'center', color: 'red' }}>
GraphQL endpoint is unreachable.
</div>
)}
Comment thread
Saurabh-2607 marked this conversation as resolved.
Comment thread
Saurabh-2607 marked this conversation as resolved.
{apolloClient && graphQLReachability === 'reachable' ? (
<ApolloProvider client={apolloClient}>
<AppInitializer />
{children}
</ApolloProvider>
) : null}
</NextThemesProvider>
</HeroUIProvider>
</SessionProvider>
Expand Down