A small Node.js library to work with Riot's League of Legend's API.

Simple example using promises and callbacks

#### `javascript const _kayn = require('kayn') const Kayn = _kayn.Kayn const REGIONS = _kayn.REGIONS const kayn = Kayn(/* process.env.RIOT_LOL_API_KEY */)(/* optional config */) kayn.Summoner.by .name('Contractz') .region(REGIONS.NORTH_AMERICA) // same as 'na' .callback(function(unhandledError, summoner) { kayn.Matchlist.by .accountID(summoner.accountId) /* Note that region falls back to default if unused. */ .query({ season: 11, champion: 67, }) .then(function(matchlist) { console.log('actual matches:', matchlist.matches) console.log('total number of games:', matchlist.totalGames) }) .catch(console.error) })`

Same example (as the above) using async/await, destructuring, and template strings

#### `javascript import { Kayn, REGIONS } from 'kayn' const kayn = Kayn(/* process.env.RIOT_LOL_API_KEY */)(/* optional config */) const main = async () => { const { accountId } = await kayn.Summoner.by.name('Contractz') // ^ default region is used, which is `na` unless specified in config const { matches, totalGames } = await kayn.Matchlist.by .accountID(accountId) .query({ season: 11, champion: 67 }) .region(REGIONS.NORTH_AMERICA) console.log('actual matches:', matches) console.log(`total number of games: ${totalGames}`) } main()`

# Table of Contents:

- Features
- Methods
- Installation & Usage
- Configuration
- My Project
- Bugs / Changelog / Disclaimer

# Features

## Rate Limiting

Handled by Colorfulstan's wonderful riot-ratelimiter.

## All Endpoints Covered

## Caching

Currently supports a basic JS cache (for simple scripts) and Redis for anything more complicated.

## Compatible with Callbacks, Promises, Async / Await

## TypeScript Support

Works immediately upon installation.

As of v0.8.0, full DTO's are provided thanks to MingweiSamuel's auto-updated Swagger JSON.

# Methods

Check out ENDPOINTS.md to see kayn's methods, as well as the endpoints covered.

## Documentation

The auto-generated ESDoc documentation can be found here.

# Installation and Usage

The minimum required Node.js version is v7.6.0 for native async/await support (there's only a mere line in the codebase, though).

## npm

    npm i --save kayn

## yarn

    yarn add kayn

### Quick Setup with Default Config

    const { Kayn, REGIONS } = require('kayn')
    const kayn = Kayn('RGAPI-my-api-key')(/*{
        region: REGIONS.NORTH_AMERICA,
        debugOptions: {
            isEnabled: true,
            showKey: false,
        },
        requestOptions: {
            shouldRetry: true,
            numberOfRetriesBeforeAbort: 3,
            delayBeforeRetry: 1000,
            burst: false,
        },
        cacheOptions: {
            cache: null,
            timeToLives: {
                useDefault: false,
                byGroup: {},
                byMethod: {},
            },
        },
    }*/)

Note: Any config passed in is deeply merged with the default config.

### Environment Variables

    const kayn = Kayn(/* process.env.RIOT_LOL_API_KEY */)(myConfig)

Although it is possible to manually pass in the API key, it is preferable to store the key in a secret file (which should not be committed).

This allows `kayn` to be constructed like in the above code.

    # filename: .env
    RIOT_LOL_API_KEY=RGAPI-my-api-key

### Callbacks

    kayn.Summoner.by.name('Contractz').callback(function(err, summoner) {
        // do something
    })

### Promises

    kayn.Summoner.by.name('Contractz')
        .then(summoner => doSomething(summoner))
        .then(console.log)
        .catch(error => console.error(error))

### Async / Await

    const main = async () => {
        const ctz = await kayn.Summoner.by.name('Contractz')
    }

### Region

This forces a request to target a specific region instead of the default region set in `kayn`'s config. If `.region()` is not used, `kayn` will use the default region to make requests.

    kayn.Summoner.by.name('hide on bush')
        .region(REGIONS.KOREA)
        .callback(function(error, summoner) {
            doSomething(summoner)
        })

#### Region without Throwing

There is another utility method in case if you want to avoid handling exceptions caused by `.region()`. This method simply catches `.region()`'s exception, and so it will fall back to the default region as well.

    kayn.Summoner.by.name('hide on bush')
        .regionNoThrow(null) // No error thrown. Uses default region.

    kayn.Summoner.by.name('hide on bush')
        .regionNoThrow(3) // Same as above.

    kayn.Summoner.by.name('hide on bush')
        .regionNoThrow('kr524') // Same as above.

### Query Parameters

    kayn.Matchlist.by.accountID(3440481)
        .region(REGIONS.KOREA)
        .query({
            champion: 67,
            season: 9,
        })
        .callback(function(err, matchlist) {
            console.log(matchlist.matches.length)
        })

### Request Errors

Errors as of v0.8.7 return the following error object:

    {
        statusCode: 42, // some random number
        url: '', // the debug URL that is used in logging as well
        error: {} // the rest of the error object
    }

# Configuration

## Request Options

### numberOfRetriesBeforeAbort

Default: 3 attempts.

### delayBeforeRetry

Default: 1000 ms (1 second).

This option will be scrapped in the future in favor for more flexibility (linear, exponential, random, etc).

### burst

Default: false.

Disabled by default in favor of `spread`.

`true` => `riotratelimiter` will use its burst strategy.

`false` => `riotratelimiter` will use its spread strategy.

## Cache Options

To cache, firstly create some cache that implements the `get` and `set` functions that `kayn` interfaces with, and then pass that cache instance to `cacheOptions.cache`.

`ttls` are method ttls. This part is pretty inconvenient right now. Suggestions are welcome.

Current caches:

- basic in-memory cache
- (node) lru-cache
- Redis cache

    import { Kayn, REGIONS, METHOD_NAMES, BasicJSCache, LRUCache, RedisCache } from 'kayn'

    const redisCache = new RedisCache({
        host: 'localhost',
        port: 5000,
        keyPrefix: 'kayn',
        password: 'hello-world',
        // etc...
    })

    const lruCache = new LRUCache({
        max: 500,
        dispose: (key, value) => {},
        length: (value, key) => 1,
    })

    const basicCache = new BasicJSCache()

    const myCache = redisCache // or basicCache/lruCache

    const kayn = Kayn(/* optional key */)({
        region: 'na',
        debugOptions: {
            isEnabled: true,
            showKey: false,
        },
        requestOptions: {
            shouldRetry: true,
            numberOfRetriesBeforeAbort: 3,
            delayBeforeRetry: 1000,
        },
        cacheOptions: {
            cache: myCache,
            timeToLives: {
                useDefault: true,
                byGroup: {
                    STATIC: 1000 * 60 * 60 * 24 * 30, // cache for a month
                },
                byMethod: {
                    [METHOD_NAMES.SUMMONER.GET_BY_SUMMONER_NAME]: 1000, // ms
                },
            },
        },
    })

    kayn.Summoner.by
        .name('Contractz')
        .then(() => kayn.Summoner.by.name('Contractz'))

    /*
    200 @ https://na1.api.riotgames.com/lol/summoner/v3/summoners/by-name/Contractz
    CACHE HIT @ https://na1.api.riotgames.com/lol/summoner/v3/summoners/by-name/Contractz
    */

### TTLs???

Check out `Enums/default-ttls` and `Enums/method-names` to find the constants that you can use as keys within `byGroup` and `byMethod`.

Here is the order in which ttl's resolve (highest-priority first):

1. byMethod
2. byGroup
3. useDefault

Note that if you're using the `ttls` prop before v0.8.9, you're perfectly fine. `ttls` is the source of truth, and has the highest priotity over the above 3 ways.

#### byMethod

`byMethod` takes pairs of `Enums/method-names`, which are just unique (string-type) identifiers and the ttl value you desire.

    cacheOptions: {
        timeToLives: {
            byMethod: {
                [METHOD_NAMES.SUMMONER.GET_BY_SUMMONER_NAME]: 1000,
            }
        }
    }

    // Same as passing this:
    // 'SUMMONER.GET_BY_SUMMONER_NAME': 1000,
    // Constants just help for auto-complete.

#### byGroup

`byGroup` takes pairs as well. The key difference is that it follows how Riot groups their API methods on their developer interface. You can check `Enums/method-names` once again to see how methods are grouped toegher.

`byGroup` has lower priority than `byMethod`. This as you'll see is flexible.

    cacheOptions: {
        timeToLives: {
            byGroup: {
                MATCH: 60 * 60 * 24 * 30, // 30 days
            }
            byMethod: {
                [METHOD_NAMES.MATCH.GET_MATCHLIST]: 1000,
            }
        }
    }

    // Enums/method-names
    /*
    const MATCH = {
        GET_MATCH: 'MATCH.GET_MATCH',
        GET_MATCHLIST: 'MATCH.GET_MATCHLIST',
        GET_RECENT_MATCHLIST: 'MATCH.GET_RECENT_MATCHLIST',
        GET_MATCH_TIMELINE: 'MATCH.GET_MATCH_TIMELINE',
        GET_MATCH_IDS_BY_TOURNAMENT_CODE: 'MATCH.GET_MATCH_IDS_BY_TOURNAMENT_CODE',
        GET_MATCH_BY_TOURNAMENT_CODE: 'MATCH.GET_MATCH_BY_TOURNAMENT_CODE',
    }
    */

What this does is set the cache ttl of every single one of the above match method to 30 days. However, since `byMethod` has higher priority, we are then able to overwrite the `Matchlist.by.accountID` ttl, making it only be cached for a second instead.

This is good because the other match methods rarely change, while matchlists can change every 20 minutes.

#### useDefault

Simply set `useDefault` in `timeToLives` to true. This option basically sets ttls I thought made some sense. `useDefault` has the lowest priority, which means you can set it to `true`, and then overwrite it on a case-by-case basis using `byGroup` and `byMethod`.

### Flushing the Cache

    // BasicJSCache O(1)
    // synchronous
    kayn.flushCache()
    // this has been turned into a promise so that it can be chained.
    // still can just be called normally though.
    // the `data` parameter returns "OK" just like in the RedisCache.
    async1
      .then(() => kayn.flushCache())
      .then(console.log) // prints OK always. there's no way to get an error.
      .catch(console.err)

    // RedisCache O(N)
    // asynchronous
    kayn.flushCache(function (err, ok) {
      console.log(ok === "OK")
    })

    const flush = async () => {
      try {
        await kayn.flushCache() // returns "OK", but not really necessary to store.
      } catch (exception) {
        console.log(exception)
      }
    }

    async1
      .then(() => async2())
      .then(() => kayn.flushCache())
      .then(console.log)
      .catch(console.log)

## Debug Options

### showKey

When logging, URLs printed out on the screen will also have the API key query string attached to it, allowing the user to conveniently inspect the response if necessary.

### loggers

`kayn` now uses debug for all logging purposes.

Here are the current namespaces:

kayn

- init
- request
  - incoming
    - success
    - error
  - outgoing
- cache
  - set
  - get

To enable debugging, firstly make sure `config.debugOptions.isEnabled` is `true`. Then, run your program with the desired DEBUG environment variables.

For example, if you wish to only see the request errors (404, 420, 503, 500, etc), run:

    DEBUG=kayn:request:incoming:error <command>
    # DEBUG=kayn:*:error works too.

...where command runs the script/server/whatever (`npm run start`, `yarn start`, `node index.js`).

To enable all loggers, simply run:

    DEBUG=kayn:* <command>

# My Project

If you're interested in what I have built using this library, here's a small web application I made, along with the original reddit post.

One Tricks:

- site: http://onetricks.net
- reddit link: https://www.reddit.com/r/leagueoflegends/comments/5x1c5c/hi_i_made_a_small_website_to_compile_a_list_of/
- src: https://github.com/cnguy/OneTricks

Here are the requests stats for anyone interested.

Note that my requests stats are inflated since I'm not really caching at the moment (lol).

# Bugs

Feel free to make an issue (bug, typos, questions, suggestions, whatever) or pull request to fix an issue. Just remember to run `prettier` (via `yarn lint`).

Package commands:

- `yarn lint` for `prettier` (will add `eslint` to `prettier` soon)
- `yarn example` to run the various files in `./examples`
- `yarn build` to build. `yarn example` runs this command
- `yarn test`

# Changelog

CHANGELOG.md.

As long this library is pre-1.0.0, breaking changes may be made, but will be documented and will generally not be drastic. Upon 1.0.0, SemVer will be followed strictly.

# Disclaimer

`kayn` isn't endorsed by Riot Games and doesn't reflect the views or opinions of Riot Games or anyone officially involved in producing or managing League of Legends. League of Legends and Riot Games are trademarks or registered trademarks of Riot Games, Inc. League of Legends © Riot Games, Inc.