• Stars
    star
    2,789
  • Rank 16,330 (Top 0.4 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 8 years ago
  • Updated about 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Axios + standardized errors + request/response transforms.

Apisauce

(Ring ring ring)
< Hello?
> Hi, can I speak to JSON API.
< Speaking.
> Hi, it's me JavaScript.  Look, we need to talk.
< Now is not a good time...
> Wait, I just wanted to say, sorry.
< ...

Talking to APIs doesn't have to be awkward anymore.

npm module

Features

  • low-fat wrapper for the amazing axios http client library
  • all responses follow the same flow: success and failure alike
  • responses have a problem property to help guide exception flow
  • attach functions that get called each request
  • attach functions that change all request or response data
  • detects connection issues (on React Native)

Installing

npm i apisauce --save or yarn add apisauce

  • Depends on axios.
  • Compatible with ES5.
  • Built with TypeScript.
  • Supports Node, the browser, and React Native.

Quick Start

// showLastCommitMessageForThisLibrary.js
import { create } from 'apisauce'

// define the api
const api = create({
  baseURL: 'https://api.github.com',
  headers: { Accept: 'application/vnd.github.v3+json' },
})

// start making calls
api
  .get('/repos/skellock/apisauce/commits')
  .then(response => response.data[0].commit.message)
  .then(console.log)

// customizing headers per-request
api.post('/users', { name: 'steve' }, { headers: { 'x-gigawatts': '1.21' } })

See the examples folder for more code.

Documentation

Create an API

You create an api by calling .create() and passing in a configuration object.

const api = create({ baseURL: 'https://api.github.com' })

The only required property is baseURL and it should be the starting point for your API. It can contain a sub-path and a port as well.

const api = create({ baseURL: 'https://example.com/api/v3' })

HTTP request headers for all requests can be included as well.

const api = create({
  baseURL: '...',
  headers: {
    'X-API-KEY': '123',
    'X-MARKS-THE-SPOT': 'yarrrrr',
  },
})

Default timeouts can be applied too:

const api = create({ baseURL: '...', timeout: 30000 }) // 30 seconds

You can also pass an already created axios instance

import axios from 'axios'
import { create } from 'apisauce'

const customAxiosInstance = axios.create({ baseURL: 'https://example.com/api/v3' })

const apisauceInstance = create({ axiosInstance: customAxiosInstance })

Calling The API

With your fresh api, you can now call it like this:

api.get('/repos/skellock/apisauce/commits')
api.head('/me')
api.delete('/users/69')
api.post('/todos', { note: 'jump around' }, { headers: { 'x-ray': 'machine' } })
api.patch('/servers/1', { live: false })
api.put('/servers/1', { live: true })
api.link('/images/my_dog.jpg', {}, { headers: { Link: '<http://example.com/profiles/joe>; rel="tag"' } })
api.unlink('/images/my_dog.jpg', {}, { headers: { Link: '<http://example.com/profiles/joe>; rel="tag"' } })
api.any({ method: 'GET', url: '/product', params: { id: 1 } })

get, head, delete, link and unlink accept 3 parameters:

  • url - the relative path to the API (required)
  • params - Object - query string variables (optional)
  • axiosConfig - Object - config passed along to the axios request (optional)

post, put, and patch accept 3 different parameters:

  • url - the relative path to the API (required)
  • data - Object - the object jumping the wire
  • axiosConfig - Object - config passed along to the axios request (optional)

any only accept one parameter

  • config - Object - config passed along to the axios request, this object same as axiosConfig

Responses

The responses are promise-based, so you'll need to handle things in a .then() function.

The promised is always resolved with a response object.

Even if there was a problem with the request! This is one of the goals of this library. It ensures sane calling code without having to handle .catch and have 2 separate flows.

A response will always have these 2 properties:

ok      - Boolean - True if the status code is in the 200's; false otherwise.
problem - String  - One of 6 different values (see below - problem codes)

If the request made it to the server and got a response of any kind, response will also have these properties:

data     - Object - this is probably the thing you're after.
status   - Number - the HTTP response code
headers  - Object - the HTTP response headers
config   - Object - the `axios` config object used to make the request
duration - Number - the number of milliseconds it took to run this request

Sometimes on different platforms you need access to the original axios error that was thrown:

originalError - Error - the error that axios threw in case you need more info

Changing Base URL

You can change the URL your api is connecting to.

api.setBaseURL('https://some.other.place.com/api/v100')
console.log(`omg i am now at ${api.getBaseURL()}`)

Changing Headers

Once you've created your api, you're able to change HTTP requests by calling setHeader or setHeaders on the api. These stay with the api instance, so you can just set 'em and forget 'em.

api.setHeader('Authorization', 'the new token goes here')
api.setHeaders({
  Authorization: 'token',
  'X-Even-More': 'hawtness',
})

Adding Monitors

Monitors are functions you can attach to the API which will be called when any request is made. You can use it to do things like:

  • check for headers and record values
  • determine if you need to trigger other parts of your code
  • measure performance of API calls
  • perform logging

Monitors are run just before the promise is resolved. You get an early sneak peak at what will come back.

You cannot change anything, just look.

Here's a sample monitor:

const naviMonitor = response => console.log('hey!  listen! ', response)
api.addMonitor(naviMonitor)

Any exceptions that you trigger in your monitor will not affect the flow of the api request.

api.addMonitor(response => this.kaboom())

Internally, each monitor callback is surrounded by an oppressive try/catch block.

Remember. Safety first!

Adding Transforms

In addition to monitoring, you can change every request or response globally.

This can be useful if you would like to:

  • fix an api response
  • add/edit/delete query string variables for all requests
  • change outbound headers without changing everywhere in your app

Unlike monitors, exceptions are not swallowed. They will bring down the stack, so careful!

Response Transforms

For responses, you're provided an object with these properties.

  • data - the object originally from the server that you might wanna mess with
  • duration - the number of milliseconds
  • problem - the problem code (see the bottom for the list)
  • ok - true or false
  • status - the HTTP status code
  • headers - the HTTP response headers
  • config - the underlying axios config for the request

Data is the only option changeable.

api.addResponseTransform(response => {
  const badluck = Math.floor(Math.random() * 10) === 0
  if (badluck) {
    // just mutate the data to what you want.
    response.data.doorsOpen = false
    response.data.message = 'I cannot let you do that.'
  }
})

Or make it async:

api.addAsyncResponseTransform(async response => {
  const something = await AsyncStorage.load('something')
  if (something) {
    // just mutate the data to what you want.
    response.data.doorsOpen = false
    response.data.message = 'I cannot let you do that.'
  }
})

Request Transforms

For requests, you are given a request object. Mutate anything in here to change anything about the request.

The object passed in has these properties:

  • data - the object being passed up to the server
  • method - the HTTP verb
  • url - the url we're hitting
  • headers - the request headers
  • params - the request params for get, delete, head, link, unlink

Request transforms can be a function:

api.addRequestTransform(request => {
  request.headers['X-Request-Transform'] = 'Changing Stuff!'
  request.params['page'] = 42
  delete request.params.secure
  request.url = request.url.replace(/\/v1\//, '/v2/')
  if (request.data.password && request.data.password === 'password') {
    request.data.username = `${request.data.username} is secure!`
  }
})

And you can also add an async version for use with Promises or async/await. When you resolve your promise, ensure you pass the request along.

api.addAsyncRequestTransform(request => {
  return new Promise(resolve => setTimeout(resolve, 2000))
})
api.addAsyncRequestTransform(request => async () => {
  await AsyncStorage.load('something')
})

This is great if you need to fetch an API key from storage for example.

Multiple async transforms will be run one at a time in succession, not parallel.

Using Async/Await

If you're more of a stage-0 kinda person, you can use it like this:

const api = create({ baseURL: '...' })
const response = await api.get('/slowest/site/on/the/net')
console.log(response.ok) // yay!

Cancel Request

import { CancelToken } from 'apisauce'

const source = CancelToken.source()
const api = create({ baseURL: 'github.com' })
api.get('/users', {}, { cancelToken: source.token })

// To cancel request
source.cancel()

Problem Codes

The problem property on responses is filled with the best guess on where the problem lies. You can use a switch to check the problem. The values are exposed as CONSTANTS hanging on your built API.

Constant        VALUE               Status Code   Explanation
----------------------------------------------------------------------------------------
NONE             null               200-299       No problems.
CLIENT_ERROR     'CLIENT_ERROR'     400-499       Any non-specific 400 series error.
SERVER_ERROR     'SERVER_ERROR'     500-599       Any 500 series error.
TIMEOUT_ERROR    'TIMEOUT_ERROR'    ---           Server didn't respond in time.
CONNECTION_ERROR 'CONNECTION_ERROR' ---           Server not available, bad dns.
NETWORK_ERROR    'NETWORK_ERROR'    ---           Network not available.
CANCEL_ERROR     'CANCEL_ERROR'     ---           Request has been cancelled. Only possible if `cancelToken` is provided in config, see axios `Cancellation`.

Which problem is chosen will be picked by walking down the list.

Mocking with axios-mock-adapter (or other libraries)

A common testing pattern is to use axios-mock-adapter to mock axios and respond with stubbed data. These libraries mock a specific instance of axios, and don't globally intercept all instances of axios. When using a mocking library like this, it's important to make sure to pass the same axios instance into the mock adapter.

Here is an example code from axios_mock, modified to work with Apisauce:

import apisauce from 'apisauce'
import MockAdapter from 'axios-mock-adapter'

test('mock adapter', async () => {
  const api = apisauce.create("https://api.github.com")
- const mock = new MockAdapter(axios)
+ const mock = new MockAdapter(api.axiosInstance)
  mock.onGet("/repos/skellock/apisauce/commits").reply(200, {
    commits: [{ id: 1, sha: "aef849923444" }],
  });

  const response = await api..get('/repos/skellock/apisauce/commits')
  expect(response.data[0].sha).toEqual"aef849923444")
})

Contributing

Bugs? Comments? Features? PRs and Issues happily welcomed! Make sure to check out our contributing guide to get started!

More Repositories

1

ignite

Infinite Red's battle-tested React Native project boilerplate, along with a CLI, component/model generators, and more!
TypeScript
17,283
star
2

reactotron

A desktop app for inspecting your React JS and React Native projects. macOS, Linux, and Windows.
TypeScript
14,757
star
3

nsfwjs

NSFW detection on the client-side via TensorFlow.js
JavaScript
7,194
star
4

gluegun

A delightful toolkit for building TypeScript-powered command-line apps.
TypeScript
2,937
star
5

thesis-phoenix

A lightweight, bolt-on, intuitive content editing system for Elixir/Phoenix websites. Star this repo and follow along with our progress!
Elixir
648
star
6

ignite-bowser

Bowser is now re-integrated into Ignite CLI! Head to https://github.com/infinitered/ignite to check it out.
TypeScript
615
star
7

solidarity

Solidarity is an environment checker for project dependencies across multiple machines.
TypeScript
614
star
8

ignite-andross

The original React Native boilerplate from Infinite Red - Redux, React Navigation, & more
JavaScript
475
star
9

ChainReactApp2017

The official Chain React Conf 2017 App
JavaScript
437
star
10

rmq

RMQ - RubyMotionQuery
HTML
307
star
11

redpotion

We believe iPhone development should be clean, scalable, and fast with a language that developers not only enjoy, but actively choose. With the advent of Ruby for iPhone development the RubyMotion community has combined and tested the most active and powerful gems into a single package called RedPotion
Ruby
234
star
12

cdq

Core Data Query for RubyMotion
Ruby
172
star
13

ChainReactApp2019

The Chain React 2019 Conference App
TypeScript
165
star
14

react-native-mlkit

The definitive MLKit wrapper for React Native and Expo
TypeScript
124
star
15

nsfwjs-mobile

NSFWjs in React Native
JavaScript
112
star
16

ChainReactApp2023

The official Chain React App for #ChainReact2023, written in React Native
TypeScript
110
star
17

reactotron-react-native

Reactotron's react-native client.
TypeScript
110
star
18

ruby-xcdm

Ruby XCDM
Ruby
94
star
19

bluepotion

Like RedPotion, but for Android
Ruby
74
star
20

ProMotion-menu

RubyMotion gem allowing you to easily setup a facebook or Path style hidden slide menu easily with the ProMotion gem.
Ruby
74
star
21

ramdasauce

Ramda smothered in saucy helpers.
JavaScript
69
star
22

awesome-react-testing

React and React Native testing tools and strategies
67
star
23

firebird

Template for Phoenix 1.3 projects
Elixir
66
star
24

phoenix_base

Template project for Phoenix
Elixir
65
star
25

flipper-plugin-reactotron

A plugin for the Flipper desktop application
TypeScript
63
star
26

ignite-cookbook

Ignite Cookbook for React Native
CSS
47
star
27

ignite-example-login

This is an example Ignited app that shows how to integrate a login screen with redux-aware react navigation
JavaScript
47
star
28

solidarity-react-native

Solidarity snapshot plugin for React Native projects
JavaScript
47
star
29

ai-lab

Library of components for TensorFlow.js in web frameworks.
TypeScript
46
star
30

ionicons-version-3-search

Quickly identify IonIcons from version 3, with version 2 names as tags!
JavaScript
41
star
31

apex

Apex: the RubyMotion web framework for OS X.
Ruby
31
star
32

ir-docs

Omnibus Documentation for Infinite Red Opensource
CSS
26
star
33

ignite-ir-boilerplate-2016

[Not supported] A boilerplate for Ignite with best practices we've learned from 2015.
JavaScript
26
star
34

ignite-bowser-examples

This repository is out of date and is archived here for historical purposes only. See current Ignite Bowser for more relevant examples.
TypeScript
26
star
35

maybe

Access Elixir maps and structs, protected from `nil`
Elixir
25
star
36

open-source

Information and Guides for Infinite Red Open Source Projects
24
star
37

thesis-rails

Thesis: A Rails CMS that doesn't hijack your development workflow. [Deprecated]
Ruby
23
star
38

authority

Next-gen Elixir authentication specification
Elixir
22
star
39

babel-plugin-ignite-ignore-reactotron

Strips Reactotron from production builds for Ignite-based apps.
JavaScript
21
star
40

ChainReactApp2022

TypeScript
20
star
41

react-navx

[Experimental] Navigation and state management in one place for your React Native projects, featuring React Navigation and MobX / MST
TypeScript
20
star
42

mst-reference-pool

MST Reference Pool is a MobX-State-Tree extension that allows you to use references to a pool of model instances in any store.
TypeScript
20
star
43

ProMotion-iap

In-app purchases for ProMotion!
Ruby
19
star
44

ProMotion-form

Deprecated -- use ProMotion-XLForm
Ruby
19
star
45

ignite-maps

Painlessly add react-native-maps to your React Native app using Ignite and Ignite Maps.
EJS
19
star
46

ProMotion-push

Push notification support for ProMotion.
Ruby
18
star
47

rmq-example-app-image-browser

An example app for RubyMotionQuery (rmq). The user can query, browse, and zoom photos. Uses RMQ and AFMotion
Ruby
18
star
48

Parsistence

A nice wrapper for your Parse models on RubyMotion, complete with querying and much more.
Ruby
17
star
49

reactotron-react-js

Reactotron's react-dom client.
TypeScript
16
star
50

whitepotion

WhitePotion is just like RedPotion, but for OS X
Ruby
15
star
51

ProMotion-map

ProMotion::MapScreen gem. Extracted from ProMotion core.
Ruby
13
star
52

react-native-ai

13
star
53

motion-scene-kit

SceneKit stuff in RubyMotion
Ruby
13
star
54

middleman-template

A nice default project template for Middleman, the fantastic static site building tool.
CSS
12
star
55

harvest-invision-integration

Install this User Script via TamperMonkey to add Harvest Tracker to Invision Enterprise workflow pages
JavaScript
12
star
56

authority_ecto

Ecto integration for Authority https://github.com/infinitered/authority
Elixir
12
star
57

rmq-slide-over-control

A RubyMotion RMQ control that places a draggable view over another view, allowing the user to show more or less of the main view below
Ruby
12
star
58

keras-model-zoo

Ready to go, downloadable models for Keras
11
star
59

ignite-json-server

Ignite plugin that adds json-server to an Ignited project
JavaScript
10
star
60

reactotron-redux

The redux plugin for reactotron that allows tracking redux actions and state
TypeScript
10
star
61

reactotron-redux-saga

The redux saga plugin for reactotron. Allows for tracking of Redux Sagas
TypeScript
10
star
62

reactotron-core-client

The core client for all reactotron clients
TypeScript
10
star
63

ignite-redux-persist

An Ignite CLI plugin for Redux Persist
JavaScript
9
star
64

addon-storyshots

JavaScript
8
star
65

rmq-template

A template for RubyMotionQuery projects
Ruby
8
star
66

DiveIntoNative

Just scratching the surface of jumping into Native
Objective-C
7
star
67

ignite-dev-screens

Ignite DevScreens plugin for ignite-cli
JavaScript
7
star
68

tensorplayground

Playground for visual tensors and interactive code
JavaScript
7
star
69

mithril_pubsub

A PubSub layer for Mithril-architected apps
Elixir
6
star
70

ignite-animatable

An ignite plugin for react-native-animatable.
JavaScript
6
star
71

reactotron-core-server

The core reactotron server used by the reactotron app
TypeScript
6
star
72

redpotion-template

Ruby
5
star
73

bluepotion-template

Ruby
5
star
74

ignite-vector-icons

An ignite plugin for `react-native-vector-icons`.
JavaScript
5
star
75

jest-preset-ignite

Glues together TypeScript, React Native, and Jest.
JavaScript
5
star
76

ignite-redux-devtools

An Ignite CLI plugin for Redux DevTools
JavaScript
5
star
77

reactotron-mst

Reactotron's mobx-state-tree plugin
TypeScript
4
star
78

ignite-i18n

An ignite plugin for react-native-i18n.
JavaScript
4
star
79

reactotron-core-ui

TypeScript
4
star
80

ueberauth_dwolla

Ueberauth Plugin for OAuth through Dwolla
Elixir
3
star
81

ups

Address validation/normalization against UPS API
Elixir
3
star
82

ignite-ir-boilerplate

Boilerplate Index
3
star
83

whack-a-mole

A game to demonstrate lists, lifecycle methods, and fundamentals of React Native
JavaScript
3
star
84

tutorial-movie

A demo CLI app, powered by Gluegun
TypeScript
3
star
85

ignite-next-js

Ignite Next.js website boilerplate with Next.js, MySQL, and Github authentication
JavaScript
3
star
86

elavon-elixir

Native elixir client for USBank Elavon Converge API
Elixir
3
star
87

ignite-plugins

The Official Ignite Plugin Registry
3
star
88

eversign

Elixir wrapper for Eversign API
Elixir
2
star
89

MarqueeMark

Large text displayer.
Objective-C
2
star
90

reactotron-apisauce

Reactotron's apisauce plugin
TypeScript
2
star
91

msterymeat

mobx-state-tree proclivities
TypeScript
2
star
92

motion-conf

motion-conf is an easy to use configuration class generator for your RubyMotion apps.
Ruby
2
star
93

wpf-base

Base React Native Windows WPF project for bare bones use in upstream
C#
2
star
94

mobx-forge

TypeScript
2
star
95

cr-2024-intermediate-workshop-lessons

TypeScript
2
star
96

cr-2024-intermediate-workshop-template

TypeScript
2
star
97

orb-publish-docs

CircleCI Orb for publishing docs
Shell
1
star
98

TrainingAppDec2018

What we covered in the online Infinite Red Academy training on December 12, 2018
JavaScript
1
star
99

rmq-plugin-template

Template Repo for RMQ
HTML
1
star
100

solidarity-stacks

Community Solidarity files for well known software stacks
1
star