Exploiter les API dans Power BI : Authentification JWT et Pagination avec Power Query
📆 Publié le
9 min de lecture
mack par Macktireh

Exploiter les API dans Power BI : Authentification JWT et Pagination avec Power Query


Table des matières


Introduction

Si vous utilisez Power BI, vous avez probablement déjà eu besoin d’importer des données depuis une API REST. Et vous savez quoi ? Ce n’est pas toujours une partie de plaisir ! Entre l’authentification, les en-têtes HTTP, la pagination… on peut vite se perdre.

Dans cet article, je vais vous montrer concrètement comment importer des données d’une API dans Power BI en utilisant Power Query. On va voir ensemble comment gérer l’authentification JWT et la pagination, le tout avec un exemple pratique basé sur l’API DummyJSON . C’est une API gratuite et simple qui simule parfaitement les cas d’usage réels.

Avant de commencer, j’imagine que vous :

  • Connaissez déjà les bases de Power BI Desktop
  • Avez déjà ouvert l’éditeur Power Query au moins une fois
  • Comprenez les concepts de base des API REST (GET, POST, en-têtes, JSON)

Si c’est le cas, parfait ! On peut y aller.


Exploration de l’API DummyJSON

Avant de plonger dans le code, prenons quelques minutes pour comprendre comment fonctionne l’API qu’on va utiliser. DummyJSON est une ressource géniale qui fournit des données fictives (produits, utilisateurs, articles, etc.) et simule les mécanismes typiques des vraies API : authentification, pagination, etc.

Voici le processus général pour récupérer des données depuis une API protégée :

Schéma des étapes de récupération des données

Étape 1 : L’authentification

Envoi des identifiants

La première chose à faire, c’est s’authentifier. On envoie une requête POST à l’endpoint /auth/login avec nos identifiants au format JSON :

{
"username": "emilys",
"password": "emilyspass"
}

Réception des jetons

En retour, l’API nous donne un objet JSON avec deux jetons importants :

  • accessToken : c’est notre sésame pour accéder aux données protégées
  • refreshToken : il servira à renouveler l’access token quand il expirera

Étape 2 : Récupération des données

Envoi de la requête avec le jeton

Maintenant qu’on a notre accessToken, on peut demander les données ! On envoie une requête GET à /auth/products en incluant le jeton dans l’en-tête Authorization comme ceci : Bearer votre_access_token.

Réception des données

L’API nous renvoie un objet JSON contenant :

  • La liste des produits dans le champ products
  • Des infos sur la pagination : total (nombre total d’éléments), skip (combien on en a sauté), limit (combien on en récupère par page)

Voici un exemple de réponse simplifiée :

{
"products": [
{
"id": 1,
"title": "iPhone 13 Pro",
"description": "Latest iPhone model with advanced features",
"category": "Electronics",
"price": 999.99,
"discountPercentage": 10.5,
"rating": 4.7,
"stock": 50,
"brand": "Apple",
...
},
...
],
"total": 80,
"skip": 0,
"limit": 20
}

Vous voyez le principe ? Maintenant qu’on comprend le fonctionnement, passons à l’implémentation dans Power Query !


Configuration des paramètres et des fonctions Power Query

C’est parti pour le code ! On va construire notre solution étape par étape en créant des paramètres et des fonctions réutilisables. L’idée, c’est de faire les choses proprement dès le départ pour faciliter la maintenance et les évolutions futures.

1. Configuration des paramètres globaux Power Query

Commençons par créer des paramètres qui vont centraliser toutes nos configurations. Comme ça, si vous devez changer l’URL de l’API ou vos identifiants, vous n’aurez qu’un seul endroit à modifier !

Comment faire :

  1. Dans l’Éditeur Power Query, allez dans l’onglet Accueil

  2. Cliquez sur Gérer les paramètres > Nouveau paramètre

  3. Créez les paramètres suivants :

    • API_BASE_URL :
    • API_LOGIN_USERNAME :
      • Description : Votre nom d’utilisateur
      • Type : Texte
      • Valeur : emilys
    • API_LOGIN_PASSWORD :
      • Description : Votre mot de passe
      • Type : Texte
      • Valeur : emilyspass
    • API_PAGINATION_SKIP :
      • Description : Point de départ pour la pagination
      • Type : Nombre décimal
      • Valeur : 0
    • API_PAGINATION_LIMIT :
      • Description : Nombre d’éléments par page
      • Type : Nombre décimal
      • Valeur : 20
Paramètres power query

2. Fonctions utilitaires pour les requêtes HTTP

Maintenant, on va créer quatre fonctions “briques de base” qui vont nous simplifier la vie pour tous nos appels API. Ces fonctions vont gérer les en-têtes, les paramètres de requête, et les appels HTTP eux-mêmes.

Comment créer une fonction :

  1. Dans l’Éditeur Power Query, onglet Accueil
  2. Cliquez sur Nouvelle source > Requête vide
  3. Ouvrez l’éditeur avancé
  4. Collez le code de la fonction
  5. Donnez-lui un nom explicite

2.1 GetHeaderParameters : Préparer les en-têtes HTTP

Cette fonction configure les en-têtes de base nécessaires pour nos requêtes HTTP. Elle définit par défaut le type de contenu en JSON et peut accepter des en-têtes supplémentaires si besoin.

GetHeadersParameters
let
GetHeadersParameters = (optional Headers as record) =>
let
Headers = Record.Combine({
[#"Content-Type" = "application/json"],
(Headers ?? [])
})
in
Headers
in
GetHeadersParameters

Elle crée un enregistrement avec Content-Type: application/json et fusionne avec d’éventuels en-têtes supplémentaires que vous passez en paramètre.

2.2 GetQueryParameters : Gérer les paramètres de pagination

Cette fonction prépare les paramètres d’URL pour la pagination. Elle utilise les paramètres globaux qu’on a définis plus tôt.

GetQueryParameters
let
GetQueryParameters = (optional Skip as number, optional Query as record) =>
let
Query = Record.Combine({
[
skip = Number.ToText(Skip ?? API_PAGINATION_SKIP),
limit = Number.ToText(API_PAGINATION_LIMIT)
],
(Query ?? [])
})
in
Query
in
GetQueryParameters

Elle construit un objet avec les paramètres skip et limit pour la pagination, et peut inclure d’autres paramètres si vous en passez.

2.3 HttpClientGetRequest : Effectuer les requêtes GET

Voici notre fonction pour les requêtes GET. Elle utilise Web.Contents de Power Query pour faire l’appel HTTP.

HttpClientGetRequest
let
HttpClientGetRequest = (
RelativePath as text,
optional Query as record,
optional Headers as record
) =>
let
Options = [
RelativePath = RelativePath,
Query = (Query ?? []),
Headers = (Headers ?? [])
],
RawData = Web.Contents(API_BASE_URL, Options),
Json = Json.Document(RawData)
in
Json
in
HttpClientGetRequest

Elle combine l’URL de base avec le chemin relatif, ajoute les paramètres de requête et les en-têtes, fait l’appel HTTP, et retourne le résultat en JSON.

2.4 HttpClientPostRequest : Effectuer les requêtes POST

Même principe que pour GET, mais pour les requêtes POST qui incluent un corps de requête.

HttpClientPostRequest
let
HttpClientPostRequest = (
RelativePath as text,
optional Query as record,
optional Headers as record,
optional Body as record
) =>
let
Options = [
RelativePath = RelativePath,
Query = (Query ?? []),
Headers = (Headers ?? []),
Content = Json.FromValue(Body ?? [])
],
RawData = Web.Contents(API_BASE_URL, Options),
Json = Json.Document(RawData)
in
Json
in
HttpClientPostRequest

Pareil que GET, mais avec un paramètre Body en plus. Le fait d’ajouter Content à l’option transforme automatiquement la requête en POST.

Note

Pourquoi deux fonctions séparées ?

Vous vous demandez peut-être pourquoi je ne fais pas une seule fonction générique qui gère GET et POST ? Bonne question ! J’ai essayé, et ça fonctionne… mais ça pose un problème.

Cliquez pour voir la fonction générique et comprendre le problème
HttpClient
let
HttpClient = (
RelativePath as text,
optional Query as record,
optional Headers as record,
optional Body as record
) =>
let
GetOptions = [
RelativePath = RelativePath,
Query = (Query ?? []),
Headers = (Headers ?? [])
],
PostOptions = [
RelativePath = RelativePath,
Query = (Query ?? []),
Headers = (Headers ?? []),
Content = Json.FromValue(Body ?? [])
],
Options = if Body <> null and Record.FieldCount(Body) > 0
then PostOptions
else GetOptions,
RawData = Web.Contents(API_BASE_URL, Options),
Json = Json.Document(RawData)
in
Json
in
HttpClient

Le problème : Power Query considère les options de Web.Contents comme dynamiques (à cause du if), et ça génère un avertissement dans les paramètres de source de données. Résultat ? Vous ne pourrez pas planifier le rafraîchissement automatique dans le Power BI Service !

Data source settings

C’est pour ça que j’ai préféré séparer en deux fonctions distinctes. Si vous avez une meilleure solution, je suis tout ouïe dans les commentaires !


3. Gestion de l’authentification

Maintenant qu’on a nos fonctions de base, occupons-nous de l’authentification JWT.

3.1 GetJwtTokens : Obtenir les jetons d’accès

Cette fonction se connecte à l’API et récupère nos précieux jetons.

GetJwtTokens
let
GetJwtTokens = () =>
let
Headers = GetHeadersParameters(),
Query = [],
Body = [
username = API_LOGIN_USERNAME,
password = API_LOGIN_PASSWORD
],
Response = HttpClientPostRequest("auth/login", Query, Headers, Body),
JwtTokens = [
accessToken = Record.Field(Response, "accessToken"),
refreshToken = Record.Field(Response, "refreshToken")
]
in
JwtTokens
in
GetJwtTokens

Elle envoie une requête POST avec vos identifiants à /auth/login et extrait les jetons d’accès et de rafraîchissement de la réponse.

3.2 GetHeadersParametersWithAccessToken : Ajouter le jeton aux en-têtes

Cette fonction combine tout ce qu’on a fait jusqu’ici : elle récupère le jeton et l’ajoute aux en-têtes HTTP.

GetHeadersParametersWithAccessToken
let
GetHeadersParametersWithAccessToken = (optional Headers as record) =>
let
JwtTokens = GetJwtTokens(),
accessToken = Record.Field(JwtTokens, "accessToken"),
Headers = Record.Combine({
GetHeadersParameters((Headers ?? [])),
[Authorization = Text.Format("Bearer #{0}", {accessToken})]
})
in
Headers
in
GetHeadersParametersWithAccessToken

Elle appelle GetJwtTokens(), récupère l’access token, et l’ajoute dans un en-tête Authorization: Bearer token. Comme ça, toutes vos requêtes authentifiées sont prêtes !


4. Récupération des données et gestion de la pagination

On arrive au cœur du système : la fonction qui va tout orchestrer pour récupérer les données, page par page.

FetchDataFromApi
let
FetchDataFromApi = (
RelativePath as text,
ResultsFieldName as text,
TotalFieldName as text
) =>
let
// Fonction interne : récupère le nombre total d'éléments
RetrieveTotalCount = (FieldName as text) =>
let
ApiResponse = HttpClientGetRequest(
RelativePath,
GetQueryParameters(),
GetHeadersParametersWithAccessToken()
),
TotalCount = Record.Field(ApiResponse, FieldName)
in
TotalCount,
// Fonction interne : récupère une page spécifique
FetchPage = (FieldName as text, PageIndex as number) =>
let
Offset = (PageIndex ?? 0) * API_PAGINATION_LIMIT,
QueryParams = GetQueryParameters(Offset),
AuthHeaders = GetHeadersParametersWithAccessToken(),
ApiResponse = HttpClientGetRequest(
RelativePath,
QueryParams,
AuthHeaders
),
Results = Record.Field(ApiResponse, FieldName)
in
Results,
// Fonction principale : récupère toutes les pages
GetAllData = () =>
let
TotalCount = RetrieveTotalCount(TotalFieldName),
MaxCount = List.Max({API_PAGINATION_LIMIT, TotalCount}),
TotalPages = Number.RoundUp(MaxCount / API_PAGINATION_LIMIT),
PageIndices = {0..TotalPages - 1},
AllPages = List.Transform(
PageIndices,
each FetchPage(ResultsFieldName, _)
),
CombinedPages = List.Union(AllPages),
DataTable = Table.FromList(
CombinedPages,
Splitter.SplitByNothing(),
null,
null,
ExtraValues.Error
),
ColumnIndices = List.Positions(Table.ColumnNames(DataTable)),
RecordColumnIndex = List.First(
List.Select(
ColumnIndices,
each Type.Is(
Value.Type(Record.FieldValues(DataTable{0}){_}),
type record
)
)
),
RecordColumnName = Table.ColumnNames(DataTable){RecordColumnIndex},
RecordFieldNames = Record.FieldNames(
Record.FieldValues(DataTable{0}){RecordColumnIndex}
),
ExpandedTable = Table.ExpandRecordColumn(
DataTable,
RecordColumnName,
RecordFieldNames,
RecordFieldNames
)
in
ExpandedTable
in
GetAllData()
in
FetchDataFromApi

Je ne vais pas entrer dans les détails de chaque ligne de code, je vous encourage à examiner le code pour bien comprendre son fonctionnement. Mais voici ce que fait cette fonction :

  1. Elle a besoin de trois infos :

    • RelativePath : le chemin de l’API (exemple : “auth/products”)
    • ResultsFieldName : le nom du champ qui contient les résultats (exemple : “products”)
    • TotalFieldName : le nom du champ qui indique le total (exemple : “total”)
  2. Elle compte d’abord : Elle fait un premier appel juste pour savoir combien il y a d’éléments au total. Comme quand vous regardez le nombre de pages d’un livre avant de le lire

  3. Elle récupère page par page : Elle calcule combien de pages il faut télécharger, puis elle va chercher chaque page l’une après l’autre

  4. Elle assemble le tout : Toutes les pages sont combinées en une seule grande liste

  5. Elle nettoie et structure : La liste est transformée en un beau tableau Power Query, avec toutes les colonnes bien dépliées et organisées

En gros, vous lui donnez le chemin de l’API et les noms des champs importants, et elle vous rend un tableau complet avec toutes les données. Magique, non ?


5. Organisation de vos fonctions

Petit conseil d’organisation : créez des dossiers dans Power Query pour ranger vos paramètres et fonctions. Ça aide vraiment à maintenir un projet propre !

Voici comment je structure mes projets par exemple :

Project file structure

Importation des données dans Power BI (Utilisation de la fonction FetchDataFromApi)

Maintenant que tout est en place, voyons à quel point c’est simple d’utiliser notre travail !

Récupérer les produits

Créez une nouvelle requête vide et collez ce code :

Products
let
Source = FetchDataFromApi("auth/products", "products", "total")
in
Source

C’est tout ! Vraiment, c’est tout.

Powerquery code for products query

Cliquez sur Terminer et admirez vos données qui se chargent :

Products query

Récupérer les utilisateurs

Même principe, c’est tout aussi simple :

Users
let
Source = FetchDataFromApi("auth/users", "users", "total")
in
Source

Vous voyez ? Une fois que vous avez fait le travail de base, ajouter de nouvelles sources de données devient un jeu d’enfant !

Tip

Visualisez vos dépendances

Power Query propose une vue super pratique appelée “Dépendances de requêtes” qui vous montre graphiquement comment toutes vos fonctions et requêtes sont connectées. C’est parfait pour :

  • Comprendre la structure de votre projet d’un coup d’œil
  • Vérifier que tout est bien relié
  • Repérer d’éventuels problèmes de dépendance

Pour y accéder : Onglet Affichage > Dépendances de requêtes

Query Dependencies

Conclusion

Voilà, on a fait le tour !

Dans cet article, on a vu ensemble comment :

  • Structurer proprement un projet Power Query avec des paramètres et des fonctions réutilisables
  • Gérer l’authentification JWT avec une API
  • Récupérer automatiquement toutes les pages de données grâce à la pagination
  • Transformer tout ça en tableaux propres et prêts à l’emploi dans Power BI

Le gros avantage de cette approche, c’est que maintenant vous avez un système réutilisable. Besoin de connecter une nouvelle API ? Vous n’avez qu’à adapter les paramètres et les chemins, le reste est déjà fait !

Quelques conseils pour aller plus loin :

  • Adaptez le code à vos propres API (elles fonctionnent toutes un peu différemment)
  • Ajoutez de la gestion d’erreurs si nécessaire
  • Pensez à documenter vos fonctions avec des commentaires
  • Testez toujours

Si vous avez apprécié cette approche basée sur des fonctions réutilisables, vous allez adorer ces deux autres articles qui suivent la même philosophie :

🔗 Gestion flexible des sources de données
Vous gérez des tableaux de bord qui doivent fonctionner sur différents environnements ? Découvrez comment adapter dynamiquement vos connexions MySQL selon l’environnement (développement, test, production). C’est le complément parfait de cet article si vous travaillez avec des bases de données volumineuses et que vous voulez éviter de reconfigurer manuellement vos sources à chaque fois.

📁 Importer plusieurs fichiers Excel/CSV proprement
Marre de voir votre éditeur Power Query pollué par des dizaines de requêtes quand vous importez plusieurs fichiers ? Je vous montre comment créer une fonction personnalisée pour combiner des fichiers Excel ou CSV de façon élégante.