{"id":19021602,"url":"https://github.com/codementorio/redux-api-middleman","last_synced_at":"2025-04-23T07:55:08.145Z","repository":{"id":53598410,"uuid":"61174226","full_name":"CodementorIO/redux-api-middleman","owner":"CodementorIO","description":"A Redux middleware making sending API a breeze","archived":false,"fork":false,"pushed_at":"2021-03-22T09:59:08.000Z","size":915,"stargazers_count":38,"open_issues_count":5,"forks_count":10,"subscribers_count":26,"default_branch":"master","last_synced_at":"2025-04-23T07:55:02.727Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CodementorIO.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-06-15T03:19:31.000Z","updated_at":"2023-06-07T00:21:00.000Z","dependencies_parsed_at":"2022-09-15T03:10:24.541Z","dependency_job_id":null,"html_url":"https://github.com/CodementorIO/redux-api-middleman","commit_stats":null,"previous_names":[],"tags_count":42,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodementorIO%2Fredux-api-middleman","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodementorIO%2Fredux-api-middleman/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodementorIO%2Fredux-api-middleman/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodementorIO%2Fredux-api-middleman/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CodementorIO","download_url":"https://codeload.github.com/CodementorIO/redux-api-middleman/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250395201,"owners_count":21423376,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-08T20:22:38.828Z","updated_at":"2025-04-23T07:55:08.121Z","avatar_url":"https://github.com/CodementorIO.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/CodementorIO/redux-api-middleman.svg?branch=master)](https://travis-ci.org/CodementorIO/redux-api-middleman)\n[![npm version](https://badge.fury.io/js/redux-api-middleman.svg)](https://www.npmjs.com/package/redux-api-middleman)\n[![minzip bundle size](https://img.shields.io/bundlephobia/minzip/redux-api-middleman.svg)](https://www.npmjs.com/package/redux-api-middleman) [![Greenkeeper badge](https://badges.greenkeeper.io/CodementorIO/redux-api-middleman.svg)](https://greenkeeper.io/)\n\n# Redux API Middleman\n\nA Redux middleware extracting the asynchronous behavior of sending API requests.\n\n# Usage\n\n## Get Started\n\n- Create the middleware and put into your middleware chain:\n\n```javascript\nimport { createStore, applyMiddleware } from 'redux'\nimport createApiMiddleman from 'redux-api-middleman'\n\nlet apiMiddleware = createApiMiddleman({\n  baseUrl: 'http://api.myapp.com',\n})\n\nconst store = applyMiddleware(\n  [ apiMiddleware ]\n)(createStore)()\n```\n\n- Use it in your action creators:\n\n```javascript\n// user action\n\nimport { CALL_API } from 'redux-api-middleman'\n\nexport const GETTING_MY_INFO = 'GETTING_MY_INFO'\nexport const GET_MY_INFO_SUCCESS = 'GET_MY_INFO_SUCCESS'\nexport const GET_MY_INFO_FAILED = 'GET_MY_INFO_FAILED'\n\nexport function getMyInfo() {\n  return {\n    [CALL_API]: {\n      method: 'get',\n      path: '/me',\n      sendingType: GETTING_MY_INFO,\n      successType: GET_CONTRACTS_SUCCESS,\n      errorType: GET_MY_INFO_FAILED\n    }\n  }\n}\n```\n\n- Handle it in your reducer:\n\n```javascript\n// user reducer\n\nimport { GET_CONTRACTS_SUCCESS } from 'actions/users'\n\nconst defaultState = {}\n\nexport default function(state = defaultState, action) {\n  switch(action.type) {\n    case GET_CONTRACTS_SUCCESS:\n      return action.response\n    default:\n      return state\n  }\n}\n\n```\n\nThe code above would send a `GET` request to `http://api.myapp.com/me`,\nwhen success, it would dispatch an action:\n\n```javascript\n{\n  type: GET_CONTRACTS_SUCCESS,\n  response: { the-camelized-response-body }\n}\n```\n\n# Features\n\n- Async to Sync: Abstract the async nature of sending API to make it easier to implement/test\n- Universal Rendering Friendly\n- Support chaining(successive) API calls\n- Side Effect Friendly\n- Replay request optionally when failed\n- Tweek request/response format when needed\n\n# API Documentation\n\n## Creation\n\nA middleware can be created like this:\n\n```javascript\nimport apiMiddleware from 'redux-api-middleman'\n\napiMiddleware({\n  baseUrl: 'https://api.myapp.com',\n  errorInterceptor: ({ err, proceedError, replay, getState })=\u003e {\n    // handle replay here\n  },\n  generateDefaultParams: ({ getState })=\u003e {\n    return {\n      headers: { 'X-Requested-From': 'my-killer-app' },\n    }\n  },\n  maxReplayTimes: 5\n})\n```\n\n### Options\n\n#### `baseUrl`: The base url of api calls(required)\n\n#### `errorInterceptor`(optional)\n\nWhen provided, this function would be invoked whenever an API call fails.\nThe function signature looks like this:\n\n```javascript\n({ err, proceedError, replay, getState })=\u003e {\n\n}\n```\n\nWhere:\n\n`err` is the error object returned by [`superagent`](https://visionmedia.github.io/superagent/),\n`replay()` can be used to replay the request with the same method/parameters,\n`proceedError()` can be used to proceed error to reducers\n\nFor example, to refresh access token when server responds 401:\n\n```javascript\n({ err, proceedError, replay, getState })=\u003e {\n  if(err.status === 401) {\n    refreshAccessToken().then((res)=\u003e {\n       // here you can pass additional headers if you want\n       let headers = {\n         'x-access-token': res.token,\n       }\n       replay({ headers })\n     })\n  } else {\n    proceedError()\n  }\n}\n```\n\nThe code above would do the token refreshing whenever err is 401,\nand proceed the original error otherwise.\n\n#### `generateDefaultParams`(optional)\n\nA function which takes `({ getState })` and returns an object like this:\n\n```javascript\n{\n  headers: { 'x-header-key': 'header-val' },\n  query: { queryKey: 'query-val' },\n  body: { bodyKey: 'body-val' }\n}\n```\n\nOn each request, the object returned by this function would be merged into the request's `header`, `query`, and `body`, respectively.\n\n----\n\n## Usage In Action Creators\n\nIn Action Creators, we can use the following code to send a single request:\n\n```javascript\nimport { CALL_API } from 'redux-api-middleman'\n\nexport const ON_REQUEST_SUCCESS = 'ON_REQUEST_SUCCESS'\nexport const ON_REQUEST_FAILED = 'ON_REQUEST_FAILED'\nexport const ON_SENDING_REQUEST = 'ON_SENDING_REQUEST'\n\nexport function getInfo({ username }) {\n  return {\n    extraKey: 'extra-val',\n\n    [CALL_API]: {\n      method: 'get',\n      path: `/users/${username}/info`,\n      successType: ON_REQUEST_SUCCESS,\n      errorType: ON_REQUEST_FAILED,\n      sendingType: ON_REQUEST_FAILED,\n      afterSuccess: ({ getState, dispatch, response }) =\u003e {\n        //...\n      },\n      afterError: ({ getState, error })=\u003e {\n        //...\n      }\n    }\n  }\n}\n```\n\nIn short, just return an action object with `CALL_API`.\n\n### Options\n\n### method(required)\nHttp verb to use, can be `get`, `post`, `put` or `del`\n\n### path(optional)\nRequest path to be concated with `baseUrl`\n\n### url\nFull url of request, will take precedence over `path` and will ignore `baseUrl`\n\n### camelizeResponse(optional)\nCamelize response keys of the request. default to `true`\n\nTransform `{ user_name: 'name' }` to `{ userName: 'name' }`\n\n### decamelizeRequest(optional)\nDecamelize request payload keys. default to `true`\n\nTransform `{ userName: 'name' }` to `{ user_name: 'name' }`\n\n### withCredentials(optional)\nEnable Access-Control requests or not. default to `true`\n\n### sendingType(optional)\nAction type to be dispatched immediately after sending the request\n\n### successType(required)\nAction type to be dispatched after the API call success\n\n### errorType(optional)\nAction type to be dispatched  after the API call fails\n\n### afterSuccess(optional)\nA callback function to be invoked after dispatching the action with type `successType`.\n`({ getState, dispatch, response })` would be passed into this callback function.\nThis is a good place to handle request-related side effects such as route pushing.\n\n### afterError(optional)\nA callback function to be invoked after dispatching the action with type `errorType`.\n`({ getState, error })` would be passed into this callback function.\n\n\n## Sending Chaining Requests\n\nTo send chaining requests, just return an action with `CHAIN_API`-keyed object like this:\n\n```javascript\nimport { CALL_API, CHAIN_API } from 'redux-api-middleman'\n\nexport const ON_REQUEST_SUCCESS1 = 'ON_REQUEST_SUCCESS1'\nexport const ON_REQUEST_SUCCESS2 = 'ON_REQUEST_SUCCESS2'\n\nexport function getInfo({ username }) {\n  return {\n    [CHAIN_API]: [\n      ()=\u003e {\n        return {\n          extraKey: 'extra-val',\n          [CALL_API]: {\n            method: 'get',\n            path: `/users/${username}/info`,\n            successType: ON_REQUEST_SUCCESS1\n          }\n        }\n      },\n      (responseOfFirstReq)=\u003e {\n        return {\n          [CALL_API]: {\n            method: 'get',\n            path: `/blogs/${responseOfFirstReq.blogId}`,\n            successType: ON_REQUEST_SUCCESS2\n          }\n        }\n      }\n    ]\n  }\n}\n```\n\nIn the code above, we send an API to `/users/${username}/info` to fetch user info containing a key `blogId`.\nAfter the first request is finished, we then send the second request with the `blogId` returned by server.\n\n---\n\n## Usage In Reducers\n\nDuring the life cycle of an API call, several types of actions would be dispatched:\n\n### `sendingType` action\n\nAfter the request has been sent, an action of type `sendingType` would be dispatched immediately.\nThe action would contain the key-val pairs other than `CALL_API` in the action object.\n\nFor example, if our action object looks like this:\n\n```javascript\n{\n  extraKey1: 'extra-val-1',\n  extraKey2: 'extra-val-2',\n  [CALL_API]: {\n    ...\n  }\n}\n```\n\nthen the `sendingType` action would be:\n\n```javascript\n{\n  type: sendingType,\n  extraKey1: 'extra-val-1',\n  extraKey2: 'extra-val-2'\n}\n```\n\n### `successType` action\n\nAfter the server responds successfully, an action of type `successType` would be dispatched.\nThe action would contain:\n\n- the key-val pairs other than `CALL_API` in the action object\n- an extra `response` key, with its value be the server response\n\nFor example, if the server responds with a body like this:\n\n```javascript\n{\n  responseKey: 'response-val'\n}\n```\n\nthen the `successType` action would be:\n\n```javascript\n{\n  type: successType,\n  extraKey1: 'extra-val-1',\n  extraKey2: 'extra-val-2',\n  response: {\n    responseKey: 'response-val'\n  }\n}\n```\n\n### `errorType` action\n\nAfter the server responds fails, an action of type `errorType` would be dispatched.\nThe action would contain:\n\n- the key-val pairs other than `CALL_API` in the action object\n- an extra `error` key, with its value be the error object returned by [`axios`](https://github.com/axios/axios)\n\n# LICENCE:\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodementorio%2Fredux-api-middleman","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodementorio%2Fredux-api-middleman","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodementorio%2Fredux-api-middleman/lists"}