@nuxtjs/axios
Advanced tools
Comparing version 1.0.2 to 2.0.0
@@ -6,2 +6,29 @@ # Change Log | ||
<a name="2.0.0"></a> | ||
# [2.0.0](https://github.com/nuxt/modules/compare/@nuxtjs/axios@1.0.2...@nuxtjs/axios@2.0.0) (2017-06-09) | ||
### Bug Fixes | ||
* **axios:** install using Vue.use ([184651b](https://github.com/nuxt/modules/commit/184651b)) | ||
* **axios:** req typo ([16f28b1](https://github.com/nuxt/modules/commit/16f28b1)) | ||
* **axios:** use relative `API_URL` if same host and port else `API_URL` ([3421d19](https://github.com/nuxt/modules/commit/3421d19)) | ||
### Features | ||
* **axios:** AXIOS_CREDENTIALS, AXIOS_SSR_HEADERS ([4dfdc2d](https://github.com/nuxt/modules/commit/4dfdc2d)) | ||
* **axios:** don't append optional config into env ([fe189e8](https://github.com/nuxt/modules/commit/fe189e8)) | ||
* **axios:** Easier API ([f54a434](https://github.com/nuxt/modules/commit/f54a434)) | ||
* **axios:** New API ([0194226](https://github.com/nuxt/modules/commit/0194226)) | ||
* **axios:** nuxt friendly errors for SSR ([65bc50f](https://github.com/nuxt/modules/commit/65bc50f)) | ||
### BREAKING CHANGES | ||
* **axios:** API_PREFIX is deprecated. | ||
<a name="1.0.2"></a> | ||
@@ -8,0 +35,0 @@ ## [1.0.2](https://github.com/nuxt/modules/compare/@nuxtjs/axios@1.0.0...@nuxtjs/axios@1.0.2) (2017-05-29) |
51
index.js
const chalk = require('chalk') | ||
const path = require('path') | ||
const { URL } = require('url') | ||
module.exports = function nuxtAxios(options) { | ||
// Register plugin | ||
this.addPlugin({src: path.resolve(__dirname, 'plugin.js'), options}) | ||
const port = process.env.PORT || process.env.npm_package_config_nuxt_port || 3000 | ||
const host = process.env.HOST || process.env.npm_package_config_nuxt_host || 'localhost' | ||
// API URL | ||
const API_URL = options.API_URL || process.env.API_URL || 'http://localhost:3000' | ||
process.env.API_URL = API_URL | ||
module.exports = function nuxtAxios (moduleOptions) { | ||
// Apply defaults | ||
const options = Object.assign({ | ||
baseURL: process.env.API_URL || `http://${host}:${port}/api`, | ||
browserBaseURL: null, | ||
credentials: true, | ||
proxyHeaders: true | ||
}, this.options.axios, moduleOptions) | ||
// API URL for Browser | ||
const API_URL_BROWSER = options.API_URL_BROWSER || process.env.API_URL_BROWSER || API_URL | ||
process.env.API_URL_BROWSER = API_URL_BROWSER | ||
if (process.env.API_URL) { | ||
options.baseURL = process.env.API_URL | ||
} | ||
// Common API Prefix | ||
const API_PREFIX = options.API_PREFIX || process.env.API_PREFIX || '/api' | ||
process.env.API_PREFIX = API_PREFIX | ||
if (process.env.API_URL_BROWSER) { | ||
options.browserBaseURL = process.env.API_URL_BROWSER | ||
} | ||
// Don't add env to production bundles | ||
if (process.env.NODE_ENV !== 'production') { | ||
this.options.env.API_URL = API_URL | ||
this.options.env.API_URL_BROWSER = API_URL_BROWSER | ||
if (!options.browserBaseURL) { | ||
const url = new URL(options.baseURL) | ||
const sameHost = url.host === `${host}:${port}` | ||
options.browserBaseURL = sameHost ? url.pathname : options.url | ||
} | ||
printURL('API URL', API_URL, API_PREFIX) | ||
// Register plugin | ||
this.addPlugin({ | ||
src: path.resolve(__dirname, 'plugin.js'), | ||
options | ||
}) | ||
if (API_URL_BROWSER !== API_URL) { | ||
printURL('API URL (Browser)', API_URL_BROWSER, API_PREFIX) | ||
} | ||
} | ||
function printURL(title, url, prefix) { | ||
/* eslint-disable no-console */ | ||
console.log(chalk.bgMagenta.black(` ${title} `) + chalk.magenta(` ${url}${prefix}`) + '\r\n') | ||
console.log(`[AXIOS] Base URL: ${chalk.green(options.baseURL)} , Browser: ${chalk.green(options.browserBaseURL)}`) | ||
} | ||
module.exports.meta = require('./package.json') |
{ | ||
"name": "@nuxtjs/axios", | ||
"version": "1.0.2", | ||
"version": "2.0.0", | ||
"license": "MIT", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
174
plugin.js
import Axios from 'axios' | ||
import Vue from 'vue' | ||
const inBrowser = typeof window !== 'undefined' | ||
const axiosPlugin = { | ||
install() { | ||
if(Vue.__nuxt_axios_installed__) { | ||
return | ||
} | ||
Vue.__nuxt_axios_installed__ = true | ||
const API_URL = inBrowser ? (process.env.API_URL_BROWSER || '') : (process.env.API_URL || 'http://localhost:3000') | ||
// Try to get axios from components context, fallback to global Axios else | ||
function getAxios() { | ||
return (this && this.$root && this.$root.$options.$axios) ? this.$root.$options.$axios : Axios | ||
} | ||
// Vue Component Mixins | ||
Vue.mixin({ | ||
computed: { | ||
$axios() { | ||
return getAxios.call(this) | ||
// Make `this.$axios` available | ||
if (!Vue.prototype.hasOwnProperty('$axios')) { | ||
// Add mixin to add this._axios | ||
Vue.mixin({ | ||
beforeCreate () { | ||
// Check if `axios` has been defined in App | ||
// Then fallback to $root.$axios | ||
// Finally use global instance of Axios | ||
this._axios = this.$options.axios || this.$root.$axios || Axios | ||
} | ||
}) | ||
// Add this.$axios instance | ||
Object.defineProperty(Vue.prototype, '$axios', { | ||
get () { | ||
return this._axios | ||
} | ||
}) | ||
} | ||
}, | ||
methods: { | ||
// opts | ||
$request(opts) { | ||
return getAxios.call(this).request(opts); | ||
}, | ||
// url, opts | ||
$get(url, opts) { | ||
return getAxios.call(this).get(url, opts); | ||
}, | ||
$delete(url, opts) { | ||
return getAxios.call(this).delete(url, opts); | ||
}, | ||
$head(url, opts) { | ||
return getAxios.call(this).head(url, opts); | ||
}, | ||
// url, data, opts | ||
$post(url, data, opts) { | ||
return getAxios.call(this).post(url, data, opts); | ||
}, | ||
$put(url, data, opts) { | ||
return getAxios.call(this).put(url, data, opts); | ||
}, | ||
$patch(url, data, opts) { | ||
return getAxios.call(this).patch(url, data, opts); | ||
} | ||
} | ||
}) | ||
// Send credentials only to relative and API Backend requests | ||
const withCredentials = config => { | ||
if (config.withCredentials === undefined) { | ||
if (!/^https?:\/\//i.test(config.url) || config.url.indexOf(process.env.API_URL_BROWSER) === 0 || config.url.indexOf(process.env.API_URL) === 0) { | ||
config.withCredentials = true | ||
} | ||
// Vue Component Mixins | ||
Vue.mixin({ | ||
methods: { | ||
// opts | ||
$request (opts) { | ||
return this.$axios.request(opts); | ||
}, | ||
// url, opts | ||
$get (url, opts) { | ||
return this.$axios.get(url, opts); | ||
}, | ||
$delete (url, opts) { | ||
return this.$axios.delete(url, opts); | ||
}, | ||
$head (url, opts) { | ||
return this.$axios.head(url, opts); | ||
}, | ||
// url, data, opts | ||
$post (url, data, opts) { | ||
return this.$axios.post(url, data, opts); | ||
}, | ||
$put (url, data, opts) { | ||
return this.$axios.put(url, data, opts); | ||
}, | ||
$patch (url, data, opts) { | ||
return this.$axios.patch(url, data, opts); | ||
} | ||
} | ||
}) | ||
} | ||
return config | ||
} | ||
// Nuxt friendly http errors | ||
const handleError = error => { | ||
// const response = error.response || {} | ||
// const config = error.config || {} | ||
// TODO: Add integration with vue notification and nuxt.$error | ||
// Avoid promise rejections in SSR context | ||
return inBrowser? Promise.reject(error) : error | ||
} | ||
Vue.use(axiosPlugin) | ||
// Set requests token | ||
function setToken(token, type = 'Bearer') { | ||
function setToken (token, type = 'Bearer') { | ||
if (!token) { | ||
@@ -76,17 +73,66 @@ delete this.defaults.headers.common.Authorization; | ||
export default ({app}) => { | ||
// Nuxt friendly error handler | ||
function errorHandler(error) { | ||
if (error.response) { | ||
// Error from backend (non 2xx status code) | ||
if (error.response.status === 401) { | ||
return this.redirect('/login') | ||
} | ||
error.statusCode = error.statusCode || parseInt(error.response.status) || 500 | ||
error.message = error.message || error.response.statusText || (error.statusCode + ' (Internal Server Error)') | ||
} else if (error.request) { | ||
// Error while making request | ||
error.statusCode = error.statusCode || 500 | ||
error.message = error.message || 'request error' | ||
} else { | ||
// Something happened in setting up the request that triggered an Error | ||
error.statusCode = 500 | ||
error.message = error.message || 'axios error' | ||
} | ||
// Display error page on unhandled promises | ||
if(process.browser) { | ||
return Promise.reject(error) | ||
} else { | ||
// Don't throw unhandled promises in SSR context | ||
return this.app._nuxt.error.call({$options: this.app}, { | ||
message: error.message, | ||
statusCode: error.statusCode | ||
}) | ||
} | ||
} | ||
export default (ctx) => { | ||
const { app, store, req } = ctx | ||
// Create new axios instance | ||
const baseURL = process.browser | ||
? (process.env.API_URL_BROWSER || '<%= options.browserBaseURL %>') | ||
: (process.env.API_URL || '<%= options.baseURL %>') | ||
const axios = Axios.create({ | ||
baseURL: API_URL + (process.env.API_PREFIX || '/api') | ||
baseURL, | ||
<% if(options.proxyHeaders) { %>headers: (req && req.headers) ? req.headers : {} <% } %> | ||
}) | ||
<% if(options.credentials) { %> | ||
// Send credentials only to relative and API Backend requests | ||
axios.interceptors.request.use(config => { | ||
if (config.withCredentials === undefined) { | ||
if (!/^https?:\/\//i.test(config.url) || config.url.indexOf(baseURL) === 0) { | ||
config.withCredentials = true | ||
} | ||
} | ||
return config | ||
}); | ||
<% } %> | ||
// Setup instance interceptors | ||
axios.interceptors.request.use(withCredentials); | ||
axios.interceptors.response.use(undefined, handleError); | ||
// Error handler | ||
axios.interceptors.response.use(undefined, errorHandler.bind(ctx)); | ||
// Make accessible using {this,$nuxt}.$root.$options.$axios | ||
// Make accessible using *.$axios | ||
app.$axios = axios | ||
store.$axios = axios | ||
ctx.$axios = axios | ||
// setToken helper | ||
// token helper for authentication | ||
axios.setToken = setToken.bind(axios) | ||
} |
148
README.md
# Axios | ||
This plugin is a wrapper around [axios](https://github.com/mzabriskie/axios). | ||
> Use [axios](https://github.com/mzabriskie/axios) with deep Nuxt integration and no pain! | ||
- Sets default base URL. | ||
- Handles all HTTP exceptions and prevents server side unhandled promise exceptions. | ||
- Automatically set base URL for client & server side | ||
- Injects `$get`,`$post`,... into vue context instances so requests can be done easily. | ||
- Exposes `setToken` function to `$axios` so we can easily and globally set authentication tokens. | ||
- Throws *nuxt-friendly* exceptions. | ||
- Automatically enables `withCredentials` when requesting to default base URL. | ||
- Throws *nuxt-friendly* exceptions and prevent SSR crashes. | ||
- Automatically enables `withCredentials` when requesting to base URL. | ||
- Proxy request headers in SSR. | ||
@@ -22,6 +22,6 @@ ## Setup | ||
### Inside `asyncData` | ||
### Component `asyncData` | ||
```js | ||
async asyncData({app: {$axios}}) { | ||
const {data} = await $axios.get('http://icanhazip.com') | ||
async asyncData({ app }) { | ||
const {data} = await app.$axios.get('http://icanhazip.com') | ||
return { | ||
@@ -33,4 +33,20 @@ ip: data | ||
### Inside component methods | ||
### Component mixins | ||
You can always access axios instance using `this.$axios`. | ||
This mixins are available for easier usage: | ||
- `$request` | ||
```js | ||
this.$request({url: 'http://example.com') | ||
``` | ||
- `$get`, `$delete`, `$head` | ||
```js | ||
this.$get('http://example.com') | ||
``` | ||
- `$post`, `$put`, `$patch` | ||
```js | ||
this.$post('http://example.com', { foo: 'bar' }) | ||
```` | ||
Example: | ||
```js | ||
async mounted() { | ||
@@ -42,10 +58,110 @@ const {data} = await this.$get('http://icanhazip.com') | ||
## Customization | ||
### Store `nuxtServerInit` | ||
```js | ||
async nuxtServerInit ({ commit }, { app }) { | ||
const { data } = await app.$axios.get('http://icanhazip.com') | ||
commit('SET_IP', data) | ||
} | ||
``` | ||
Customization can be done using shared environment variables. | ||
### Store actions | ||
If you need axios instance in store actions, you may have to pass it when dispatching. | ||
Environment variable | Default | Description | ||
---------------------|-------------------------|-------------------------------------------- | ||
API_URL | http://localhost:3000 | Base url for ajax requests in server-side | ||
API_URL_BROWSER | [API_URL] | Base url for ajax requests in client-side | ||
API_PREFIX | /api | Adds this prefix before all relative urls | ||
```js | ||
// In components | ||
export default { | ||
methods: { | ||
updateIP() { | ||
this.$store.dispatch('getIP', { $axios: this.$axios }) | ||
} | ||
} | ||
} | ||
// In store | ||
{ | ||
actions: { | ||
async getIP ({ commit }, { $axios }) { | ||
const { data } = await $axios.get('http://icanhazip.com') | ||
commit('SET_IP', data) | ||
} | ||
} | ||
} | ||
``` | ||
## Options | ||
You can pass options using module options or `axios` section in `nuxt.config.js`: | ||
### `baseURL` | ||
- Default: `http://[HOST]:[PORT]/api` | ||
Base URL is required for requests in server-side & SSR and prepended to all requests with relative path. | ||
You can also use environment variable `API_URL` which **overrides** `baseURL`. | ||
### `browserBaseURL` | ||
- Default: `/api` | ||
Base URL which is used in client side prepended to all requests with relative path. | ||
You can also use environment variable `API_URL_BROWSER` which **overrides** `browserBaseURL`. | ||
- If `browserBaseURL` is not provided it defaults to `baseURL` value. | ||
- If hostname & port of `browserbaseURL` are equal to nuxt server, it defaults to relative part of `baseURL`. | ||
So if your nuxt application is being accessed under a different domain, requests go to same origin and prevents Cross-Origin problems. | ||
### `credentials` | ||
- Default: `true` | ||
Adds an interceptor to automatically set `withCredentials` config of axios when requesting to `baseUrl` | ||
which allows passing authentication headers to backend. | ||
### `proxyHeaders` | ||
- Default: `true` | ||
In SSR context, sets client request header as axios default request headers. | ||
This is useful for making requests which need cookie based auth on server side. | ||
Also helps making consistent requests in both SSR and Client Side code. | ||
## Dynamic API Backend | ||
Please notice that, `API_URL` is saved into bundle on build, CANNOT be changed | ||
on runtime! You may use [proxy](../proxy) module for dynamically route api requests to different backend on test/staging/production. | ||
**Example: (`nuxt.config.js`)** | ||
```js | ||
{ | ||
modules: [ | ||
'@nuxtjs/axios', | ||
'@nuxtjs/proxy' | ||
], | ||
proxy: [ | ||
['/api', { target: 'http://www.mocky.io', pathRewrite: { '^/api': '/v2' } }] | ||
] | ||
} | ||
``` | ||
Start Nuxt | ||
``` | ||
[AXIOS] Base URL: http://localhost:3000/api | Browser: /api | ||
[HPM] Proxy created: /api -> http://www.mocky.io | ||
[HPM] Proxy rewrite rule created: "^/api" ~> "/v2" | ||
``` | ||
Now you can make requests to backend: (Works fine in both SSR and Browser) | ||
```js | ||
async asyncData({app}) { | ||
// Magically makes request to http://www.mocky.io/v2/59388bb4120000dc00a672e2 | ||
const {data: {nuxt} } = await app.$axios.get('59388bb4120000dc00a672e2') | ||
return { | ||
nuxt // -> 'Works!' | ||
} | ||
} | ||
``` | ||
Details | ||
- `'@nuxtjs/axios'` | ||
- By default axios plugin sets base url to `http://[host]:[port]/api` which is `http://localhost:3000/api` | ||
- `'/api': 'http://www.mocky.io/v2'` | ||
- This line creates a server middleware to pass requests from `/api` to `http://www.mocky.io/v2` | ||
- We used `pathRewrite` to remove `/api` from starting of requests and change it to `/v2` | ||
- For more information and advanced usage please refer to [proxy](../proxy) docs. |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 4 instances in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
12111
158
165
12