12k
All articles

Utilisation du client Redis intégré à Bun

Guide du client Redis intégré de Bun : connexion, commandes typées, Pub/Sub, auto-pipelining, limitation de débit et passage à ioredis.

OpenReplay Team
OpenReplay Team
Utilisation du client Redis intégré à Bun

Bun embarque un client Redis natif importable directement — import { redis, RedisClient } from "bun" — de sorte que pour la plupart des charges de travail liées au cache, à la gestion de sessions, au pub/sub et à la limitation de débit, vous n’avez plus besoin d’ajouter ioredis ou node-redis. Le client Redis natif de Bun dispose d’une API basée sur les Promises avec une gestion intégrée des connexions, des réponses entièrement typées et la prise en charge de TLS ; il est compatible avec les versions Redis 7.2 et supérieures. Ce guide porte spécifiquement sur ce client intégré — et non sur ioredis exécuté sous Bun, ce que la plupart des tutoriels « Redis avec Bun » illustrent en réalité, sans le préciser.

La distinction est importante. Un schéma récurrent dans les tutoriels existants sur Bun et Redis consiste à exécuter bun add ioredis puis à appeler new Redis() — il s’agit là de la bibliothèque tierce, et non de l’API du runtime. Tout ce qui suit utilise l’API fournie directement par Bun.

Points clés à retenir

  • Importez le client avec import { redis, RedisClient } from "bun" ; l’instance redis par défaut lit REDIS_URL, puis VALKEY_URL, et se replie sur redis://localhost:6379 si aucune des deux n’est définie.
  • Le client n’ouvre aucune connexion avant l’exécution de la première commande, pipeline automatiquement les commandes et se reconnecte avec un backoff exponentiel démarrant à 50 ms, plafonné à 2 000 ms, sur un maximum de 10 tentatives par défaut.
  • À partir de Bun 1.3, le client encapsule 66 commandes Redis sous forme de méthodes typées ; redis.send(command, argsArray) permet d’exécuter toute commande non encapsulée — c’est également ainsi que vous émettez MULTI/EXEC aujourd’hui.
  • Le Pub/Sub monopolise une connexion ; appelez .duplicate() pour obtenir une seconde connexion dédiée à la publication ou à l’exécution d’autres commandes.
  • À partir de Bun 1.3.x, le client intégré ne prend pas en charge Redis Cluster ni Sentinel ; pour ces cas d’usage, tournez-vous vers node-redis ou ioredis.

Connexion avec le client Redis intégré

La voie la plus rapide est l’export redis par défaut, qui se connecte de façon paresseuse à partir des variables d’environnement. Par défaut, le client lit les informations de connexion depuis REDIS_URL, puis VALKEY_URL ; si aucune n’est définie, il se replie sur redis://localhost:6379. Aucun socket n’est ouvert tant que vous n’émettez pas la première commande.

import { redis, RedisClient } from "bun";

// Client par défaut — lit REDIS_URL / VALKEY_URL depuis l'environnement
await redis.set("hello", "world");
const result = await redis.get("hello");

// Client personnalisé avec une URL explicite
const client = new RedisClient("redis://username:password@localhost:6379");
await client.set("counter", "0");
await client.incr("counter");

Utilisez l’instance redis par défaut pour un accès à l’échelle de l’application ; instanciez votre propre RedisClient lorsque vous avez besoin d’une connexion distincte, d’options spécifiques ou d’une base de données différente. La documentation Redis de Bun répertorie l’ensemble des schémas d’URL pris en charge, notamment rediss:// et redis+tls:// pour TLS, ainsi que redis+unix:// pour les sockets Unix.

La gestion des connexions est prise en charge automatiquement :

  • Aucune connexion n’est établie tant qu’une commande n’est pas exécutée ; la première commande initie la connexion, qui reste ouverte pour les commandes suivantes jusqu’à l’appel de client.close().
  • En cas de coupure, le client se rétablit de lui-même : il démarre avec un délai de 50 ms, le double à chaque tentative, plafonne le délai de reconnexion à 2 000 ms et effectue jusqu’à maxRetries tentatives (10 par défaut).
  • Les commandes émises pendant une déconnexion sont mises en file d’attente lorsque enableOfflineQueue est à true (valeur par défaut) et rejetées immédiatement lorsqu’il est à false.

Ce sont les mêmes comportements que vous auriez autrement à configurer manuellement avec ioredis.

Opérations principales et le recours à send()

Le client expose des méthodes typées pour les commandes Redis courantes et convertit les réponses en valeurs JavaScript natives, vous évitant ainsi la plupart des analyses manuelles :

  • les réponses entières sont renvoyées sous forme de nombres JavaScript ;
  • les chaînes bulk et simples sous forme de chaînes de caractères ;
  • les chaînes bulk nulles sous forme de null ;
  • les tableaux sous forme de tableaux JavaScript.

Une coercition spécifique à certaines commandes est également appliquée : EXISTS renvoie un booléen plutôt qu’un nombre, et SISMEMBER renvoie également un booléen.

Vous pouvez ajuster tout cela via l’objet d’options :

const client = new RedisClient("redis://localhost:6379", {
  connectionTimeout: 5000,     // valeur par défaut : 10000 ms
  autoReconnect: true,         // valeur par défaut : true
  maxRetries: 10,              // valeur par défaut : 10
  enableOfflineQueue: true,    // valeur par défaut : true
  enableAutoPipelining: true,  // valeur par défaut : true
  tls: true,
});
// Chaînes et expiration
await redis.set("session:123", "active");
await redis.expire("session:123", 3600); // en secondes
const ttl = await redis.ttl("session:123");

// Compteurs
await redis.set("counter", "0");
await redis.incr("counter");
await redis.decr("counter");

// exists() renvoie un booléen
const present = await redis.exists("session:123"); // true

// Hashes
await redis.hmset("user:123", ["name", "Alice", "email", "alice@example.com"]);
const [name, email] = await redis.hmget("user:123", ["name", "email"]);

Toutes les commandes ne disposent pas d’une méthode dédiée. L’ensemble des opérations standard sont prises en charge — notamment les hashes, les listes et les ensembles — pour un total de 66 commandes à partir de Bun 1.3. Pour tout ce qui se situe en dehors de cet ensemble, utilisez send(). La méthode send exécute n’importe quelle commande Redis, y compris celles sans méthode dédiée ; le premier argument est le nom de la commande et le second est un tableau d’arguments sous forme de chaînes.

// Recours aux commandes brutes — nom + tableau d'arguments sous forme de chaînes
await redis.send("LPUSH", ["mylist", "value1", "value2"]);
const list = await redis.send("LRANGE", ["mylist", "0", "-1"]);

send() est également la façon dont vous exécutez des transactions aujourd’hui, ce qui constitue la principale lacune de l’API du client, abordée plus loin.

Pub/Sub avec .duplicate()

Le Pub/Sub Redis est pris en charge, mais une connexion abonnée ne peut rien faire d’autre. L’abonnement monopolise la connexion RedisClient : un client ayant des abonnements actifs ne peut appeler que subscribe(). Pour envoyer d’autres commandes, vous devez créer une connexion distincte avec .duplicate(). Le callback d’abonnement reçoit d’abord le message, puis le canal.

import { RedisClient } from "bun";

const redis = new RedisClient("redis://localhost:6379");
await redis.connect();

// Seconde connexion pour la publication / autres commandes
const subscriber = await redis.duplicate();

await subscriber.subscribe("notifications", (message, channel) => {
  console.log(`[${channel}] ${message}`);
});

await redis.publish("notifications", "Hello from Bun!");

Le Pub/Sub a été ajouté dans Bun 1.2.23, et la documentation le signale encore comme expérimental — stable en pratique, mais il vaut la peine d’épingler votre version de Bun si vous en dépendez. Désabonnez-vous avec .unsubscribe() (sans argument pour effacer tous les canaux ; passez un canal ou un listener pour cibler un abonnement spécifique).

Pipelining automatique

Le client intégré effectue le pipelining par défaut — vous n’avez pas à l’activer explicitement. Le client pipeline automatiquement les commandes, améliorant les performances en envoyant plusieurs commandes en lot et en traitant les réponses au fur et à mesure de leur arrivée. Émettez plusieurs commandes avec await en parallèle (par exemple avec Promise.all) et elles partent en un seul lot plutôt qu’en autant d’allers-retours :

const [a, b, c] = await Promise.all([
  redis.get("user:1:name"),
  redis.get("user:2:name"),
  redis.get("user:3:name"),
]);

Le pipelining automatique est là où l’écart de performance avec ioredis se creuse. Bun décrit son client Redis comme significativement plus rapide qu’ioredis, l’avantage s’accentuant à mesure que la taille des lots augmente. Les chiffres publiés et reproductibles de Bun issus des notes de version 1.2.9 le confirment : dans leur benchmark GET, Bun.redis était 44,82 % plus rapide pour des lots de 10, 58,29 % plus rapide pour des lots de 100 et 85,39 % plus rapide pour des lots de 1 000 — soit environ 1,85x pour le plus grand lot, et non un multiple fixe. (Le blog de la version 1.3 de Bun publie par ailleurs un graphique de benchmark indiquant que le client intégré atteint plus de 7,9x le débit d’ioredis — ce chiffre est donc un nombre publié par Bun lui-même, et non une simple reprise secondaire.)

Quelques commandes désactivent le pipelining automatique car elles sont à état — parmi elles AUTH, INFO, MULTI, EXEC, WATCH, SUBSCRIBE et SELECT. Pour désactiver le pipelining globalement, définissez enableAutoPipelining: false.

Patterns appliqués : cache, sessions et limitation de débit

Les patterns ci-dessous sont transposables depuis n’importe quel client Redis ; ici, ils s’exécutent entièrement sur l’API intégrée. Considérez ceci comme un petit module de service autonome.

Cache-aside. Consultez Redis en premier, revenez à la base de données en cas d’absence, puis alimentez le cache avec un TTL.

import { redis } from "bun";

async function getUser(userId: string) {
  const cacheKey = `user:${userId}`;
  const cached = await redis.get(cacheKey);
  if (cached) return JSON.parse(cached);

  const user = await db.getUser(userId);       // votre source de données
  await redis.set(cacheKey, JSON.stringify(user));
  await redis.expire(cacheKey, 3600);          // 1 heure
  return user;
}

Pour le write-through, mettez à jour le cache dans la même opération que l’écriture en base de données. Pour le stale-while-revalidate, servez immédiatement la valeur en cache et actualisez-la en arrière-plan lorsque le TTL approche de son expiration.

Stockage de sessions avec TTL. Stockez les sessions sous forme de hashes et laissez Redis les expirer automatiquement.

async function createSession(userId: number, data: object) {
  const sessionId = crypto.randomUUID();
  const key = `session:${sessionId}`;
  await redis.hmset(key, [
    "userId", String(userId),
    "created", String(Date.now()),
    "data", JSON.stringify(data),
  ]);
  await redis.expire(key, 86400); // 24 heures
  return sessionId;
}

Limitation de débit par fenêtre glissante avec des ensembles triés. Les commandes sur les ensembles triés ne sont pas encapsulées en méthodes dédiées, ce qui en fait un cas d’usage naturel pour send(). Supprimez les entrées antérieures à la fenêtre temporelle, comptez ce qui reste, puis ajoutez la requête courante.

async function rateLimit(id: string, limit = 100, windowMs = 60_000) {
  const key = `ratelimit:${id}`;
  const now = Date.now();

  await redis.send("ZREMRANGEBYSCORE", [key, "0", String(now - windowMs)]);
  const count = Number(await redis.send("ZCARD", [key]));

  if (count >= limit) return { allowed: false, remaining: 0 };

  await redis.send("ZADD", [key, String(now), `${now}-${crypto.randomUUID()}`]);
  await redis.expire(key, Math.ceil(windowMs / 1000));
  return { allowed: true, remaining: limit - count - 1 };
}

Grâce au pipelining automatique du client, la séquence ZREMRANGEBYSCORE/ZCARD/ZADD/EXPIRE est regroupée efficacement en lot, sans nécessiter d’objets pipeline manuels.

Limites, et quand continuer à utiliser ioredis ou node-redis

Le client intégré couvre les cas d’usage courants décrits ci-dessus, mais il présente des lacunes documentées à partir de Bun 1.3.x. Les transactions (MULTI/EXEC) doivent être effectuées via des commandes brutes, et Redis Sentinel ainsi que Redis Cluster ne sont pas pris en charge. Si vous avez besoin du clustering, du basculement via Sentinel ou des modules Redis Stack (recherche, JSON, séries temporelles, structures probabilistes), tournez-vous vers une bibliothèque complète. Le ticket de migration suivi par Bun lui-même recommande node-redis comme client privilégié pour les nouveaux projets, avec ioredis comme alternative établie. Le blog de la version 1.3 indique que la prise en charge des clusters, des streams et des scripts Lua est en cours de développement — consultez donc la section Limitations de la documentation avant de présumer de l’état actuel, car cette surface évolue encore.

Fonctionnalitéredis intégré à Bunnode-redis / ioredis
Étape d’installationAucune — fourni avec Bunbun add node-redis / ioredis
Pipelining automatiqueActivé par défautConfigurable / manuel
Pub/SubOui (expérimental, via .duplicate())Oui
Transactions (MULTI/EXEC)Via send() brut uniquementAPI native
Redis ClusterNon pris en chargePris en charge
Redis SentinelNon pris en chargePris en charge
Modules Redis StackUtiliser une bibliothèquePris en charge par node-redis

Un détail d’implémentation mérite une précision : la documentation décrit le client comme compilé nativement (actuellement caractérisé comme implémenté en Rust, suite au portage de Bun depuis Zig en 2026). Ce détail ne modifie pas une seule ligne de votre code applicatif — la surface d’API est identique quelle que soit la technologie sous-jacente.

Pour le cache, les sessions, le pub/sub, les compteurs et la limitation de débit, le client intégré est suffisant, et l’élimination d’une dépendance constitue un gain réel. Dès que vous avez besoin de Cluster, de Sentinel, de Redis Stack ou de flux transactionnels complexes, installez node-redis. Commencez avec import { redis } from "bun" contre un serveur local, vérifiez que l’ensemble de commandes dont vous avez besoin est couvert, et épinglez votre version de Bun afin qu’une API en évolution ne vous réserve pas de mauvaises surprises en production.

FAQ

Comment exécuter une transaction Redis avec le client intégré de Bun ?

Le client intégré de Bun ne dispose pas d'API native pour MULTI/EXEC ; vous exécutez donc les transactions via la méthode brute send(). Émettez redis.send('MULTI', []), puis vos commandes en file d'attente, puis redis.send('EXEC', []). Étant donné que MULTI, EXEC et WATCH désactivent le pipelining automatique, ils sont envoyés individuellement plutôt qu'en lot. Si vous avez besoin d'une API de transaction native plus riche, node-redis ou ioredis en proposent une.

Le client Redis intégré à Bun fonctionne-t-il avec Redis Cluster ou Sentinel ?

Non. À partir de Bun 1.3.x, le client intégré ne prend pas en charge Redis Cluster ni Redis Sentinel, conformément à la section Limitations de la documentation. Pour le clustering fragmenté, le basculement via Sentinel ou les modules Redis Stack tels que la recherche, JSON et les séries temporelles, utilisez plutôt une bibliothèque complète. Le ticket de migration de Bun recommande node-redis comme client privilégié pour les nouveaux projets, avec ioredis comme alternative établie.

Quelle est la différence entre l'import redis intégré à Bun et l'exécution d'ioredis sous Bun ?

Le client intégré est l'API du runtime accessible via import { redis, RedisClient } from 'bun', sans installation requise et avec le pipelining des commandes activé par défaut. Utiliser ioredis implique bun add ioredis et new Redis(), une bibliothèque tierce avec sa propre gestion des connexions. La plupart des tutoriels « Redis avec Bun » utilisent silencieusement ioredis. Le client intégré ne requiert aucune dépendance ; ioredis ajoute les API natives pour Cluster, Sentinel et les transactions dont le client intégré est dépourvu.

Que se passe-t-il avec les commandes envoyées pendant que le client Redis de Bun est déconnecté ?

Les commandes émises pendant que le client est déconnecté sont mises en file d'attente et rejouées une fois la connexion rétablie, car enableOfflineQueue est à true par défaut. Le client se reconnecte automatiquement en utilisant un backoff exponentiel démarrant à 50 ms, doublant à chaque tentative, plafonné à 2 000 ms, sur un maximum de 10 tentatives par défaut. Définissez enableOfflineQueue à false pour rejeter immédiatement les commandes lors d'une déconnexion plutôt que de les mettre en mémoire tampon.

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.