Skip to content

queicherius/gw2api-client

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gw2api-client

Build Status Coverage Status Greenkeeper badge Bundle Size

Javascript wrapper for the official Guild Wars 2 API.

Install

npm install gw2api-client

This module can be used for Node.js as well as browsers using Browserify.

Usage

Basic usage

const client = require('gw2api-client')

// Get an instance of an API client
let api = client()

// Optional, but recommended: Set the schema version of the client
api.schema('2019-03-26T00:00:00Z')

// Optional: Set the language of the client
api.language('en')

// Optional: Authenticate the client using an API key
api.authenticate('my-secret-key')

// Get the ids of all items
api.items().ids().then(items => console.log(items))

// Get a single item
api.items().get(123).then(item => console.log(item))

// Get multiple items
api.items().many([123, 456]).then(items => console.log(items))

// Get all items
api.items().all().then(items => console.log(items))

Endpoints

You can find all endpoints and their respective function calls in this document.

Caching

You can find all cache storages (and the interface for custom ones) in this document.

By default, calling any endpoint requests data from the live API. However, you can easily enable caching for all appropriate endpoints by giving the client a cache storage to work with. You can find the default cache times of all endpoints here.

const cacheMemory = require('gw2api-client/src/cache/memory')
api.cacheStorage(cacheMemory())

// This will only call the official API once
api.items().ids()
// ...
api.items().ids()

// When the cache expires, this will call the official API again
api.items().ids()

// You can skip the cache for guaranteed live data
api.items().live().ids()

Note: The cache storage save is asynchronous in the background. During this time, the API function already resolves a result for best performance. Therefore it can happen that some data gets requested twice, if you request it in rapid succession and are not using a cache that saves in memory (memory or browser caches).

You can also chain multiple cache storages together. In this case, the cache gets saved in all storages and read from the first storage in the list answering with a valid value. The more persistent and more reliable cache storages should therefore be on the end of the list and the fastest (e.g. memory) should be at the start of the list.

const cacheMemory = require('gw2api-client/src/cache/memory')
const cacheRedisStorage = require('gw2api-client/src/cache/redis')

// Save in memory and local storage
// Try to answer from memory first, then from local storage and then hit the API
api.cacheStorage([
  cacheMemory(),
  cacheRedisStorage({ ... })
])

The cache uses expiration times and not the build number, because the content of the API can update independently of the build id. This is caused by the internal whitelisting of the API. However, if you want your cache to invalidate when there is a game update, this is easily possible too:

// configure api.cacheStorage(...) beforehand

// Check the build every 10 minutes and flush the cache if it updated
setInterval(() => api.flushCacheIfGameUpdated(), 10 * 60 * 1000)

Error handling

You can use the Promise catch to handle all possible errors.

api.account().bank().catch(err => {
  // err.response is the last response object (e.g. err.response.status)
  // err.content is the parsed body of the response, if available
  // err.content.text is the error text thrown of the API, if available
  console.error('Something went wrong', err)
})

The API can throw server errors (status >= 500) that don't have a text property set. However, most of the time it responds with one of the following errors:

  • endpoint requires authentication
  • invalid key
  • requires scope <xyz>
  • membership required
  • access restricted to guild leaders
  • page out of range
  • no such id
  • all ids provided are invalid

Retrying

By accessing the fetch instance, you can enable retrying in case the API or the user has problems getting a valid response. You can find the full documentation for retrying here.

// Retry up to 3 times if the status indicates an request error
api.fetch.retry((tries, err) => {
  if (tries > 3) { 
    return false
  }

  const res = err.response
  if (res && (res.status < 400 || res.status === 403)) {
    return false
  }

  return true
})

// Wait in between retries
api.fetch.retryWait((tries) => tries * 100)

// This request will now retry if it fails (e.g. API issues)
api.items().ids()

Extending

You can extend or overwrite the API client with your own endpoints if you wish so. The only thing that is required is an extension of AbstractEndpoint to provide all the logic for pagination, bulk, localisation, caching and so on.

If you need more specific ways to handle data then the already defined ones, take a look at how the existing endpoints handle these edge cases (e.g. in /src/endpoints/recipes.js).

const client = require('gw2api-client')
const AbstractEndpoint = require('gw2api-client/src/endpoint')

// Get an instance of an API client
const api = client()

// Example: Add a new function inside the abstract endpoint
AbstractEndpoint.prototype.post = function () {
  console.log(this)
}

// Example: Define our custom "items" endpoint
class ItemsEndpoint extends AbstractEndpoint {
  constructor (client) {
    super(client)
    this.baseUrl = 'https://api.my-domain.com' // The base URL of the API
    this.url = '/items' // The endpoint URL path
    this.isPaginated = false // If the endpoint supports ?page and ?page_size
    this.isBulk = true // If the endpoint supports ?ids
    this.supportsBulkAll = false // If the endpoint supports ?ids=all
    this.isLocalized = true // If the endpoint supports ?lang
    this.cacheTime = 5 * 60 // How long to cache the endpoint responses for
    this.isAuthenticated = true // If the endpoint requires ?access_token

    this.credentials = true // If the endpoint requires credentials (e.g. session cookies)
  }
}

// Attach it to the client, either as a new endpoint or overwriting an already existing one
api.items = () => new ItemsEndpoint(api)

// Use the new, overwritten endpoint
api.items().many([123, 456])
  .then(items => console.log('Got the items', items))
  .catch(err => console.error('Things went badly', err))

Mocking

If you want to mock this module in your tests, you can replace the underlying lets-fetch library with the provided mock module, e.g. using rewire. You can find all available mock methods here.

const fetchMock = require('lets-fetch/mock')
const file = require('./some/file/using/gw2api/client.js')

// Get the variable "api" (which would be the initialized api client
// in your own code) and replace the fetch method with the fetchMock
file.__get__('api').fetch = fetchMock

// Use the fetch mock methods as described in the link above

Debugging

You can enable debug messages by setting a flag on the client:

const client = require('gw2api-client')
let api = client()

// Set for specific endpoints
let items = api.items().debugging(true).ids().then(items => console.log(items))

// Set for all endpoints
api.debugging(true).items().ids().then(items => console.log(items))

Tests

npm test

Licence

MIT