Quand je manipule des réponses d’API, des exports de base ou des fichiers de logs au format JSON, je préfère rester dans le terminal et aller droit au résultat. jq sert précisément à ça: lire, filtrer, transformer et reformater des données JSON sans écrire un script complet. Je vais ici montrer les usages qui reviennent vraiment en développement logiciel, avec des commandes courtes, des variantes utiles et les erreurs que j’évite systématiquement.
Ce qu’il faut retenir pour manipuler du JSON sans écrire de script
-
jqfonctionne comme un moteur de filtres: il lit un JSON, applique une expression, puis renvoie un résultat. - Les gestes de base sont la lecture de champs, le parcours de tableaux, le filtrage avec
selectet la transformation avecmap. -
-r,-c,-s,--arget--argjsonchangent vraiment l’usage au quotidien. - La qualité des commandes dépend surtout de deux choses: bien citer le filtre dans le shell et garder la structure de données en tête.
- Le projet affiche actuellement
jq1.8.2, et les filtres présentés ici restent valables dans l’usage courant.
Pourquoi jq mérite sa place dans la boîte à outils backend
La documentation officielle décrit jq comme un filtre: il prend une entrée et produit une sortie. C’est exactement ce qui le rend pratique dans un contexte backend, API ou observabilité. J’aime le voir comme le sed du JSON: il ne remplace pas un vrai script quand la logique devient lourde, mais il coupe beaucoup de friction sur les tâches répétitives.
Je m’en sers surtout pour vérifier un payload, extraire un identifiant, comparer deux réponses d’API ou préparer un log avant de le passer à un autre outil. Le fait qu’il soit autonome, rapide à lancer et sans dépendance runtime rend son usage très confortable dans un conteneur, un serveur minimal ou un poste de dev un peu sale. Une fois ce modèle en tête, lire une propriété ou une liste devient presque mécanique.
Lire un JSON sans se perdre dans les crochets
Le point de départ, c’est presque toujours l’accès à une propriété. Ici, je veux éviter les commandes trop magiques: il faut savoir ce qu’on lit, à quel niveau, et si on parle d’un objet ou d’un tableau. Les exemples ci-dessous couvrent 80 % des besoins simples.
Accéder à un champ d’objet
jq '.user.name' utilisateur.json
Cette forme renvoie la valeur du champ name dans l’objet user. Quand la structure est stable, c’est la commande la plus lisible que je connaisse pour extraire une donnée précise. Si le champ est destiné à être réutilisé par un autre outil, je passe souvent en sortie brute avec -r.
jq -r '.user.id' utilisateur.json
Parcourir un tableau
jq '.posts[]' blog.json
Le suffixe [] déroule chaque élément du tableau. C’est l’équivalent mental d’une boucle: jq traite chaque entrée l’une après l’autre, puis affiche le résultat filtré. Pour récupérer seulement certains éléments, je combine ce parcours avec select.
jq '.posts[] | select(.author == "stedolan")' blog.json
Ici, je garde uniquement les billets écrits par un auteur précis. Ce motif est très utile dès qu’on a une réponse d’API un peu bavarde et qu’on veut réduire le bruit sans perdre le contexte. Avec ces bases, on passe naturellement de la lecture à la transformation.
Transformer les données au lieu de seulement les lire
La vraie valeur de jq apparaît quand je cesse de penser “afficher” et que je pense “réécrire”. À ce stade, je ne me contente plus d’extraire un champ: je nettoie une structure, je reconstruis un objet plus petit ou je calcule une valeur dérivée. C’est là que les filtres map, del, to_entries et from_entries deviennent intéressants.
Modifier chaque élément d’un tableau
jq 'map(. + 1)' nombres.json
Chaque nombre du tableau est augmenté de 1, puis jq reconstruit un nouveau tableau. Si je travaille plutôt sur un objet, j’utilise souvent map_values pour appliquer une transformation à toutes les valeurs.
jq 'map_values(. + 1)' stats.json
Supprimer ou réordonner des clés
jq 'del(.secret)' configuration.json
Cette commande enlève une clé sensible avant de partager un extrait ou de journaliser une réponse. Quand je dois toucher à toutes les clés d’un objet, je passe volontiers par to_entries et from_entries.
jq 'with_entries(.key |= "KEY_" + .)' objet.json
Ce genre de transformation est utile quand je veux standardiser des noms de champs ou préparer une structure pour une étape suivante. Si certaines valeurs peuvent être nulles, je garde aussi sous la main l’opérateur de repli //.
jq 'map_values(. // empty)' objet.json
Ce filtre me sert à écarter silencieusement les valeurs manquantes dans un objet. Pour les commandes destinées à d’autres outils, j’ajoute souvent -c pour obtenir un JSON compact, ou -r pour sortir une simple chaîne. Quand je dois injecter un paramètre depuis le shell, --arg et --argjson évitent les concaténations fragiles. Le passage suivant montre pourquoi ces options font gagner du temps dans les vrais projets.

Exemples concrets pour des API, des logs et des scripts d’intégration
C’est la partie que je garde le plus souvent sous la main, parce qu’elle colle à la réalité d’un projet web ou backend. Les cas ci-dessous reviennent sans cesse: extraire une donnée d’une API, formater un log JSONL, compter des éléments ou alimenter un script de déploiement. Là où jq devient franchement rentable, c’est quand une seule ligne remplace un petit script jetable.
Lire une réponse d’API
curl -s API_URL | jq '.data[] | {id, name, email}'
Je transforme la réponse en objets plus lisibles, avec uniquement les champs utiles. Si je veux récupérer une liste d’identifiants pour une étape suivante, je bascule en sortie brute.
curl -s API_URL | jq -r '.data[].id'
Nettoyer des logs JSONL
jq -c 'select(.level == "error") | {ts, service, message}' app.log
Ici, -c est important: chaque événement reste sur une seule ligne, ce qui simplifie la lecture dans un terminal ou l’envoi vers un autre outil. Le format JSON Lines est souvent le meilleur terrain de jeu pour jq dans l’observabilité.
Extraire une version ou un paramètre de configuration
jq -r '.version' package.json
Cette commande est assez banale, mais elle sert partout: script de publication, pipeline CI, vérification d’environnement. Quand la valeur vient du shell et doit rester typée, je passe plutôt par --argjson.
jq --argjson threshold 20 'select(.score > $threshold)' users.json
Lire aussi : Gantt en projet logiciel - Planifiez sans improviser !
Grouper et dédupliquer
jq 'group_by(.status) | map({status: .[0].status, count: length})' tickets.json
Ce schéma me sert à compter des éléments par statut sans ouvrir une base de données ni écrire un traitement séparé. Pour une liste où les doublons posent problème, unique_by est souvent suffisant.
jq 'unique_by(.email)' users.json
Ces exemples couvrent des besoins très différents, mais ils reposent tous sur le même réflexe: réduire, remodeler puis transmettre. Reste maintenant le point qui fait trébucher le plus de gens au début.
Les pièges que je vois le plus souvent
La plupart des erreurs viennent moins de jq lui-même que du shell, de la forme des données ou d’un mauvais choix de filtre. Je préfère les nommer clairement, parce qu’ils reviennent partout et qu’ils coûtent du temps pour rien.
| Piège | Ce qui se passe | Le réflexe que j’adopte |
|---|---|---|
| Oublier les quotes autour du filtre | Le shell interprète une partie de l’expression | Je mets presque toujours le filtre entre apostrophes simples |
| Confondre JSON et sortie brute | Une chaîne garde ses guillemets ou un autre outil la lit mal | J’ajoute -r quand je veux un texte pur |
Utiliser map au lieu de select
|
Je transforme tout alors que je voulais seulement filtrer | Je réserve select au filtrage d’éléments |
| Supposer qu’un champ existe toujours | La commande casse sur une structure incomplète | J’utilise ?, // ou try...catch selon le cas |
Charger un gros flux avec -s
|
Tout est mis en mémoire avant traitement | Je n’emploie -s que quand l’agrégation justifie ce coût |
Quand une donnée peut manquer, je préfère des expressions comme jq '.user.email // "inconnu"' profil.json ou jq '.items[]? | .price' catalog.json plutôt qu’un traitement fragile. Et si je veux vraiment ignorer silencieusement l’absence d’une valeur, empty reste plus propre qu’un bricolage autour de null. Avec ces réflexes, jq reste rapide, lisible et sûr même dans un script simple.
Le petit jeu de commandes que je garde sous la main
Si je devais réduire jq à une poignée de commandes, je garderais celles-ci en tête. Elles couvrent la validation, l’extraction, le compactage, la transformation et l’agrégation sans m’obliger à relire la documentation à chaque fois.
-
jq '.'pour valider et indenter un JSON rapidement. -
jq -r '.version' package.jsonpour sortir une valeur réutilisable par un autre outil. -
jq -c '.items[] | {id, status}'pour produire des lignes compactes adaptées aux logs ou au JSON Lines. -
jq 'group_by(.status) | map({status: .[0].status, count: length})'pour compter des éléments dans un tableau. -
jq --argjson threshold 20 'select(.score > $threshold)'pour filtrer avec un paramètre externe sans concaténation fragile.
Ce socle suffit déjà pour la majorité des usages que je rencontre sur des projets web, backend ou d’automatisation. Le plus rentable, ensuite, n’est pas d’apprendre cinquante filtres de plus, mais de combiner proprement ceux que vous utilisez déjà, avec des commandes courtes que vous pourrez relire sans effort.