Voici comment démarrer tranquillement une application avec un front Vue.js qui interroge une API GraphQL.
Le tout sans faire trop d’effort à l’aide d’Hasura et d’Apollo.
Ça fait pas mal de technos :
- Vue.js : framework front JS
- Apollo : composant qui permet d’interroger une API GraphQL depuis le front
- GraphQL : type d’API créé par Facebook en 2012 (différent de REST et SOAP)
- Hasura : outil open-source qui permet d’exposer une API GraphQL depuis une base de donnée PostgreSQL
- PostgreSQL : le SGBD qui va stocker les données
Pour re-resumé : en installant l’outil Hasura sur un serveur PostgreSQL, on expose une API de type GraphQL à moindre effort.
Ensuite on peut interroger cette API depuis n’importe quel front, en l’occurrence on va se concentrer sur Vue.js et le composant Apollo (on peut imaginer qu’Apollo est à GraphQL/JS ce que PDO_MYSQL est à MySQL/PHP).
L’API GraphQL permet de récupérer des données plus finement et en une seule requête, contrairement à une API REST qui va nécessiter une requête par type de donnée.
Pour l’exemple on va utiliser des données data.gouv.fr : la liste des équipements sportifs en France.
Lancer Hasura sur sa machine
Créer un docker-compose qui charge Hasura et une base PostgreSQL, pour cela deux solutions :
- ajouter un service postgres dans le docker-compose (si on n’a pas Postgres en local)
- créer une base local Postgres sur sa machine
Version tout packagée avec PostgreSQL intégré à la stack Docker
Fichier docker-compose.yaml
version: '3.6'
services:
postgres:
image: postgres
ports:
- "5432:5432"
restart: always
volumes:
- ./db_data:/var/lib/postgresql/data
graphql-engine:
image: hasura/graphql-engine:v1.1.0
ports:
- "8080:8080"
depends_on:
- "postgres"
restart: always
environment:
HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:@postgres:5432/postgres
HASURA_GRAPHQL_ENABLE_CONSOLE: "true" # set to "false" to disable console
HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log
#HASURA_GRAPHQL_ADMIN_SECRET: secret
volumes:
db_data:
Version avec PostgreSQL installé en local
Fichier docker-compose.yaml
version: '3.6'
services:
graphql-engine:
image: hasura/graphql-engine:v1.1.0
ports:
- "8080:8080"
restart: always
environment:
HASURA_GRAPHQL_DATABASE_URL: postgres://where_to_sport:secret@172.17.0.1:5432/where_to_sport
HASURA_GRAPHQL_ENABLE_CONSOLE: "true" # set to "false" to disable console
HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log
#HASURA_GRAPHQL_ADMIN_SECRET: secret
Manipulation à faire sur PostgreSQL (pour la solution 2)
Création d’un base de donnée :
CREATE DATABASE where_to_sport;
Hasura nécessite l’extension pgcrypto pour fonctionner :
CREATE EXTENSION IF NOT EXISTS pgcrypto;
Création d’un schéma :
CREATE SCHEMA app;
Création d’un user pour accéder à la BDD :
CREATE ROLE where_to_sport LOGIN password 'secret';
GRANT ALL ON DATABASE where_to_sport TO where_to_sport;
Ajout des droits sur la BDD :
Fichier /etc/postgresql/11/main/pg_hba.conf
(remplacer 11 par votre version de PostgreSQL).
host where_to_sport where_to_sport 0.0.0.0/0 md5
Lancer la stack Docker :
docker-compose up -d
Accéder à Hasura : http://localhost:8080/
I want data !
On récupère le CSV sur data.gouv
On l’importe via un outil ou script (DataGrip par exemple).
On s’ajoute deux tables de liaison pour l’exemple (equipements_type qui contient le code équipement ainsi que son libellé, et une table departements).
Une fois que notre schéma est ok, il faut indiquer à Hasura quelle tables “tracker” et les relations entre elles (il les devine tout seul, il faut juste les activer).
On peut ensuite s’amuser avec l’outil intégré à Hasura (GraphiQL) pour faire nos premières requêtes :
Il reste maintenant à démarrer une application Vue.js et interroger cette API de manière dynamique.
L’outil Visual Studio Code dispose de plugins GraphQL, Apollo, Vue.js etc.
Vue.js
Initier un projet Vue.js avec ce dont vous avez besoin (node-sass, babel, router, eslint…).
npm run serve
La page d’accueil par défaut s’affiche sur http://localhost:8081/
Ajouter le composant Apollo au projet :
vue add apollo
Créer un fichier .env
à la racine du projet et ajouter l’url de l’API GraphQL exposé par Hasura :
VUE_APP_GRAPHQL_HTTP=http://localhost:8080/v1/graphql
VUE_APP_GRAPHQL_WS=ws://localhost:8080/v1/graphql
ws://
est le Websocket utilisé, il permet au back de pusher automatiquement les modifications de data au front, qui pourra ensuite les afficher en temps réel (comme un tchat).
Dans l’arborescence du projet Vue.js, plutôt que stocker les requêtes des données dans le dossier store, on va les stocker dans un dossier graphql.
C’est ici que seront organisé nos requêtes GraphQL, celles qui sont générées par l’outil GraphiQL d’Hasura.
Un fragment fonctionne de la même manière qu’un include, par exemple si vous avez 10 requêtes qui concernent des Users, et que vous récupérez systématiquement les mêmes champs (id, name, email), plutôt que de réécrire ces champs X fois il est préférable de les avoir dans un fragment qui sera réutilisable.
Voila à quoi ressemble la requête qui récupère les départements :
query GetDepartements {
app_departements(order_by: {libelle: asc}) {
id
libelle
}
}
Puis le fragment de ma recherche (equipement_type et departement étant les données des tables liées) :
fragment equipementsFragment on app_equipements {
id
cominsee
comlib
insnom
gestiontypeproprietaireprinclib
equnom
equdouche
equanneeservice
equnbplacetribune
naturesollib
naturelibelle
equnbvestiairespo
equaccueilbuvette
equgpsx
equgpsy
equdatemaj
equipement_type {
equipementtypecode
equipementtypelib
}
departement {
libelle
}
}
Et enfin la récupération d’équipements avec les deux critères de recherche et l’import du fragment qui s’écrit ...equipementsFragment
:
#import "./EquipementsFragment.gql"
query GetAllEquipements($departement_id: Int, $equipement_code: Int) {
app_equipements(
where: {
departement: {id: {_eq: $departement_id}},
_and: {
equipement_type: {equipementtypecode: {_eq: $equipement_code}}
}
},
order_by: {
comlib: asc,
equipement_type: {equipementtypelib: asc},
insnom: asc
}
) {
...equipementsFragment
}
}
Il faut noter que ces requêtes sont uniquement des query (équivalent du SELECT), il existe également les mutation (INSERT, UPDATE, DELETE), et les subscription (permet de récupérer en live les modifications de données).
Pour pouvoir utiliser ces requêtes il faut les importer dans le composant (ou la vue) :
import GET_DEPARTEMENTS from '@/graphql/GetDepartements.gql'
import GET_TYPES_EQUIPEMENTS from '@/graphql/GetTypesEquipements.gql'
import SEARCH_EQUIPEMENTS from '@/graphql/SearchEquipements.gql'
Le composant Apollo permet d’interroger l’API avec ces requêtes de manière simple.
Voila le bout de code qui récupère la liste des départements et des équipements, puis qui gère la recherche à la validation du formulaire :
export default {
name: 'Home',
data () {
return {
departements: [],
typesEquipements: [],
resultats: null,
searchVars: null,
searchDepartementId: null,
searchEquipementCode: null
}
},
apollo: {
app_departements: {
query: GET_DEPARTEMENTS,
result ({ data }) {
this.departements = data.app_departements
}
},
app_equipements_type: {
query: GET_TYPES_EQUIPEMENTS,
result ({ data }) {
this.typesEquipements = data.app_equipements_type
}
},
app_equipements: {
query: SEARCH_EQUIPEMENTS,
variables () {
return {
departement_id: this.searchDepartementId,
equipement_code: this.searchEquipementCode
}
},
skip () {
return !this.searchVars || !this.searchVars.searchDepartementId || !this.searchVars.searchEquipementCode
},
result ({ data }) {
this.resultats = data.app_equipements
}
}
},
methods: {
search (e) {
e.preventDefault()
this.searchVars = {
searchDepartementId: this.searchDepartementId,
searchEquipementCode: this.searchEquipementCode
}
}
}
}
L’instruction skip indique sous quelles conditions la requête ne doit pas être lancée, en l’occurrence si nos critères de recherches ne sont pas renseignés.
On ajoute à ça le plugin vue2-google-maps avec npm et on obtient une application de recherche d’équipements sportifs :
Pour aller plus loin on peut ajouter des facettes afin de filtrer plus finement la recherche, agrémenter Google Maps de photos de lieux etc. :)