<commit_before># Kindred
[![NPM](https://nodei.co/npm/kindred-api.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/kindred-api/)

[![Build Status](https://travis-ci.org/ChauTNguyen/kindred-api.svg?branch=master)](https://travis-ci.org/ChauTNguyen/kindred-api)
[![codecov](https://codecov.io/gh/ChauTNguyen/kindred-api/branch/master/graph/badge.svg)](https://codecov.io/gh/ChauTNguyen/kindred-api)
[![dependencies Status](https://david-dm.org/chautnguyen/kindred-api/status.svg)](https://david-dm.org/chautnguyen/kindred-api)

Node.js League of Legends v3 API wrapper with built-in rate-limiting (enforced per region, burst/spread, follows retry headers), caching (in-memory, Redis), automatic retries, and parameter checking on top of [Riot's League of Legends API](http://www.developer.riotgames.com).

### Treat my [versioning](https://github.com/ChauTNguyen/kindred-api/blob/master/CHANGELOG.md) as 0.x.y!

# Refer to [Wiki](https://github.com/ChauTNguyen/kindred-api/wiki) for Complete Documentation and Working Examples!

# Table of Contents:
* [Core Features](#core-features)
* [How the Methods Work](#how-the-methods-work)
* [Quickstart](#quickstart)
* [Known Issues](#known-issues)
* [Changelog](https://github.com/ChauTNguyen/kindred-api/blob/master/CHANGELOG.md)
* [Performance & Other Libraries](https://github.com/ChauTNguyen/kindred-api/wiki/Benchmarks)

# Core Features
* All standard endpoints covered but tournament endpoints.
* Supports both **callbacks** and **promises**.
* **Burst/Spread** rate limiter that is **enforced per region** and **follows retry headers**.
    * **Retries** on 429 and >= 500 **until all calls are successful**. (Doesn't retry on 404)
* Built-in **parameter checks** so you can hopefully refer to documentation less! :)
    * Checks type of parameters (id, name, accout id).
    * Checks if you're not passing valid query parameters!
* Built-in **caching** (in-memory and Redis).
    * **Customized expiration timers**. You can set a timer for each endpoint type. Refer to [Caching](https://github.com/ChauTNguyen/kindred-api/wiki/Caching) for more info.
* Designed to be simple but convenient. For example, you can call an exclusively by-id endpoint (such as grabbing the runes of a player) **with just the summoner name**.
* Tons of config (showing debug, configurable number of retries, turning off auto-retry, etcetc). Check out [Initialization](https://github.com/ChauTNguyen/kindred-api/wiki/Initialization) for all configs.

# How the Methods Work
* [Object + Callback Functions](#object-and-callback-functions)
* [Standard Functions](#standard-functions)

All `list` and `by.xxx` functions will have standard parameters.

**Any other method** will always take in an object as the first parameter, and an optional callback as the second.

These methods can work with different type of parameters (id, name, accountId) when applicable.

What I like about the functions that take object parameters is that you can declare config objects and pass in things really cleanly instead of worrying about the order of parameters in standard functions.

These functions take in an optional `region` and an optional `options` parameter (whenever possible) WITHIN the same object parameter. Most of the time, when they're called, they look like this:

## Object and Callback Functions
```javascript
const id = 32932398 // summonerId
const name = 'Contractz'
const accId = 47776491

k.Matchlist.get({ id }, KindredAPI.print)
k.Matchlist.get({ name }, KindredAPI.print)
k.Matchlist.get({ accId }, KindredAPI.print)

k.Summoner.get({ id }, KindredAPI.print)
k.Summoner.get({ name }, KindredAPI.print)
k.Summoner.get({ accId }, KindredAPI.print)

// Want to use more regular-looking functions? Scroll to the `Standard Functions` section!

const config = {
    id: 6323,
    region: REGIONS.NORTH_AMERICA,
    options: {
        masteryData: ['image', 'sanitizedDescription']
    }
}

k.Static.mastery(config, KindredAPI.print)

const matchlistConfig = {
    name: 'Contractz',
    options: {
        queue: QUEUES.TEAM_BUILDER_RANKED_SOLO, // 420
        champion: 67
    }
}

k.Matchlist.get(matchlistConfig, KindredAPI.print)

const runesConfig = {
  options: {
    locale: 'es_ES',
    runeListData: 'stats'
  },
  region: REGIONS.NORTH_AMERICA
}

k.Static.runes(runesConfig, KindredAPI.print)

const championsConfig = {
  options: {
    champListData: 'all',
    version: '7.9.1'
  }
}

k.Static.champions(championsConfig, KindredAPI.print)

const koreaChampListConfig = {
  options: {
    champListData: 'all'
  },
  region: REGIONS.KOREA
}

k.Static.champions(koreaChampListConfig)
        .then(data => console.log(data))
        .catch(error => console.error(error))

const rakanConfig = {
  id: 497,
  options: {
    champData: 'all'
  },
  region: REGIONS.BRAZIL
}

k.Static.champion(rakanConfig)
        .then(data => console.log(data))
        .catch(err => console.error(err))

// As you can see in the above, all functions that aren't named `list` or `by-id`
// will take in an object parameter as the first parameter ALWAYS.
```

## Standard Functions
```javascript
const QUEUES = KindredAPI.QUEUE_TYPES

k.Summoner.by.name('Contractz', KindredAPI.print)
k.Summoner.by.id(32932398, KindredAPI.print)

// The way these functions handle parameters is:
// Optimal parameters can be excluded, but it's in a left -> right order.
// For example, not including options when it's possible
// would mean whatever you place in there will be in region.
// function(
//  <id>|<name>|<accId>, type varies, existence depends on endpoint, not optional
//  <options>, object, existence depends on endpoint, optional
//  <region>, string, in every function, optional
//  <callback>, function, in every function, optional
//)

k.Summoner
 .by.account(47776491)
 .then(data => console.log(data))
 .catch(error => console.error(error))

const opts = {
  queue: [QUEUES.TEAM_BUILDER_RANKED_SOLO, QUEUES.RANKED_FLEX_SR],
  champion: 81 // Ezreal
}

k.Matchlist.by.name('Contractz', opts, KindredAPI.print)

k.Matchlist.by.name('Contractz', KindredAPI.print)

k.Matchlist
 .by.name('Contractz')
 .then(data => console.log(data))
 .catch(error => console.error(error))

k.Matchlist
 .by.name('sktt1peanut', REGIONS.KOREA)
 .then(data => console.log(data))
 .catch(error => console.error(error))

k.Matchlist
 .by.name('sktt1peanut', opts, REGIONS.KOREA)
 .then(data => console.log(data))
 .catch(error => console.error(error))

k.Static.Champion.list({ champListData: 'all' }, KindredAPI.print)
k.Static.Champion.list(KindredAPI.print)
k.Static.Champion.list(REGIONS.KOREA, KindredAPI.print)
k.Static.Champion.list({ champListData: 'all' }, REGIONS.KOREA, KindredAPI.print)

// Notice how parameters are somewhat flexible! This is the case for all functions.
```

Make sure to check out the [Wiki](https://github.com/ChauTNguyen/kindred-api/wiki)
for working, copy-pastable examples.

# Quickstart
* Debug on
* Dev key rate limiting per region
* In-memory (JS) cache with default settings on for quick scripts
* Burst Rate Limiter

This initialization's purpose should be fairly obvious; This is just for quickly starting on a project. You can always easily replace the QuickStart code with the code required for initializing through the regular way (see below), and Kindred will work just fine.

Start out with this if you want to make test calls right away to play around with the library and decide if you like it.

[For regular and more customizable Initialization (caching, spread rate limiter), refer to wiki.](https://github.com/ChauTNguyen/kindred-api/wiki/Initialization)

```javascript
const KindredAPI = require('kindred-api')
const REGIONS = KindredAPI.REGIONS
const QUEUES = KindredAPI.QUEUE_TYPES // numbers for match endpoints
const debug = true // shows status code + url on request. enables showKey and showHeaders.
const k = KindredAPI.QuickStart('YOUR_KEY', REGIONS.NORTH_AMERICA, debug)
const Q_STRINGS = KindredAPI.QUEUE_STRINGS // strings for editor convenience

// Make sure to read `How the methods work` to understand the difference between
// get/noun functions VS by.id/list.
k.Challenger.list(Q_STRINGS.RANKED_SOLO_5x5)

/* Summoners! */
k.Summoner.get({ id: 32932398 }, KindredAPI.print)
k.Summoner.get({ name: 'Contractz' }, KindredAPI.print)
k.Summoner.by.id(32932398, KindredAPI.print)
k.Summoner.by.name('Contractz', REGIONS.NORTH_AMERICA, KindredAPI.print)

/* How to pass in options 101. */
const runesConfig = {
  options: {
    locale: 'es_ES',
    runeListData: 'stats'
  },
  region: REGIONS.NORTH_AMERICA,
}

k.Static.runes(runesConfig, KindredAPI.print)

const name = 'caaaaaaaaaria'
const region = REGIONS.NORTH_AMERICA
const options = {
  // no need for joins or hardcoded numbers
  queue: [QUEUES.TEAM_BUILDER_RANKED_SOLO, QUEUES.RANKED_FLEX_SR],
  // array values will always be joined into a string
  champion: 79
  // option params should be spelled and capitalized the same as it is in Riot's docs!
  // ex: Matchlist params in Riot's docs include `champion`, `beginIndex`, `beginTime`, `season`
}

k.Summoner
 .get({ name, region })
 .then(data => k.Matchlist.get(
   { accId: data.accountId, options }
   )
 )
 .then(data => console.log(data))
 .catch(err => console.error(err))

/*
    Instead of chaining requests like in the above, you can simply call
    k.Matchlist.get with the `name` param or the `id` (summonerId) param.
    Any function that targets just Ids or accountIds can use all three
    different type of params (summonerId, accountId, name).
*/
k.Matchlist
 .get({ name, region, options })
 .then(data => console.log(data))
 .catch(err => console.error(err))

const accId = 47776491
const id = 32932398 // summonerId
k.Matchlist.get({ name }, KindredAPI.print)
k.Matchlist.get({ accId }, KindredAPI.print)
k.Matchlist.get({ id }, KindredAPI.print)

/* Up to preference. */
k.Runes.get({ name }, KindredAPI.print)
k.Summoner.runes({ name }, KindredAPI.print)

k.Matchlist.get({ name }, KindredAPI.print) // full matchlist
k.Summoner.matchlist({ name }, KindredAPI.print)

k.Matchlist.recent({ name }, KindredAPI.print)
k.Summoner.matchHistory({ name }, KindredAPI.print) // recent matches (20)

const koreaChampListConfig = {
  options: {
    champListData: 'all'
  },
  region: REGIONS.KOREA
}

k.Static.champions(koreaChampListConfig)
        .then(data => console.log(data))
        .catch(error => console.error(error))

const rakanConfig = {
  id: 497,
  options: {
    champData: 'all'
  },
  region: REGIONS.BRAZIL<commit_msg>Update README examples (->tags)<commit_after># Kindred
[![NPM](https://nodei.co/npm/kindred-api.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/kindred-api/)

[![Build Status](https://travis-ci.org/ChauTNguyen/kindred-api.svg?branch=master)](https://travis-ci.org/ChauTNguyen/kindred-api)
[![codecov](https://codecov.io/gh/ChauTNguyen/kindred-api/branch/master/graph/badge.svg)](https://codecov.io/gh/ChauTNguyen/kindred-api)
[![dependencies Status](https://david-dm.org/chautnguyen/kindred-api/status.svg)](https://david-dm.org/chautnguyen/kindred-api)

Node.js League of Legends v3 API wrapper with built-in rate-limiting (enforced per region, burst/spread, follows retry headers), caching (in-memory, Redis), automatic retries, and parameter checking on top of [Riot's League of Legends API](http://www.developer.riotgames.com).

### Treat my [versioning](https://github.com/ChauTNguyen/kindred-api/blob/master/CHANGELOG.md) as 0.x.y!

# Refer to [Wiki](https://github.com/ChauTNguyen/kindred-api/wiki) for Complete Documentation and Working Examples!

# Table of Contents:
* [Core Features](#core-features)
* [How the Methods Work](#how-the-methods-work)
* [Quickstart](#quickstart)
* [Known Issues](#known-issues)
* [Changelog](https://github.com/ChauTNguyen/kindred-api/blob/master/CHANGELOG.md)
* [Performance & Other Libraries](https://github.com/ChauTNguyen/kindred-api/wiki/Benchmarks)

# Core Features
* All standard endpoints covered but tournament endpoints.
* Supports both **callbacks** and **promises**.
* **Burst/Spread** rate limiter that is **enforced per region** and **follows retry headers**.
    * **Retries** on 429 and >= 500 **until all calls are successful**. (Doesn't retry on 404)
* Built-in **parameter checks** so you can hopefully refer to documentation less! :)
    * Checks type of parameters (id, name, account id).
    * Checks if you're not passing valid query parameters!
* Built-in **caching** (in-memory and Redis).
    * **Customized expiration timers**. You can set a timer for each endpoint type. Refer to [Caching](https://github.com/ChauTNguyen/kindred-api/wiki/Caching) for more info.
* Designed to be simple but convenient. For example, you can call an exclusively by-id endpoint (such as grabbing the runes of a player) **with just the summoner name**.
* Tons of config (showing debug, configurable number of retries, turning off auto-retry, etcetc). Check out [Initialization](https://github.com/ChauTNguyen/kindred-api/wiki/Initialization) for all configs.

# How the Methods Work
* [Object + Callback Functions](#object-and-callback-functions)
* [Standard Functions](#standard-functions)

All `list` and `by.xxx` functions will have standard parameters.

**Any other method** will always take in an object as the first parameter, and an optional callback as the second.

These methods can work with different type of parameters (id, name, accountId) when applicable.

What I like about the functions that take object parameters is that you can declare config objects and pass in things really cleanly instead of worrying about the order of parameters in standard functions.

These functions take in an optional `region` and an optional `options` parameter (whenever possible) WITHIN the same object parameter. Most of the time, when they're called, they look like this:

## Object and Callback Functions
```javascript
const id = 32932398 // summonerId
const name = 'Contractz'
const accId = 47776491

k.Matchlist.get({ id }, KindredAPI.print)
k.Matchlist.get({ name }, KindredAPI.print)
k.Matchlist.get({ accId }, KindredAPI.print)

k.Summoner.get({ id }, KindredAPI.print)
k.Summoner.get({ name }, KindredAPI.print)
k.Summoner.get({ accId }, KindredAPI.print)

// Want to use more regular-looking functions? Scroll to the `Standard Functions` section!

const config = {
    id: 6323,
    region: REGIONS.NORTH_AMERICA,
    options: {
        masteryData: ['image', 'sanitizedDescription']
    }
}

k.Static.mastery(config, KindredAPI.print)

const matchlistConfig = {
    name: 'Contractz',
    options: {
        queue: QUEUES.TEAM_BUILDER_RANKED_SOLO, // 420
        champion: 67
    }
}

k.Matchlist.get(matchlistConfig, KindredAPI.print)

const runesConfig = {
  options: {
    locale: 'es_ES',
    runeListData: 'stats'
  },
  region: REGIONS.NORTH_AMERICA
}

k.Static.runes(runesConfig, KindredAPI.print)

const championsConfig = {
  options: {
    champListData: 'all',
    version: '7.9.1'
  }
}

k.Static.champions(championsConfig, KindredAPI.print)

const koreaChampListConfig = {
  options: {
    champListData: 'all'
  },
  region: REGIONS.KOREA
}

k.Static.champions(koreaChampListConfig)
        .then(data => console.log(data))
        .catch(error => console.error(error))

const rakanConfig = {
  id: 497,
  options: {
    tags: 'all'
  },
  region: REGIONS.BRAZIL
}

k.Static.champion(rakanConfig)
        .then(data => console.log(data))
        .catch(err => console.error(err))

// As you can see in the above, all functions that aren't named `list` or `by-id`
// will take in an object parameter as the first parameter ALWAYS.
```

## Standard Functions
```javascript
const QUEUES = KindredAPI.QUEUE_TYPES

k.Summoner.by.name('Contractz', KindredAPI.print)
k.Summoner.by.id(32932398, KindredAPI.print)

// The way these functions handle parameters is:
// Optimal parameters can be excluded, but it's in a left -> right order.
// For example, not including options when it's possible
// would mean whatever you place in there will be in region.
// function(
//  <id>|<name>|<accId>, type varies, existence depends on endpoint, not optional
//  <options>, object, existence depends on endpoint, optional
//  <region>, string, in every function, optional
//  <callback>, function, in every function, optional
//)

k.Summoner
 .by.account(47776491)
 .then(data => console.log(data))
 .catch(error => console.error(error))

const opts = {
  queue: [QUEUES.TEAM_BUILDER_RANKED_SOLO, QUEUES.RANKED_FLEX_SR],
  champion: 81 // Ezreal
}

k.Matchlist.by.name('Contractz', opts, KindredAPI.print)

k.Matchlist.by.name('Contractz', KindredAPI.print)

k.Matchlist
 .by.name('Contractz')
 .then(data => console.log(data))
 .catch(error => console.error(error))

k.Matchlist
 .by.name('sktt1peanut', REGIONS.KOREA)
 .then(data => console.log(data))
 .catch(error => console.error(error))

k.Matchlist
 .by.name('sktt1peanut', opts, REGIONS.KOREA)
 .then(data => console.log(data))
 .catch(error => console.error(error))

k.Static.Champion.list({ tags: 'all' }, KindredAPI.print)
k.Static.Champion.list(KindredAPI.print)
k.Static.Champion.list(REGIONS.KOREA, KindredAPI.print)
k.Static.Champion.list({ tags: 'all' }, REGIONS.KOREA, KindredAPI.print)

// Notice how parameters are somewhat flexible! This is the case for all functions.
```

Make sure to check out the [Wiki](https://github.com/ChauTNguyen/kindred-api/wiki)
for working, copy-pastable examples.

# Quickstart
* Debug on
* Dev key rate limiting per region
* In-memory (JS) cache with default settings on for quick scripts
* Burst Rate Limiter

This initialization's purpose should be fairly obvious; This is just for quickly starting on a project. You can always easily replace the QuickStart code with the code required for initializing through the regular way (see below), and Kindred will work just fine.

Start out with this if you want to make test calls right away to play around with the library and decide if you like it.

[For regular and more customizable Initialization (caching, spread rate limiter), refer to wiki.](https://github.com/ChauTNguyen/kindred-api/wiki/Initialization)

```javascript
const KindredAPI = require('kindred-api')
const REGIONS = KindredAPI.REGIONS
const QUEUES = KindredAPI.QUEUE_TYPES // numbers for match endpoints
const debug = true // shows status code + url on request. enables showKey and showHeaders.
const k = KindredAPI.QuickStart('YOUR_KEY', REGIONS.NORTH_AMERICA, debug)
const Q_STRINGS = KindredAPI.QUEUE_STRINGS // strings for editor convenience

// Make sure to read `How the methods work` to understand the difference between
// get/noun functions VS by.id/list.
k.Challenger.list(Q_STRINGS.RANKED_SOLO_5x5)

/* Summoners! */
k.Summoner.get({ id: 32932398 }, KindredAPI.print)
k.Summoner.get({ name: 'Contractz' }, KindredAPI.print)
k.Summoner.by.id(32932398, KindredAPI.print)
k.Summoner.by.name('Contractz', REGIONS.NORTH_AMERICA, KindredAPI.print)

/* How to pass in options 101. */
const runesConfig = {
  options: {
    locale: 'es_ES',
    runeListData: 'stats'
  },
  region: REGIONS.NORTH_AMERICA,
}

k.Static.runes(runesConfig, KindredAPI.print)

const name = 'caaaaaaaaaria'
const region = REGIONS.NORTH_AMERICA
const options = {
  // no need for joins or hardcoded numbers
  queue: [QUEUES.TEAM_BUILDER_RANKED_SOLO, QUEUES.RANKED_FLEX_SR],
  // array values will always be joined into a string
  champion: 79
  // option params should be spelled and capitalized the same as it is in Riot's docs!
  // ex: Matchlist params in Riot's docs include `champion`, `beginIndex`, `beginTime`, `season`
}

k.Summoner
 .get({ name, region })
 .then(data => k.Matchlist.get(
   { accId: data.accountId, options }
   )
 )
 .then(data => console.log(data))
 .catch(err => console.error(err))

/*
    Instead of chaining requests like in the above, you can simply call
    k.Matchlist.get with the `name` param or the `id` (summonerId) param.
    Any function that targets just Ids or accountIds can use all three
    different type of params (summonerId, accountId, name).
*/
k.Matchlist
 .get({ name, region, options })
 .then(data => console.log(data))
 .catch(err => console.error(err))

const accId = 47776491
const id = 32932398 // summonerId
k.Matchlist.get({ name }, KindredAPI.print)
k.Matchlist.get({ accId }, KindredAPI.print)
k.Matchlist.get({ id }, KindredAPI.print)

/* Up to preference. */
k.Runes.get({ name }, KindredAPI.print)
k.Summoner.runes({ name }, KindredAPI.print)

k.Matchlist.get({ name }, KindredAPI.print) // full matchlist
k.Summoner.matchlist({ name }, KindredAPI.print)

k.Matchlist.recent({ name }, KindredAPI.print)
k.Summoner.matchHistory({ name }, KindredAPI.print) // recent matches (20)

const koreaChampListConfig = {
  options: {
    champListData: 'all'
  },
  region: REGIONS.KOREA
}

k.Static.champions(koreaChampListConfig)
        .then(data => console.log(data))
        .catch(error => console.error(error))

const rakanConfig = {
  id: 497,
  options: {
    tags: 'all'
  },
  region: REGIONS.BRAZIL
{'fraction_non_alphanumeric': 0.126132106785528, 'fraction_numerical': 0.010052634074636066, 'mean_word_length': 4.36777806057521, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 58, 'https://': 32, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1805008', 'n_tokens_mistral': 7229, 'n_tokens_neox': 6835, 'n_words': 1980}