0.3.0-rc.2 • Public • Published


    Twitch is cool. Games are fun, streaming is fun.

    The Twitch API is a mess. They're onto "Helix" now, as if v3 and v5 weren't enough, and Helix is not only incomplete but v5 is scheduled to be shut down at the end of 2018. So that's fun. And while the rest of the world has mostly moved on to providing OpenAPI/Swagger docs or at least generating clients for you like Twitch's own parent company does with the AWS/Seahorse stuff, Twitch is happy to make its end users make clients for their chaotic APIs.

    It's no real surprise, then, that Twitch API clients are in the state they're in. People have done their best, but the quality of the existing API clients is hit-or-miss and they're all an exercise in frustration. You might be able to get completeness, for v5 anyway, but even something as simple as "get game by ID" requires Helix and on top of that you get callback hell or singletons or any number of other pains in our collective ass.

    So, fine. Let's make a Twitch client that doesn't suck. I think this is a good start.


    • 0.3.0-rc.1: updated TypeScript to 3.4.4 and rebuilt with declarations enabled in tsconfig.json (whoops).


    twitch-better-api is designed for use with Node. I don't use older versions of Node, so I've specified a minimum version of 8.0.0; this may migrate upward over time. If you encounter bugs using a version of Node prior to 8.0.0, I will accept pull requests but will otherwise not spend time addressing issues filed to that effect.

    twitch-better-api expects, as with any good application, to be used in a context where Bunyan logging is used. It will create a Bunyan logger if you don't give it one. If you use Winston, that's fine, but it's up to you to add a Winston stream to the Bunyan logger.

    twitch-better-api isn't intended for use in a browser, but can probably be made to work in a browser without too much effort.


    Normal NPM package rules apply.

    npm install @eropple/twitch-better-api
    yarn add @eropple/twitch-better-api

    twitch-better-api adheres to semver.


    twitch-better-api tries not to be prescriptive about how you use it. Unlike a lot of existing API clients, it doesn't require you to deal with singletons and it tries to handle its own business as much as possible.


    Options are verified with Joi; if something's received that twitch-better-api doesn't understand, it should scream. If there is an error related to options that you've set that isn't caught by Joi, please open an issue.

    • scopes: a list of scopes that should be requested. If for some reason the scopes returned by the Twitch API differ, an error will be thrown.


    At its core, all of the connect methods in the API return an object that inherits from BaseAuth. You can extend this yourself (if, say, you want to get a Twitch API key from a remote data source and periodically refresh it), but the two main ways of using the API are as follows:

    • export async function connectAsApp(oauthClientId, oauthClientSecret, logger, userOptions)
    • export async function connectWithUserAccessToken(oauthToken, logger, userOptions)

    Both should be self-explanatory if you've read the Twitch documentation on auth; if not, I suggest that you do as a basic understanding of OAuth is helpful even when using twitch-better-api.

    You may not use connectAsApp if the library detects that you're in a browser. This is bad-and-wrong. Besides, you're probably going to want to be able to act as a client; to that end, you'll need the connectWithUserAccessToken flow anyway. To use the client token method on a server, I recommend using TwitchApps' TokenGen to acquire a token.

    Anyway, once you've called one of those (or the connect method that they just wrap), you'll receive a Session object. You'll make your API calls on this object. It includes the aforementioned BaseAuth-derived access token handlers; if you're using the app token workflow, AppTokenAuth will periodically refresh your token behind the scenes. Client tokens (using StaticTokenAuth) are not so lucky, but they do last for about 60 days so the rollover isn't too bad. If you are using twitch-better-api as part of a web app, you can just go back into your OAuth flow; if you're using it in something like a broadcaster suite, 60 days is probably good enough for a manual token refresh.

    Getting Stuff Done

    The Easy Way


    Simple, friendly, asynchronous, Promise-based API calls. API calls are grouped into Categories, which are found in src/operations. These categories are then exposed inside of Session.

    As a rule:

    • Methods that accept a parameters object do so expecting that you pass in the params described by the method that underlies it. Consult the Twitch API docs if you're not sure what to pass.
    • The API methods do the right thing as far as data envelopes go. GET requests will pass parameters as a query string. POST, PUT, and PATCH requests will pass parameters as a JSON body.
    • If you request something by ID, i.e. Session.games.getGamesById, you will receive an object in return which contains ID to item mappings. If you request something by name, you'll receive an object with name to item mappings.
    • Cursors act differently. See below.

    Twitch has three, count-'em-three, different implementations of cursors: two in Kraken and one in Helix. This may seem preposterous at first, second, third, and forty-ninth glance, I agree. It does not get less preposterous when you reach the two hundred and eighth.

    But I digress.

    Fortunately, twitch-better-api has your back. Each cursor type is wrapped into a unified interface which presents the following API:

    • async Cursor.next(): fetches another page of data from the API and returns it. That data can also be fetched from Cursor.data afterwards. Will return null if the query has been exhausted.
    • Cursor.started: true if data has been requested via this cursor.
    • Cursor.total: an integer representing the total number of items available, or null if unavailable/inappropriate for the call.
    • Cursor.error: If a call to Twitch has thrown an error, the details will be stored here. A cursor where error is non-null is broken; further calls to Cursor.next() will throw the same error object.
    • Cursor.data: the current batch of data fetched. null before the first call to Cursor.next().

    The workflow for a Cursor is pretty simple: await on Cursor.next() in a loop (or do the moral equivalent if you're pre-async/await) and collect data until you're satisfied or until you hit a null, signaling the end of the cursor's data set.

    The Hard Way

    If there's a call you need access to that isn't made available, we've got an option for that. Two properties exist, Session.helix and Session.kraken, which will yield to you properly authenticated clients for the Helix and the v5 APIs respectively. There are also helper methods, Session.helixCall and Session.krakenCall, that make this a little less gross.

    Before you do this in your own code, however, please consider writing an operation that presents a nice interface for the problem you're trying to solve; somebody else will probably need it someday and it's a good way to contribute back.

    Don't try to save and reuse helix or kraken objects outside of a given method. They have a copy of the OAuth access token and will not be valid if the API client refreshes its access token. Just use the property and it'll be taken care of.

    The API uses Axios under the hood, so every call to, say, Session.helix.get returns a Promise.


    twitch-better-api is somewhat tested; the hot paths are tested because I used the tests to build the client, but there are certainly edge and corner cases I haven't dealt with and at least some of the API surface has probably not had eyes on it. Pull requests to improve testing are gratefully solicited. Any pull requests for new functionality should come with tests.

    Since this is effectively an integration test, you'll need to set some environment variables in order to test against Twitch's API. Specifically, the following need to be set:


    These are automatically sourced when appropriate. See env_test_secrets.bash.example (n.b.: env_test_secrets.bash is ignored via .gitignore, because obviously).

    Tests must be non-mutating. Getters are OK--setters are not. In the future we can consider the idea of more in-depth tests; for example, I have the two Twitch accounts tracecomplete and tracecomplete_test, and I wouldn't mind running mutating tests against tracecomplete_test. If you are interested in this, open an issue and we can discuss it.

    Future Work

    • There are a lot of calls not directly wrapped.
    • Retry support is TBD. Backoff/repetition isn't that difficult but it does probably mean unwrapping async/await into Promises and my own applications don't currently need it; my experience strongly leads me to think that most errors are of the 4xx class and the only one that it seems like the client can really help with is 429 Too Many Requests.
    • Not for this gem specifically, but a little OAuth app that can be run on a local machine to get an OAuth token with scopes would be handy instead of relying on TwitchApps's TokenGen.


    This library is released under the terms of the GNU AGPL 3.0 and no later version. Please ensure that you properly understand the terms and conditions to which distribution of the AGPL binds you before using this library.

    While I am not particularly big on the FSF's definition of Free, I am a little annoyed that the poor quality of API support by Twitch necessitated going out of my way and building this, so I selected the AGPL to safeguard open access to the library. I do require the signature of a Contributor License Agreement before contributing to the project.

    Non-AGPL licensure is available, as is custom feature work. Contact me at ed+twitch-better-api@edropple.com for details.




    npm i @eropple/twitch-better-api

    DownloadsWeekly Downloads






    Unpacked Size

    510 kB

    Total Files


    Last publish


    • eropple