├── .editorconfig ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── package-lock.json ├── package.json ├── test.js └── types └── index.d.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | coverage.lcov 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | *.pid.lock 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # Bower dependency directory (https://bower.io/) 28 | bower_components 29 | 30 | # node-waf configuration 31 | .lock-wscript 32 | 33 | # Compiled binary addons (https://nodejs.org/api/addons.html) 34 | build/Release 35 | 36 | # Dependency directories 37 | node_modules/ 38 | jspm_packages/ 39 | 40 | # TypeScript v1 declaration files 41 | typings/ 42 | 43 | # Optional npm cache directory 44 | .npm 45 | .tap 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # next.js build output 63 | .next 64 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "20" 5 | - "18" 6 | - "16" 7 | - "14" 8 | - "12" 9 | - "10" 10 | 11 | install: 12 | - npm install -g codecov && npm install 13 | 14 | script: 15 | - npm run coverage 16 | - npm run report-coverage 17 | 18 | notifications: 19 | email: 20 | on_success: never 21 | on_failure: always 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Davide D'Antonio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fastify-axios 2 | 3 | [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/) [![codecov](https://codecov.io/gh/davidedantonio/fastify-axios/branch/master/graph/badge.svg)](https://codecov.io/gh/davidedantonio/fastify-axios) 4 | 5 | A plugin for Fastify that adds support for sending requests via [`axios`](https://github.com/axios/axios), a promise based HTTP(s) client for node.js and browser. 6 | 7 | Under the hood `axios` http client is used, the options passed to register will be used as the default arguments while creating the axios http client. 8 | 9 | ## Install 10 | 11 | ``` 12 | npm install fastify-axios 13 | ``` 14 | 15 | ## Default usage 16 | 17 | Just add it to the project generated via [`fastify-cli`](https://github.com/fastify/fastify-cli) or with `register` in you application as below. 18 | 19 | You can access the `axios` instance via fastify.axios, which can be used to send HTTP(s) requests via methods `GET`, `POST`, `PUT`, `DELETE` etc. Here a simple example 20 | 21 | ```javascript 22 | "use strict"; 23 | 24 | module.exports = async function (fastify, opts) { 25 | fastify.register(require("fastify-axios")); 26 | 27 | // request via axios.get 28 | const { data, status } = await fastify.axios.get("https://nodejs.org/en/"); 29 | console.log("body size: %d", data.length); 30 | console.log("status: %d", status); 31 | }; 32 | ``` 33 | 34 | Alternatively you can specify default args to your axios instance. You can take a look at the default parameters here [https://github.com/axios/axios](https://github.com/axios/axios#request-config): 35 | 36 | ```javascript 37 | "use strict"; 38 | 39 | module.exports = async function (fastify, opts) { 40 | fastify.register(require("fastify-axios"), { 41 | baseURL: "https://nodejs.org", 42 | }); 43 | 44 | // request via axios.get to https://nodejs.org/en/ 45 | const { data, status } = await fastify.axios.get("/en/"); 46 | console.log("body size: %d", data.length); 47 | console.log("status: %d", status); 48 | }; 49 | ``` 50 | 51 | ## Add more clients 52 | 53 | It's possibile to add more than one `axios` client to your fastify instance. Here's how: 54 | 55 | ```javascript 56 | "use strict"; 57 | 58 | module.exports = async function (fastify, opts) { 59 | fastify.register(require("fastify-axios"), { 60 | clients: { 61 | v1: { 62 | baseURL: "https://v1.api.com", 63 | headers: { 64 | Authorization: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJxyz", 65 | }, 66 | }, 67 | v2: { 68 | baseURL: "https://v2.api.com", 69 | headers: { 70 | Authorization: "Bearer UtOkO3UI9lPY1h3h9ygTn8pD0Va2pFDcWCNbSKlf2HE", 71 | }, 72 | }, 73 | }, 74 | }); 75 | 76 | // Now you can use the apis in the following way 77 | const { dataV1, statusV1 } = await fastify.axios.v1.get("/ping"); 78 | const { dataV2, statusV2 } = await fastify.axios.v2.get("/ping"); 79 | 80 | // do something with dataV1 and dataV2 81 | }; 82 | ``` 83 | 84 | ## License 85 | 86 | Licensed under [MIT](./LICENSE) 87 | 88 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | const axios = require('axios') 5 | 6 | async function fastifyAxios (fastify, opts) { 7 | const { clients = {}, ...defaultArgs } = opts 8 | let interceptors = null 9 | 10 | if (defaultArgs.interceptors) { 11 | interceptors = defaultArgs.interceptors 12 | delete defaultArgs.interceptors 13 | } 14 | 15 | const instance = axios.create(defaultArgs) 16 | 17 | if (interceptors) { 18 | if (interceptors.request && interceptors.errorRequest) { 19 | instance.interceptors.request.use(interceptors.request, interceptors.errorRequest) 20 | } 21 | 22 | if (interceptors.response && interceptors.errorResponse) { 23 | instance.interceptors.response.use(interceptors.response, interceptors.errorResponse) 24 | } 25 | } 26 | 27 | if (Object.keys(clients).length !== 0) { 28 | for (const name of Object.keys(clients)) { 29 | const axiosInstance = axios.create(clients[name]) 30 | 31 | if (clients[name].interceptors) { 32 | if (clients[name].interceptors.request && clients[name].interceptors.errorRequest) { 33 | axiosInstance.interceptors.request.use(clients[name].interceptors.request, clients[name].interceptors.errorRequest) 34 | } 35 | 36 | if (clients[name].interceptors.response && clients[name].interceptors.errorResponse) { 37 | axiosInstance.interceptors.response.use(clients[name].interceptors.response, clients[name].interceptors.errorResponse) 38 | } 39 | } 40 | 41 | Object.assign(instance, { 42 | [name]: axiosInstance 43 | }) 44 | } 45 | } 46 | 47 | fastify.decorate('axios', instance) 48 | } 49 | 50 | module.exports = fp(fastifyAxios, { 51 | fastify: '>= 2.0.0', 52 | name: 'fastify-axios' 53 | }) 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fastify-axios", 3 | "version": "1.3.0", 4 | "description": "Decorate fastify instance with axios http client", 5 | "type": "commonjs", 6 | "types": "types/index.d.ts", 7 | "main": "index.js", 8 | "scripts": { 9 | "lint": "standard", 10 | "unit": "tap test.js", 11 | "coverage": "npm run unit -- --cov", 12 | "test": "npm run lint && npm run unit", 13 | "report-coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/davidedantonio/fastify-axios.git" 18 | }, 19 | "keywords": [ 20 | "fastify", 21 | "axios", 22 | "nodejs" 23 | ], 24 | "author": "Davide D'Antonio ", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/davidedantonio/fastify-axios/issues" 28 | }, 29 | "tap": {}, 30 | "homepage": "https://github.com/davidedantonio/fastify-axios#readme", 31 | "dependencies": { 32 | "axios": "^1.7.8", 33 | "fastify-plugin": "^4.5.1" 34 | }, 35 | "devDependencies": { 36 | "@fastify/pre-commit": "^2.2.0", 37 | "fastify": "^4.24.3", 38 | "standard": "^17.1.0", 39 | "tap": "^18.6.1" 40 | }, 41 | "pre-commit": [ 42 | "lint", 43 | "test" 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = require('tap') 4 | const { test } = t 5 | const Fastify = require('fastify') 6 | 7 | const axiosPlugin = require('./index') 8 | 9 | test('fastify.axios intance and instance.methods exists', t => { 10 | t.plan(12) 11 | const fastify = Fastify() 12 | fastify.register(axiosPlugin, {}) 13 | 14 | fastify.ready(err => { 15 | t.error(err) 16 | t.ok(fastify.axios) 17 | t.ok(fastify.axios.post) 18 | t.ok(fastify.axios.get) 19 | t.ok(fastify.axios.patch) 20 | t.ok(fastify.axios.options) 21 | t.ok(fastify.axios.put) 22 | t.ok(fastify.axios.getUri) 23 | t.ok(fastify.axios.delete) 24 | t.ok(fastify.axios.head) 25 | t.ok(fastify.axios.request) 26 | t.equal(fastify.axios.defaults.defaultArgs, undefined) 27 | 28 | fastify.close() 29 | }) 30 | }) 31 | 32 | test('fastify.axios should be an axios instance', t => { 33 | t.plan(2) 34 | const fastify = Fastify() 35 | fastify.register(axiosPlugin) 36 | 37 | fastify.ready(err => { 38 | t.error(err) 39 | t.ok(fastify.axios instanceof Function) 40 | 41 | fastify.close() 42 | }) 43 | }) 44 | 45 | test('verify default args', t => { 46 | t.plan(4) 47 | const fastify = Fastify() 48 | fastify.register(axiosPlugin, { 49 | baseURL: 'https://nodejs.org', 50 | interceptors: { 51 | request: function (config) { 52 | console.log(config) 53 | }, 54 | errorRequest: function (error) { 55 | console.log(error) 56 | }, 57 | response: function (config) { 58 | console.log(config) 59 | }, 60 | errorResponse: function (error) { 61 | console.log(error) 62 | } 63 | } 64 | }) 65 | 66 | fastify.ready(async err => { 67 | t.error(err) 68 | t.equal(fastify.axios.defaults.baseURL, 'https://nodejs.org') 69 | t.equal(typeof fastify.axios.interceptors.request, 'object') 70 | t.equal(typeof fastify.axios.interceptors.response, 'object') 71 | 72 | fastify.close() 73 | }) 74 | }) 75 | 76 | test('fastify.axios works well', t => { 77 | t.plan(3) 78 | const fastify = Fastify() 79 | fastify.register(axiosPlugin) 80 | 81 | fastify.ready(async err => { 82 | t.error(err) 83 | 84 | const { data, status } = await fastify.axios.get('https://nodejs.org/en/') 85 | t.ok(data) 86 | t.equal(status, 200) 87 | 88 | fastify.close() 89 | }) 90 | }) 91 | 92 | test('fastify.axios register multiple clients', t => { 93 | t.plan(26) 94 | const fastify = Fastify() 95 | 96 | fastify.register(axiosPlugin, { 97 | clients: { 98 | nodejs: { 99 | baseURL: 'https://nodejs.org' 100 | }, 101 | google: { 102 | baseURL: 'https://google.com', 103 | interceptors: { 104 | request: function (config) { 105 | console.log(config) 106 | }, 107 | errorRequest: function (error) { 108 | console.log(error) 109 | }, 110 | response: function (config) { 111 | console.log(config) 112 | }, 113 | errorResponse: function (error) { 114 | console.log(error) 115 | } 116 | } 117 | } 118 | } 119 | }) 120 | 121 | fastify.ready(async err => { 122 | t.error(err) 123 | t.ok(fastify.axios.nodejs) 124 | t.ok(fastify.axios.nodejs instanceof Function) 125 | t.ok(fastify.axios.nodejs.post) 126 | t.ok(fastify.axios.nodejs.get) 127 | t.ok(fastify.axios.nodejs.patch) 128 | t.ok(fastify.axios.nodejs.options) 129 | t.ok(fastify.axios.nodejs.put) 130 | t.ok(fastify.axios.nodejs.getUri) 131 | t.ok(fastify.axios.nodejs.delete) 132 | t.ok(fastify.axios.nodejs.head) 133 | t.ok(fastify.axios.nodejs.request) 134 | t.equal(fastify.axios.nodejs.defaults.baseURL, 'https://nodejs.org') 135 | 136 | t.ok(fastify.axios.google) 137 | t.ok(fastify.axios instanceof Object) 138 | t.ok(fastify.axios.google instanceof Function) 139 | t.ok(fastify.axios.google.post) 140 | t.ok(fastify.axios.google.get) 141 | t.ok(fastify.axios.google.patch) 142 | t.ok(fastify.axios.google.options) 143 | t.ok(fastify.axios.google.put) 144 | t.ok(fastify.axios.google.getUri) 145 | t.ok(fastify.axios.google.delete) 146 | t.ok(fastify.axios.google.head) 147 | t.ok(fastify.axios.google.request) 148 | t.equal(fastify.axios.google.defaults.baseURL, 'https://google.com') 149 | 150 | fastify.close() 151 | }) 152 | }) 153 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | import { FastifyPluginCallback } from "fastify"; 2 | import { AxiosInstance, AxiosRequestConfig } from "axios"; 3 | 4 | declare module "fastify" { 5 | interface FastifyInstance { 6 | axios: AxiosInstance & Record; 7 | } 8 | } 9 | 10 | export interface AxiosInterceptors { 11 | request?: ( 12 | value: AxiosRequestConfig, 13 | ) => AxiosRequestConfig | Promise; 14 | errorRequest?: (error: any) => any; 15 | response?: (value: any) => any | Promise; 16 | errorResponse?: (error: any) => any; 17 | } 18 | 19 | export interface FastifyAxiosClientOptions extends AxiosRequestConfig { 20 | interceptors?: AxiosInterceptors; 21 | } 22 | 23 | export interface FastifyAxiosOptions extends AxiosRequestConfig { 24 | clients?: Record; 25 | interceptors?: AxiosInterceptors; 26 | } 27 | 28 | declare const fastifyAxios: FastifyPluginCallback; 29 | 30 | export default fastifyAxios; 31 | --------------------------------------------------------------------------------