Au cours de mes années de création de services, l' API RESTful a été ma principale référence. Cependant, même si REST a ses avantages, cela ne signifie pas qu’il constitue la meilleure approche pour chaque cas d’utilisation. Au fil des années, j’ai appris que, parfois, il peut y avoir de meilleures alternatives pour certains scénarios. S'en tenir à REST simplement parce que cela me passionne (quand ce n'est pas la bonne solution) n'entraîne qu'une dette technologique et une relation tendue avec le propriétaire du produit.
L’un des plus gros problèmes de l’approche RESTful est la nécessité de faire plusieurs requêtes pour récupérer toutes les informations nécessaires à une décision commerciale.
À titre d'exemple, supposons que je souhaite une vue à 360° d'un client. Il me faudrait faire les demandes suivantes :
GET /customers/{some_token}
fournit les informations de base sur le clientGET /addresses/{some_token}
fournit une adresse requiseGET /contacts/{some_token}
renvoie les informations de contactGET /credit/{some_token}
renvoie des informations financières clés
Même si je comprends que l'objectif sous-jacent de REST est de garder les réponses ciblées sur chaque ressource, ce scénario nécessite davantage de travail du côté du consommateur. Juste pour remplir une interface utilisateur qui aide une organisation à prendre des décisions liées aux futures activités avec le client, le consommateur doit passer plusieurs appels.
Dans cet article, je vais montrer ici pourquoi GraphQL est l'approche préférée par rapport à une API RESTful, en démontrant comment déployer Apollo Server (et Apollo Explorer) pour être opérationnel rapidement avec GraphQL.
Je prévois de créer ma solution avec Node.js et de déployer ma solution sur Heroku.
Il existe plusieurs cas d'utilisation courants dans lesquels GraphQL constitue une meilleure approche que REST :
Étant donné que mes compétences avec GraphQL ne sont pas perfectionnées, j'ai décidé d'utiliser Apollo Server pour cet article.
Apollo Server est un serveur GraphQL qui fonctionne avec n'importe quel schéma GraphQL. L'objectif est de simplifier le processus de création d'une API GraphQL. La conception sous-jacente s'intègre bien aux frameworks tels que Express ou Koa. J'explorerai la possibilité de tirer parti des abonnements (via la bibliothèque graphql-ws ) pour des données en temps réel dans mon prochain article.
Là où Apollo Server brille vraiment, c'est Apollo Explorer, une interface Web intégrée que les développeurs peuvent utiliser pour explorer et tester leurs API GraphQL. Le studio me conviendra parfaitement, car il permet de construire facilement des requêtes et de visualiser le schéma de l'API dans un format graphique.
Pour cet exemple, supposons que nous ayons besoin du schéma suivant pour fournir une vue à 360° du client :
type Customer { token: String name: String sic_code: String } type Address { token: String customer_token: String address_line1: String address_line2: String city: String state: String postal_code: String } type Contact { token: String customer_token: String first_name: String last_name: String email: String phone: String } type Credit { token: String customer_token: String credit_limit: Float balance: Float credit_score: Int }
Je prévois de me concentrer sur les requêtes GraphQL suivantes :
type Query { addresses: [Address] address(customer_token: String): Address contacts: [Contact] contact(customer_token: String): Contact customers: [Customer] customer(token: String): Customer credits: [Credit] credit(customer_token: String): Credit }
Les consommateurs fourniront le jeton au client qu'ils souhaitent voir. Nous prévoyons également de récupérer les objets Adresse, Contact et Crédit appropriés. Le but est d’éviter de faire quatre appels API différents pour toutes ces informations plutôt qu’avec un seul appel API.
J'ai commencé par créer un nouveau dossier appelé graphql-server-customer
sur mon poste de travail local. Ensuite, à l'aide de la section Prise en main de la documentation d'Apollo Server, j'ai suivi les étapes un et deux en utilisant une approche Typescript.
Ensuite, j'ai défini mon schéma et inclus également des données statiques à des fins de test. Normalement, nous nous connecterions à une base de données, mais les données statiques fonctionneront bien pour cette démo.
Vous trouverez ci-dessous mon fichier index.ts
mis à jour :
import { ApolloServer } from '@apollo/server'; import { startStandaloneServer } from '@apollo/server/standalone'; const typeDefs = `#graphql type Customer { token: String name: String sic_code: String } type Address { token: String customer_token: String address_line1: String address_line2: String city: String state: String postal_code: String } type Contact { token: String customer_token: String first_name: String last_name: String email: String phone: String } type Credit { token: String customer_token: String credit_limit: Float balance: Float credit_score: Int } type Query { addresses: [Address] address(customer_token: String): Address contacts: [Contact] contact(customer_token: String): Contact customers: [Customer] customer(token: String): Customer credits: [Credit] credit(customer_token: String): Credit } `; const resolvers = { Query: { addresses: () => addresses, address: (parent, args, context) => { const customer_token = args.customer_token; return addresses.find(address => address.customer_token === customer_token); }, contacts: () => contacts, contact: (parent, args, context) => { const customer_token = args.customer_token; return contacts.find(contact => contact.customer_token === customer_token); }, customers: () => customers, customer: (parent, args, context) => { const token = args.token; return customers.find(customer => customer.token === token); }, credits: () => credits, credit: (parent, args, context) => { const customer_token = args.customer_token; return credits.find(credit => credit.customer_token === customer_token); } }, }; const server = new ApolloServer({ typeDefs, resolvers, }); const { url } = await startStandaloneServer(server, { listen: { port: 4000 }, }); console.log(`Apollo Server ready at: ${url}`); const customers = [ { token: 'customer-token-1', name: 'Acme Inc.', sic_code: '1234' }, { token: 'customer-token-2', name: 'Widget Co.', sic_code: '5678' } ]; const addresses = [ { token: 'address-token-1', customer_token: 'customer-token-1', address_line1: '123 Main St.', address_line2: '', city: 'Anytown', state: 'CA', postal_code: '12345' }, { token: 'address-token-22', customer_token: 'customer-token-2', address_line1: '456 Elm St.', address_line2: '', city: 'Othertown', state: 'NY', postal_code: '67890' } ]; const contacts = [ { token: 'contact-token-1', customer_token: 'customer-token-1', first_name: 'John', last_name: 'Doe', email: '[email protected]', phone: '123-456-7890' } ]; const credits = [ { token: 'credit-token-1', customer_token: 'customer-token-1', credit_limit: 10000.00, balance: 2500.00, credit_score: 750 } ];
Avec tout configuré comme prévu, nous exécutons la commande suivante pour démarrer le serveur :
$ npm start
Avec le serveur Apollo fonctionnant sur le port 4000, j'ai utilisé l'URL http://localhost:4000/ pour accéder à Apollo Explorer. Ensuite, j'ai mis en place l'exemple de requête suivant :
query ExampleQuery { addresses { token } contacts { token } customers { token } }
Voici à quoi cela ressemble dans Apollo Explorer :
En appuyant sur le bouton Exemple de requête , j'ai validé que la charge utile de la réponse était alignée sur les données statiques que j'avais fournies dans index.ts
:
{ "data": { "addresses": [ { "token": "address-token-1" }, { "token": "address-token-22" } ], "contacts": [ { "token": "contact-token-1" } ], "customers": [ { "token": "customer-token-1" }, { "token": "customer-token-2" } ] } }
Avant d'aller plus loin dans mon cas d'utilisation Customer 360, je souhaitais exécuter ce service dans le cloud.
Puisque cet article vise à faire quelque chose de nouveau, je voulais voir à quel point il serait difficile de déployer mon serveur Apollo sur Heroku.
Je savais que je devais tenir compte des différences de numéros de port entre une exécution locale et une exécution quelque part dans le cloud. J'ai mis à jour mon code pour démarrer le serveur comme indiqué ci-dessous :
const { url } = await startStandaloneServer(server, { listen: { port: Number.parseInt(process.env.PORT) || 4000 }, });
Avec cette mise à jour, nous utiliserons le port 4000 sauf si une valeur PORT est spécifiée dans une variable d'environnement.
À l'aide de Gitlab, j'ai créé un nouveau projet pour ces fichiers et je me suis connecté à mon compte Heroku à l'aide de l'interface de ligne de commande (CLI) Heroku :
$ heroku login
Vous pouvez créer une nouvelle application dans Heroku avec leur CLI ou l'interface utilisateur Web du tableau de bord Heroku. Pour cet article, nous utiliserons la CLI :
$ heroku create jvc-graphql-server-customer
La commande CLI a renvoyé la réponse suivante :
Creating ⬢ jvc-graphql-server-customer... done https://jvc-graphql-server-customer-b62b17a2c949.herokuapp.com/ | https://git.heroku.com/jvc-graphql-server-customer.git
La commande a également ajouté automatiquement le référentiel utilisé par Heroku comme télécommande :
$ git remote heroku origin
Par défaut, Apollo Server désactive Apollo Explorer dans les environnements de production. Pour ma démo, je souhaite la laisser fonctionner sur Heroku. Pour ce faire, je dois définir la variable d'environnement NODE_ENV
sur development. Je peux définir cela avec la commande CLI suivante :
$ heroku config:set NODE_ENV=development
La commande CLI a renvoyé la réponse suivante :
Setting NODE_ENV and restarting ⬢ jvc-graphql-server-customer... done, v3 NODE_ENV: development
Nous sommes maintenant en mesure de déployer notre code sur Heroku :
$ git commit --allow-empty -m 'Deploy to Heroku' $ git push heroku
Un aperçu rapide du tableau de bord Heroku montre que mon serveur Apollo fonctionne sans aucun problème :
Si vous êtes nouveau sur Heroku, ce guide vous montrera comment créer un nouveau compte et installer la CLI Heroku.
Avec GraphQL, je peux répondre aux critères d'acceptation de mon cas d'utilisation Customer 360 avec la requête suivante :
query CustomerData($token: String) { customer(token: $token) { name sic_code token }, address(customer_token: $token) { token customer_token address_line1 address_line2 city state postal_code }, contact(customer_token: $token) { token, customer_token, first_name, last_name, email, phone }, credit(customer_token: $token) { token, customer_token, credit_limit, balance, credit_score } }
Tout ce que j'ai à faire est de transmettre une seule variable token
Customer avec une valeur customer-token-1
:
{ "token": "customer-token-1" }
Nous pouvons récupérer toutes les données à l'aide d'un seul appel d'API GraphQL :
{ "data": { "customer": { "name": "Acme Inc.", "sic_code": "1234", "token": "customer-token-1" }, "address": { "token": "address-token-1", "customer_token": "customer-token-1", "address_line1": "123 Main St.", "address_line2": "", "city": "Anytown", "state": "CA", "postal_code": "12345" }, "contact": { "token": "contact-token-1", "customer_token": "customer-token-1", "first_name": "John", "last_name": "Doe", "email": "[email protected]", "phone": "123-456-7890" }, "credit": { "token": "credit-token-1", "customer_token": "customer-token-1", "credit_limit": 10000, "balance": 2500, "credit_score": 750 } } }
Vous trouverez ci-dessous une capture d'écran d'Apollo Explorer exécuté à partir de mon application Heroku :
Je me souviens plus tôt dans ma carrière, lorsque Java et C# se faisaient concurrence pour l'adoption par les développeurs. Les défenseurs des deux côtés du débat étaient prêts à prouver que la technologie qu'ils avaient choisie était le meilleur choix… même si ce n'était pas le cas.
Dans cet exemple, nous aurions pu répondre à mon cas d'utilisation Customer 360 de plusieurs manières. L'utilisation d'une API RESTful éprouvée aurait fonctionné, mais cela aurait nécessité plusieurs appels d'API pour récupérer toutes les données nécessaires. L'utilisation d'Apollo Server et de GraphQL m'a permis d'atteindre mes objectifs avec un seul appel API.
J'aime aussi la facilité avec laquelle déployer mon serveur GraphQL sur Heroku avec seulement quelques commandes dans mon terminal. Cela me permet de me concentrer sur la mise en œuvre, en déchargeant les charges de l'infrastructure et en exécutant mon code auprès d'un fournisseur tiers de confiance. Plus important encore, cela correspond parfaitement à ma déclaration de mission personnelle :
« Concentrez votre temps sur la fourniture de caractéristiques/fonctionnalités qui augmentent la valeur de votre propriété intellectuelle. Tirez parti des frameworks, des produits et des services pour tout le reste.
– J.Vester
Si vous êtes intéressé par le code source de cet article, il est disponible sur GitLab .
Mais attendez… il y a plus !
Dans mon article de suivi, nous développerons davantage notre serveur GraphQL, pour implémenter l'authentification et la récupération de données en temps réel avec les abonnements.
Passez une très bonne journée !