Aller au contenu
twitch.apiv2.0

Bases

Runtimes

Où la bibliothèque fonctionne (Node.js, Bun, Deno, Cloudflare Workers, navigateurs), comment elle est empaquetée, et les options qui remplacent fetch, WebSocket et les URL de Twitch.

L’entrée principale de la bibliothèque n’utilise que des API standard du web : fetch, AbortSignal, URL, Web Crypto et, pour l’écouteur WebSocket, le WebSocket global. Elle n’importe aucun module intégré de Node.js et n’a aucune dépendance : le même code fonctionne donc sur des serveurs, sur des plateformes edge et dans des navigateurs.

Voici l’intégralité d’un Cloudflare Worker qui répond avec un profil Twitch. Les trois mêmes lignes de code client fonctionnent dans tous les runtimes ci-dessous.

src/worker.ts
import { Twitch } from "@ix-xs/twitch.api";
interface Env {
TWITCH_CLIENT_ID: string;
TWITCH_CLIENT_SECRET: string;
}
let twitch: Twitch | undefined;
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Créé une fois, puis réutilisé par les requêtes suivantes.
twitch ??= new Twitch({ clientId: env.TWITCH_CLIENT_ID, clientSecret: env.TWITCH_CLIENT_SECRET });
const login = new URL(request.url).searchParams.get("login") ?? "twitchdev";
const user = await twitch.users.getByLogin(login);
return user ? Response.json(user) : new Response("No such user", { status: 404 });
},
};
RuntimeAppels à l’APIÉvénements par WebSocketÉvénements par webhookFileTokenStore
Node.js 22 ou ultérieurOuiOuiOuiOui
BunOuiOuiOuiOui
DenoOuiOuiOuiOui
Cloudflare WorkersOuiUtilisez les webhooksOuiPas de système de fichiers
NavigateursOui, sans secret clientOuiPas de serveur à appelerPas de système de fichiers

Node.js 22 est le minimum, et le champ engines du paquet l’indique. C’est la première version qui fournit fetch, AbortSignal.any et WebSocket comme globaux sans option.

Fenêtre de terminal
npm install @ix-xs/twitch.api

Installez le même paquet depuis npm. Les imports ne changent pas.

Fenêtre de terminal
bun add @ix-xs/twitch.api
deno add npm:@ix-xs/twitch.api

Les deux runtimes implémentent le module de système de fichiers de Node.js : FileTokenStore y fonctionne donc aussi. Avec Deno, autorisez l’accès au réseau quand vous lancez votre programme, ainsi que l’accès aux fichiers si vous utilisez ce magasin de jetons.

Créer un client n’envoie aucune requête : vous pouvez donc sans risque en créer un à la première requête et le garder dans une variable de module, comme dans l’exemple ci-dessus. Trois choses diffèrent d’un serveur :

  • Rien ne survit longtemps en mémoire. Gardez les jetons utilisateur dans un magasin de jetons adossé à KV ou à une base de données, voir Stocker les jetons.
  • Un Worker ne reste pas en vie entre deux requêtes : il ne peut donc pas garder ouvert un écouteur WebSocket. Recevez les événements avec les webhooks, page qui présente aussi les deux réglages dont un Worker a besoin.
  • Il n’y a pas de système de fichiers. N’importez pas @ix-xs/twitch.api/node.

La bibliothèque fonctionne dans un navigateur, avec une règle absolue : le secret client n’y entre pas. Créez le client avec l’identifiant client seul et connectez l’utilisateur avec le flux par code d’appareil, qui ne demande aucun secret. Les appels sont alors faits avec le jeton de cet utilisateur.

import { Twitch } from "@ix-xs/twitch.api";
const twitch = new Twitch({ clientId: "your-client-id" });

L’option userAgent n’a aucun effet dans les navigateurs, qui ne laissent pas une page définir cet en-tête.

Le paquet est livré dans les deux formats, chacun avec ses propres déclarations de types. import et require aboutissent au bon format grâce à la table exports du paquet.

import { Twitch } from "@ix-xs/twitch.api";
const { Twitch } = require("@ix-xs/twitch.api");

TypeScript trouve les types de lui-même avec les résolutions de modules node16, nodenext et bundler. Il n’y a aucun paquet @types à installer. Le paquet est déclaré sans effets de bord : un bundler ne conserve donc que les parties que vous importez.

@ix-xs/twitch.api/node est un second point d’entrée pour les utilitaires qui ont besoin des modules intégrés de Node.js. Il n’en contient qu’un aujourd’hui : FileTokenStore, qui enregistre les jetons utilisateur dans un fichier JSON.

import { Twitch } from "@ix-xs/twitch.api";
import { FileTokenStore } from "@ix-xs/twitch.api/node";
const twitch = new Twitch({
clientId,
clientSecret,
tokens: new FileTokenStore("./tokens.json"),
});

Il est séparé afin que l’entrée principale ne mentionne jamais node:fs. Un bundle destiné à un Worker ou à un navigateur se construit ainsi sans polyfills ni configuration particulière. Voir Stocker les jetons pour le magasin de jetons lui-même.

L’option fetch remplace la fonction derrière chaque requête HTTP que fait le client, vers l’API Twitch comme vers le serveur OAuth. Utilisez-la pour passer par un proxy, pour ajouter de l’instrumentation ou pour répondre depuis la mémoire dans des tests.

import { Twitch } from "@ix-xs/twitch.api";
const twitch = new Twitch({
clientId,
clientSecret,
fetch: async (input, init) => {
const started = performance.now();
const response = await fetch(input, init);
console.log(response.status, String(input), `${Math.round(performance.now() - started)} ms`);
return response;
},
});

Dans un test, un faux fetch tient lieu de Twitch. Rappelez-vous que le client demande un jeton d’application au serveur OAuth avant son premier appel :

import { Twitch } from "@ix-xs/twitch.api";
const fakeFetch: typeof fetch = async (input) => {
const url = new URL(input instanceof Request ? input.url : input);
if (url.hostname === "id.twitch.tv") {
return Response.json({ access_token: "test-token", expires_in: 3600, token_type: "bearer" });
}
if (url.pathname === "/helix/users") {
return Response.json({ data: [{ id: "1", login: "someone", display_name: "Someone" }] });
}
return Response.json({ data: [] });
};
const twitch = new Twitch({ clientId: "test", clientSecret: "test", fetch: fakeFetch });
const user = await twitch.users.getByLogin("someone");

Sans cette option, le client appelle le fetch global, et le recherche à chaque requête. Un outil de test qui remplace globalThis.fetch après la création du client est donc pris en compte. Sur un runtime sans aucun fetch, le constructeur lève une TwitchError qui vous demande d’en passer un.

Les écouteurs WebSocket et conduit ouvrent leurs connexions avec le WebSocket global. L’option webSocket prend une autre classe à la place : celle d’un paquet sur un runtime qui n’a pas de global, ou une fausse classe dans des tests.

import type { WebSocketConstructor } from "@ix-xs/twitch.api";
// Par exemple la classe exportée par le paquet "ws".
declare const WebSocketClass: WebSocketConstructor;
const events = twitch.eventsub.websocket({ webSocket: WebSocketClass });

La classe doit pouvoir être construite avec une URL et exposer onopen, onmessage, onclose, onerror et close(). Sans global et sans l’option, la création de l’écouteur lève une TwitchEventSubError. Les autres options de l’écouteur sont décrites dans WebSocket.

Deux options changent la destination des requêtes :

OptionPar défautUtilisée pour
baseUrlhttps://api.twitch.tv/helixTous les appels à l’API.
authUrlhttps://id.twitch.tv/oauth2Les jetons d’application, les flux de connexion, le rafraîchissement, la validation et la révocation des jetons.

Leur usage principal est le serveur de simulation de la Twitch CLI, qui imite l’API sur votre machine avec des utilisateurs et des streams générés. Lancez-le avec twitch mock-api start, prenez l’identifiant et le secret de l’un de ses clients sur http://localhost:8080/units/clients, et créez le client en le dirigeant vers ce serveur :

import { Twitch } from "@ix-xs/twitch.api";
const twitch = new Twitch({
clientId: process.env.MOCK_CLIENT_ID!,
clientSecret: process.env.MOCK_CLIENT_SECRET,
baseUrl: "http://localhost:8080/mock",
authUrl: "http://localhost:8080/auth",
});
const streams = await twitch.streams.get().take(5);

Le jeton d’application est demandé au serveur de simulation comme au vrai. Le serveur de simulation distribue les jetons utilisateur par un endpoint qui lui est propre, décrit dans sa documentation. Enregistrez un tel jeton avec son propriétaire et ses scopes, afin que le client n’ait pas à interroger le serveur à son sujet :

declare const mockUserToken: string;
await twitch.auth.addUser({
accessToken: mockUserToken,
userId: "141981764",
scopes: ["moderator:manage:banned_users"],
});

L’écouteur WebSocket a une option url correspondante pour le serveur EventSub de la Twitch CLI, voir WebSocket.