Remplacer Relay par Apollo Client
Cette documentation a pour but de vous guider dans le remplacement de Relay par Apollo Client comme client graphQL de l'application React de votre projet.
â ïž Avertissement
Ce guide s'applique uniquement aux projets initiés avec un OIP Starter v2.
Nous vous recommandons de suivre ce guide une fois la migration Remplacer webpack par vite.jsï»ż terminĂ©e.
Si vous choisissez dâutiliser un outil de codegen avec Apollo, une Ă©tape de gĂ©nĂ©ration peut exister. Les avantages listĂ©s ci-dessus sâappliquent Ă lâapproche Apollo sans codegen obligatoire, recommandĂ©e pour rĂ©duire la complexitĂ© au dĂ©but de la migration.
Lien vers la documentation officielle du client Apollo : apollo-client
Apollo Client
Apollo Client est une bibliothÚque moderne pour gérer les queries et mutations GraphQL cÎté client.
Fonctionnalités principales
- Gestion avancĂ©e du cache DĂ©finissez une politique de cache au niveau du client ou spĂ©cifiquement par requĂȘte ou mutation.
- Intégration naturelle avec React Conçu pour React et ses derniÚres fonctionnalités (hooks, Suspense, etc.).
Gains par rapport Ă un setup Relay
- API plus moderne et uniforme Initialisation du client et gestion des opérations GraphQL plus simples et plus lisibles.
- Aucune gĂ©nĂ©ration de fichiers obligatoire Pas dâĂ©tape supplĂ©mentaire cĂŽtĂ© client pour produire des fichiers __generated__. RĂ©sultat: suppression de milliers de lignes versionnĂ©es et dâun flux CI plus lĂ©ger.
- Pas dâintrospection manuelle imposĂ©e Plus besoin de lancer une Ă©tape dâintrospection du schĂ©ma pour gĂ©nĂ©rer des artefacts avant le build des vues. Le cycle de build devient plus court et moins couplĂ© au serveur.
- Moins de dĂ©pendances entre serveur et build front Le serveur nâa plus besoin dâĂȘtre dĂ©marrĂ© en amont dâune commande de build front uniquement pour gĂ©nĂ©rer des fichiers GraphQL.
Pour résumer, le passage à Apollo Client nettoie le setup de votre code-base et par conséquent vous fait gagner en maintenabilité. De plus, vous gagnerez en rapidité et fluidité lors des phases de développement et du build des vues aprÚs modification de la couche graphQL.
Ce guide dĂ©crit pas Ă pas la procĂ©dure de migration de votre projet OIP v2 (Relay) vers Apollo Client (OIP v3). Lâobjectif est de remplacer progressivement Relay par Apollo, sans breaking change majeur sur vos composants ou vos handlers.
1. PrĂ©paration Ă lâinstallation
1.1 Suppression des dépendances Relay
Supprimer du package.json toutes les dépendances liées à Relay :
babel-plugin-relay
react-relay
relay-compiler
@graphql-inspector/cli
relay-runtime
vite-plugin-relay1.2 Mise à jour des dépendances GraphQL
Mettre à jour la dépendance graphql en version minimale 16.10.0 :
yarn add graphql@^16.10.01.3 Suppression des scripts Relay
Retirer du champ scripts de votre package.json :
getSchema
relay1.4 Suppression des fichiers Relay
Supprimer les fichiers obsolĂštes Ă la racine du projet :
- buildViews.sh
- relay.config.json
- schema.graphql
1.5 Nettoyage des fichiers générés
Supprimer les rĂ©pertoires __generated__et nettoyer lâindex Git :
find ./app -type d -name "__generated__" | xargs rm -r
find ./views -type d -name "__generated__" | xargs rm -r
git rm -r --cached app/**/__generated__ views/**/__generated__1.6 Mise Ă jour de OIP Tools
La version 2.4.1 de la dépendance @fasstech/oip-tools apporte le support du client Apollo pour la génération des fichiers des vues des projets v2.
Mettre Ă jour @fasstech/oip-tools pour le support Apollo et ajuster le script de build des vues.
yarn add @fasstech/[email protected]{
"scripts": {
"build:views": "oip-tools views build --config ./viewsConfig --extFileBackend mjs --gql-client apollo --gen-test-files false"
}
}1.7 Mise Ă jour de la configuration Vite
Dans vite.config.js :
- Supprimer lâimport de vite-plugin-relay
- Retirer le plugin Relay de la propriété plugins
1.8 Mise Ă jour du Dockerfile
Supprimer toutes les références à Relay, notamment :
- La copie de relay.config.json, schema.graphql, etc.
- LâexĂ©cution de la commande yarn relay
1.9 Installation dâApollo Client
Installer Apollo Client :
yarn add --dev @apollo/[email protected]2. Migration du dossier app
2.1 Objectif
- Remplacer le client GraphQL Relay par Apollo
- Migrer queries, mutations, hooks et providers
- Adapter Process.jsx
2.2 Migration du provider GraphQL
Fichiers Ă supprimer dans app/_graphql :
- Environment.js
- Query.js si non utilisĂ© (ou lâadapter)
- populateChildren.js si non utilisĂ© (ou lâadapter)
Fichier à créer app/_graphql/link.js :
import { ApolloLink, HttpLink, from, split } from '@apollo/client';
import { RetryLink } from '@apollo/client/link/retry';
import { onError } from '@apollo/client/link/error';
import { getMainDefinition } from '@apollo/client/utilities';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { removeTypenameFromVariables } from '@apollo/client/link/remove-typename';
import { setContext } from '@apollo/client/link/context';
import { createClient } from 'graphql-ws';
import Tokens from '../Tokens.js';
export const createLink = (projectId) => {
const errorLink = onError(async ({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.forEach(({ message, locations, path }) =>
console.log(`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`)
);
}
if (networkError) {
console.log(`[Network error]: ${networkError}`);
}
});
const retryLink = new RetryLink({
delay: {
initial: 300,
max: Infinity,
jitter: true
},
attempts: {
max: 3,
retryIf: async (error, _operation) => {
// on 401 error, try to refresh access token and retry operation
if (error.statusCode === 401) {
await Tokens.getAccessToken(true);
return true;
}
return false;
}
}
});
const authLink = setContext(async (_, { headers }) => {
const accessToken = await Tokens.getAccessToken();
return { headers: { ...headers, Authorization: `Bearer ${accessToken}` } };
});
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return definition.kind === 'OperationDefinition' && definition.operation === 'subscription';
},
new GraphQLWsLink(
createClient({
url: async () => {
const response = await fetch('/graphqlwsurl');
const { url } = await response.json();
return url;
}
})
),
new HttpLink({ uri: '/graphql' })
);
return from([
new ApolloLink((operation, forward) => {
operation.setContext({ headers: { 'fasst-oav-project-id': projectId } });
return forward(operation);
}),
errorLink,
retryLink,
authLink,
removeTypenameFromVariables(),
splitLink
]);
};Voici une synthÚse claire et structurée de toutes les modifications apportées au fichier app/index.jsx lors de la migration de Relay vers Apollo Client :
đ§© 1. Nettoyage des imports
â Ă supprimer dans le fichier app/index.jsx
import { RelayEnvironmentProvider } from 'react-relay';
import GQLEnvironment from '@@appGraphQL/Environment';â Ă ajouter dans le fichier app/index.jsx
import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client';
import { createLink } from './_graphql';âïž 2. Suppression de lâenvironnement Relay
â Ă supprimer dans le fichier app/index.jsx
const environment = await GQLEnvironment();Lâenvironnement Relay nâest plus utilisĂ© : il est remplacĂ© par une instance ApolloClient créée dynamiquement.
đ§± 3. Suppression de la prop environment dans <App />
â Ă supprimerdans le fichier app/index.jsx
const App = ({ environment }) => { ... }
<App environment={environment} />Lâenvironnement GraphQL est dĂ©sormais injectĂ© via le provider global ApolloProvider, plus besoin de le passer en prop.
đȘ 4. Suppression du useEffect liĂ© Ă Relay
â Ă supprimer dans le fichier app/index.jsx
useEffect(() => {
GQLEnvironment.projectId = data?.projectId;
}, [data?.projectId]);Le projectId est maintenant directement passé à createLink(projectId) lors de la création du client Apollo.
đ§ 5. CrĂ©ation du client Apollo
â Ă ajouter dans le fichier app/index.jsx
const gqlClient = new ApolloClient({
cache: new InMemoryCache(),
defaultOptions: { watchQuery: { fetchPolicy: 'network-only' } },
link: createLink(data.projectId)
});Lâenvironnement Relay nâest plus utilisĂ© : il est remplacĂ© par une instance ApolloClient créée dynamiquement.
đ§© 6. Remplacement du provider GraphQL
â Ă supprimer dans le fichier app/index.jsx
<RelayEnvironmentProvider environment={environment}>
<App />
</RelayEnvironmentProvider>â Ă ajouter dans le fichier app/index.jsx
<ApolloProvider client={gqlClient}>
<App />
</ApolloProvider>Le provider Apollo remplace complĂštement le provider Relay pour exposer le client GraphQL Ă toute lâapplication.
đ 7. Comportement gĂ©nĂ©ral
- Le chargement du contexte client (projectId) doit précéder la création du client Apollo.
- Le provider Apollo englobe lâapplication entiĂšre (<App />).
- Le code est dĂ©sormais plus lĂ©ger (moins de boilerplate, pas dâenvironnement global managĂ© manuellement).
Type de changement | Description |
|---|---|
Imports | Suppression des imports Relay / ajout des imports Apollo |
Environnement | Suppression de `GQLEnvironment` et de toute dépendance Relay |
App Component | Ne reçoit plus `environment` en prop |
Hooks | Suppression du `useEffect` Relay (`projectId`) |
Client GraphQL | CrĂ©ation dâun `ApolloClient` configurĂ© avec `createLink(projectId)` |
Provider | Remplacement de `<RelayEnvironmentProvider>` par `<ApolloProvider>` |
Architecture | Passage à une logique plus standardisée et asynchrone basée sur le contexte proje |
Rappels (appliquĂ©s dans lâexemple)
- Plus aucun import Relay (RelayEnvironmentProvider, GQLEnvironment, etc.).
- Le composant <App /> ne reçoit plus de prop environment.
- Le client Apollo est créé aprÚs résolution du contexte (ici projectId) via createLink(projectId).
- _graphql/index.js ré-exporte désormais :
export * from './mutations';
export * from './queries';
export * from './link';Si vous nâavez pas useGlobalData, remplacez-le par votre mĂ©canisme actuel (selector Redux, autre hook, ou simple fetch) pour obtenir projectId.
3. Migration des Queries GraphQL
Avant (Relay)
import { graphql } from 'react-relay';
const QProcessQuery = graphql`
query QProcessQuery {
process { ... }
}
`;
export default { query: QProcessQuery };Maintenant (Apollo)
import { gql } from '@apollo/client';
export const QProcessQuery = gql`
query QProcessQuery {
process { ... }
}
`;Points clés :
- Utiliser gql depuis @apollo/client
- Préférer un named export
- Mettre à jour app/_graphql/queries/index.js en conséquence
4. Migration des Mutations GraphQL
Avant (Relay)
import { commitMutation, graphql } from 'react-relay';Maintenant (Apollo)
import { gql } from '@apollo/client';
export const ProcessActionMutation = gql`
mutation ProcessActionMutation($viewId: ID!, $action: ID!, $object: ProcessObjectInput) {
processAction(viewId: $viewId, action: $action, object: $object) {
# ...
}
}
`;Si vous aviez une fonction dâexĂ©cution custom, remplacer commitMutation par client.mutate ou un hook useMutation.
5. Migration du fichier Process.jsx
Remplacer toutes les API Relay:
- useRelayEnvironment â useApolloClient
- fetchQuery(environment, query) â client.query({ query })
- commitMutation â client.mutate({ mutation, variables })
Exemples dâadaptation des handlers:
// Query
const client = useApolloClient();
const { data } = await client.query({ query: QProcessQuery });
// Mutation
const { data } = await client.mutate({
mutation: ProcessActionMutation,
variables: { viewId, action, object }
});Handlers concernés: onGetProcess, onAction, onNext, onGoto, onRunTask, et onBack.
6. Migration des Hooks
SynthĂšse de lâĂ©volution des hooks gĂ©nĂ©rĂ©s dans _common/client/hooks
useFetchQuery
Le hook useFetchQuery est conçu pour gérer une query GraphQL depuis un composant de vue.
Fichier : _common/client/hooks/useFetchQuery.js
Ăvolution :
// before with Relay
export const useFetchQuery = ({
gqlQuery,
args,
verbose = false,
skip = false,
initialValue = {},
dataProp
}) => {
// ...
return {
isLoaded,
errorMsg,
data,
dataProp,
fetching,
isFetching,
reFetch
};
}
// now with Apollo
export const useFetchQuery = ({
gqlQuery,
args,
// verbose = false, // removed
skip = false,
initialValue = {},
dataProp
}) => {
// ...
return {
error, // added
isLoaded,
errorMsg, // different format than previously
data,
dataProp,
// fetching, // removed
isFetching,
reFetch
};
}Le hook useFetchQuery implĂ©mente dĂ©sormais le hook useQuery de la librairie Apollo Client. Pour plus de dĂ©tails, reportez-vous Ă la documentation officielle du hook useQuery dâApollo.
Modifications :
- ParamĂštres : suppression de verbose
- Résultat :
- Ajout de error
- Suppression de fetching
useCommitMutation
Le hook useCommitMutation est conçu pour gérer une mutation GraphQL depuis un composant de vue.
Fichier : _common/client/hooks/useCommitMutation.js
Ăvolution :
// before with Relay
export const useCommitMutation = (mutation, { dataProp }) => {
// ...
return {
onFormSubmit,
isFetching
};
}
// now with Apollo
export const useCommitMutation = (mutation, { dataProp }) => {
// ...
return {
onFormSubmit,
isFetching,
called // added
};
}Le hook useCommitMutation implémente désormais le hook useMutation de la librairie Apollo Client. Pour plus de détails, consultez la documentation officielle du hook useMutation.
Modifications :
- ParamĂštres : aucun changement
- Résultat :
- Ajout de la propriété called (boolean), indiquant si la mutation a été appelée ou non
useSubscription
Le hook useSubscription est conçu pour gérer une subscription GraphQL depuis un composant de vue. Il remplace le hook useTaskUpdateSubscription.
Fichier : views/_common/client/hooks/useSubscription.js
Ăvolution : Ce hook nâest plus limitĂ© Ă la souscription de lâĂ©tat dâune tĂąche. Il permet dĂ©sormais de gĂ©rer toute subscription GraphQL depuis un composant de vue.
// before with Relay
export const useTaskUpdateSubscription = (variables) => {
// ...
return {
outputValue,
errorValue
};
}
// now with Apollo
export const useSubscription = (subscription, variables) => {
// ...
return {
data, // outputValue
error, // errorValue
loading // added
};
}Le hook useSubscription implĂ©mente dĂ©sormais le hook useSubscription dâApollo Client. Pour plus de dĂ©tails, consultez la documentation officielle du hook useSubscription.
Modifications :
- ParamĂštres :
- Le premier paramĂštre correspond Ă la subscription GraphQL
- Le second paramÚtre correspond aux variables associées
- Résultat :
- outputValue devient data
- errorValue devient error
- Ajout de loading (boolean) indiquant si la subscription est en cours de chargement
7. Migration du dossier _common et des vues
Suite Ă la mise Ă jour de @fasstech/oip-tools, le server n'a plus besoin d'ĂȘtre lancĂ© pour la gĂ©nĂ©ration des fichiers des vues avec Apollo comme client graphQL.
Supprimer les fichiers suivants du dossier _common (ces fichiers seront re-générés par la commande build:views) :
- views/_common/client/graphql/queries/TaskUpdateSubscription.js
- views/_common/client/hooks/useCommitMutation.js
- views/_common/client/hooks/useFetchQuery.js
- views/_common/client/hooks/useFetching.js
- views/_common/client/hooks/useTaskUpdateSubscription.js
Re-générer les vues :
yarn build:viewsLes breaking changes au niveau des composants du dossier client des vues sont limités comme suit :
- verbose est supprimé des paramÚtres acceptés par les hooks responsables des queries graphQL, générés dans le dossier hooks
- fetching est supprimé du résultat des hooks responsables des queries graphQL, générés dans le dossier hooks
- les appels au hook useTaskUpdateSubscription du dossier _common/client/hooks doivent ĂȘtre remplacĂ©s par l'utilisation du hook useSubscription
Exemple:
import { useState } from 'react';
import withProcess from '@@app/withProcess';
// before with Relay > import
import { useTaskUpdateSubscription } from '@@viewCommonHooks/useTaskUpdateSubscription';
// now with Apollo > import
import { useSubscription } from '@@viewCommonHooks/useSubscription';
import { TaskUpdateSubscription } from '@@views/_common/graphql/subscriptions/TaskUpdateSubscription';
const MyComponent = ({ context, processHandlers }) => {
// before with Relay > hook implementation
const { outputValue, errorValue } = useTaskUpdateSubscription({
taskName: 'magicTask',
projectId: context.projectId,
});
// now with Apollo > hook implementation
const { data: outputValue, error: errorValue } = useSubscription({
taskName: 'magicTask',
projectId: context.projectId,
}, TaskUpdateSubscription);
return (
<div>
...
</div>
);
};
export default withProcess(MyComponent);8. Vérification finale
Vous devez vous assurer de gĂ©rer ces breaking changes au sein des composants de vos vues. Les hooks personnalisĂ©s qui utilisent ceux gĂ©nĂ©rĂ©s dans le dossier _common/client/hooks sont Ă©galement impactĂ©s par ces modifications et doivent ĂȘtre adaptĂ©s si nĂ©cessaire.
- Rechercher le terme relay dans app/ et views/
- Supprimer toute référence restante
- Lancer yarn build et yarn build:views
- Tester vos composants et handlers migrés
En cas de doute, contactez lâĂ©quipe OIP pour valider la conformitĂ© de votre migration.