mirror of https://github.com/sundowndev/http.git
feat: migrate to http and ky-universal
commit
d3e2c085d6
|
@ -1,7 +1,7 @@
|
|||
<!--
|
||||
IMPORTANT: Please use the following link to create a new issue:
|
||||
|
||||
https://cmty.app/nuxt/issues/new?repo=axios-module
|
||||
https://cmty.app/nuxt/issues/new?repo=http-module
|
||||
|
||||
If your issue was not created using the app above, it will be closed immediately.
|
||||
-->
|
||||
|
|
22
README.md
22
README.md
|
@ -1,4 +1,4 @@
|
|||
# 📦 HTTP Module
|
||||
# HTTP Module
|
||||
|
||||
[![npm version][npm-version-src]][npm-version-href]
|
||||
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
||||
|
@ -7,25 +7,7 @@
|
|||
[![Dependencies][david-dm-src]][david-dm-href]
|
||||
[![Standard JS][standard-js-src]][standard-js-href]
|
||||
|
||||
TODO
|
||||
|
||||
## ✅ Features
|
||||
|
||||
✓ Automatically set base URL for client & server side
|
||||
|
||||
✓ Exposes `setToken` function to `$axios` so we can easily and globally set authentication tokens
|
||||
|
||||
✓ Automatically enables `withCredentials` when requesting to base URL
|
||||
|
||||
✓ Proxy request headers in SSR (Useful for auth)
|
||||
|
||||
✓ Integrated with Nuxt.js Progressbar while making requests (TODO)
|
||||
|
||||
✓ Integrated with [Proxy Module](https://github.com/nuxt-community/proxy-module)
|
||||
|
||||
✓ Auto retry requests
|
||||
|
||||
📖 [**Read Documentation**](https://http.nuxtjs.org) (TODO)
|
||||
📖 [**Read Documentation**](https://http.nuxtjs.org)
|
||||
|
||||
## Development
|
||||
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env', {
|
||||
targets: {
|
||||
esmodules: true
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
module.exports = {
|
||||
extends: [
|
||||
'@commitlint/config-conventional'
|
||||
]
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
.vuepress/dist
|
|
@ -0,0 +1,29 @@
|
|||
module.exports = {
|
||||
title: 'HTTP Module',
|
||||
description: 'Universal HTTP Module for Nuxt',
|
||||
themeConfig: {
|
||||
repo: 'nuxt/http',
|
||||
docsDir: 'docs',
|
||||
editLinks: true,
|
||||
displayAllHeaders: true,
|
||||
sidebar: [
|
||||
{
|
||||
collapsable: false,
|
||||
children: [
|
||||
'/',
|
||||
'setup',
|
||||
'usage',
|
||||
'options',
|
||||
'advanced',
|
||||
'migration'
|
||||
]
|
||||
}
|
||||
],
|
||||
nav: [
|
||||
{
|
||||
text: 'Release Notes',
|
||||
link: 'https://github.com/nuxt/http/blob/dev/CHANGELOG.md'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
# 📦 Axios Module
|
||||
|
||||
> Secure and Easy [Axios](https://github.com/mzabriskie/axios) integration with Nuxt.js.
|
||||
|
||||
## Features
|
||||
|
||||
✓ Automatically set base URL for client & server side
|
||||
|
||||
✓ Exposes `setToken` function to `$axios` so we can easily and globally set authentication tokens
|
||||
|
||||
✓ Automatically enables `withCredentials` when requesting to base URL
|
||||
|
||||
✓ Proxy request headers in SSR (Useful for auth)
|
||||
|
||||
✓ Fetch Style requests
|
||||
|
||||
✓ Integrated with Nuxt.js Progressbar while making requests
|
||||
|
||||
✓ Integrated with [Proxy Module](https://github.com/nuxt-community/proxy-module)
|
||||
|
||||
✓ Auto retry requests with [axios-retry](https://github.com/softonic/axios-retry)
|
||||
|
||||
## Links
|
||||
|
||||
* [GitHub](https://github.com/nuxt/http-module)
|
||||
* [Release Notes](./CHANGELOG.md)
|
||||
* [Migration Guide](migration.md)
|
||||
* [Examples](https://axios.nuxtjs.org/usage.html)
|
||||
|
||||
> 👉 To get started head to [Setup](setup.md) section.
|
|
@ -1,9 +0,0 @@
|
|||
# Summary
|
||||
|
||||
* [Setup](setup.md)
|
||||
* [Usage](usage.md)
|
||||
* [Extending axios](extend.md)
|
||||
* [Helpers](helpers.md)
|
||||
* [Options](options.md)
|
||||
* [Migration Guide](migration.md)
|
||||
* [Changelog](../CHANGELOG.md)
|
|
@ -0,0 +1,101 @@
|
|||
# Advanced
|
||||
|
||||
## Hooks
|
||||
|
||||
Sometimes we want to globally intercept HTTP request and responses.
|
||||
for example display a toast on error or log them or dynamically modify requests.
|
||||
|
||||
HTTP module provides helpers to register hooks for request lifecycle:
|
||||
|
||||
- `onRequest(config)`
|
||||
- `onResponse(response)`
|
||||
- `onError(err)` (`err.response` may be available on response errors)
|
||||
|
||||
These functions don't have to return anything by default.
|
||||
|
||||
### Register Hooks
|
||||
|
||||
For registering hooks, you have to create a nuxt plugin:
|
||||
|
||||
**nuxt.config.js**
|
||||
|
||||
```js
|
||||
{
|
||||
modules: [
|
||||
'@nuxt/http',
|
||||
],
|
||||
|
||||
plugins: [
|
||||
'~/plugins/http'
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**plugins/http.js**
|
||||
|
||||
```js
|
||||
export default function ({ $http }) {
|
||||
$http.onRequest(config => {
|
||||
console.log('Making request to ' + config.url)
|
||||
})
|
||||
|
||||
$http.onError(error => {
|
||||
if(error.response.status === 500) {
|
||||
alert('Request Error!')
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Header Helpers
|
||||
|
||||
### `setHeader(name, value)`
|
||||
|
||||
Globally set a header to all subsequent requests.
|
||||
|
||||
> NOTE: This method should not be called inside hooks as it is global
|
||||
|
||||
Parameters:
|
||||
|
||||
* **name**: Name of the header
|
||||
* **value**: Value of the header
|
||||
|
||||
```js
|
||||
// Add header `Authorization: 123` to all requests
|
||||
this.$http.setHeader('Authorization', '123')
|
||||
|
||||
// Override `Authorization` header with new value
|
||||
this.$http.setHeader('Authorization', '456')
|
||||
|
||||
// Add header `Content-Type: application/x-www-form-urlencoded`
|
||||
this.$http.setHeader('Content-Type', 'application/x-www-form-urlencoded')
|
||||
|
||||
// Remove default Content-Type header
|
||||
this.$http.setHeader('Content-Type', false)
|
||||
```
|
||||
|
||||
### `setToken(token, type)`
|
||||
|
||||
Globally set `Authorization` header to all subsequent requests.
|
||||
|
||||
Parameters:
|
||||
|
||||
* **token**: Authorization token
|
||||
* **type**: Authorization token prefix, usually `Bearer`. Defaults to nothing
|
||||
|
||||
```js
|
||||
// Adds header: `Authorization: 123` to all requests
|
||||
this.$http.setToken('123')
|
||||
|
||||
// Overrides `Authorization` header with new value
|
||||
this.$http.setToken('456')
|
||||
|
||||
// Adds header: `Authorization: Bearer 123` to all requests
|
||||
this.$http.setToken('123', 'Bearer')
|
||||
|
||||
// Adds header: `Authorization: Bearer 123` to only post and delete requests
|
||||
this.$http.setToken('123', 'Bearer', ['post', 'delete'])
|
||||
|
||||
// Removes default Authorization header
|
||||
this.$http.setToken(false)
|
||||
```
|
|
@ -1,34 +0,0 @@
|
|||
## Extending Axios
|
||||
|
||||
If you need to customize axios by registering interceptors and changing global config, you have to create a nuxt plugin.
|
||||
|
||||
**nuxt.config.js**
|
||||
|
||||
```js
|
||||
{
|
||||
modules: [
|
||||
'@nuxt/http',
|
||||
],
|
||||
|
||||
plugins: [
|
||||
'~/plugins/axios'
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**plugins/axios.js**
|
||||
|
||||
```js
|
||||
export default function ({ $axios, redirect }) {
|
||||
$axios.onRequest(config => {
|
||||
console.log('Making request to ' + config.url)
|
||||
})
|
||||
|
||||
$axios.onError(error => {
|
||||
const code = parseInt(error.response && error.response.status)
|
||||
if (code === 400) {
|
||||
redirect('/400')
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
|
@ -1,96 +0,0 @@
|
|||
## Helpers
|
||||
|
||||
### Interceptors
|
||||
|
||||
Axios plugin provides helpers to register axios interceptors easier and faster.
|
||||
|
||||
- `onRequest(config)`
|
||||
- `onResponse(response)`
|
||||
- `onError(err)`
|
||||
- `onRequestError(err)`
|
||||
- `onResponseError(err)`
|
||||
|
||||
These functions don't have to return anything by default.
|
||||
|
||||
Example: (`plugins/axios.js`)
|
||||
|
||||
```js
|
||||
export default function ({ $axios, redirect }) {
|
||||
$axios.onError(error => {
|
||||
if(error.response.status === 500) {
|
||||
redirect('/sorry')
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Fetch Style requests
|
||||
|
||||
Axios plugin also supports fetch style requests with `$` prefixed methods:
|
||||
|
||||
```js
|
||||
// Normal usage with axios
|
||||
let data = (await $axios.get('...')).data
|
||||
|
||||
// Fetch Style
|
||||
let data = await $axios.$get('...')
|
||||
```
|
||||
|
||||
### `setHeader(name, value, scopes='common')`
|
||||
|
||||
Axios instance has a helper to easily set any header.
|
||||
|
||||
Parameters:
|
||||
|
||||
* **name**: Name of the header
|
||||
* **value**: Value of the header
|
||||
* **scopes**: Send only on specific type of requests. Defaults
|
||||
* Type: _Array_ or _String_
|
||||
* Defaults to `common` meaning all types of requests
|
||||
* Can be `get`, `post`, `delete`, ...
|
||||
|
||||
```js
|
||||
// Adds header: `Authorization: 123` to all requests
|
||||
this.$axios.setHeader('Authorization', '123')
|
||||
|
||||
// Overrides `Authorization` header with new value
|
||||
this.$axios.setHeader('Authorization', '456')
|
||||
|
||||
// Adds header: `Content-Type: application/x-www-form-urlencoded` to only post requests
|
||||
this.$axios.setHeader('Content-Type', 'application/x-www-form-urlencoded', [
|
||||
'post'
|
||||
])
|
||||
|
||||
// Removes default Content-Type header from `post` scope
|
||||
this.$axios.setHeader('Content-Type', false, ['post'])
|
||||
```
|
||||
|
||||
### `setToken(token, type, scopes='common')`
|
||||
|
||||
Axios instance has an additional helper to easily set global authentication header.
|
||||
|
||||
Parameters:
|
||||
|
||||
* **token**: Authorization token
|
||||
* **type**: Authorization token prefix(Usually `Bearer`).
|
||||
* **scopes**: Send only on specific type of requests. Defaults
|
||||
* Type: _Array_ or _String_
|
||||
* Defaults to `common` meaning all types of requests
|
||||
* Can be `get`, `post`, `delete`, ...
|
||||
|
||||
```js
|
||||
// Adds header: `Authorization: 123` to all requests
|
||||
this.$axios.setToken('123')
|
||||
|
||||
// Overrides `Authorization` header with new value
|
||||
this.$axios.setToken('456')
|
||||
|
||||
// Adds header: `Authorization: Bearer 123` to all requests
|
||||
this.$axios.setToken('123', 'Bearer')
|
||||
|
||||
// Adds header: `Authorization: Bearer 123` to only post and delete requests
|
||||
this.$axios.setToken('123', 'Bearer', ['post', 'delete'])
|
||||
|
||||
// Removes default Authorization header from `common` scope (all requests)
|
||||
this.$axios.setToken(false)
|
||||
```
|
|
@ -0,0 +1,53 @@
|
|||
# Migration Guide
|
||||
|
||||
If you are migrating from [axios-module](https://github.com/nuxt-community/axios-module) this guide may be useful.
|
||||
|
||||
- There is no scope for `setHeader`, `setToken`. Scope is common which means being applied to all requests.
|
||||
- `onRequestError` and `onResponseError` hooks removed. Use `onError` instead.
|
||||
- `debug` option has been removed. You can setup a basic logger using `onRequest` hook.
|
||||
- The is no longer progress bar integration due to the lack of support from `fetch` spec. This option may be back after KY support of [`onProgress`](https://github.com/sindresorhus/ky/pull/34)
|
||||
|
||||
This module is using [ky](https://github.com/sindresorhus/ky) amd [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). There are breaking changes for usage and making requests.
|
||||
|
||||
## Parsing response body
|
||||
|
||||
Despite axios that does this automatically, you have to call specific methods to parse reponse body.
|
||||
|
||||
```diff
|
||||
-- const resJson = await this.$axios.get('/url')
|
||||
++ const resJson = await this.$http.get('/url').json()
|
||||
```
|
||||
|
||||
There is also a shortcut for JSON by using `$` prefix on request method name.
|
||||
|
||||
```js
|
||||
const resJson = await this.$http.$get('/url')
|
||||
```
|
||||
|
||||
Supported response types:
|
||||
|
||||
- `json`
|
||||
- `text`
|
||||
- `formData`
|
||||
- `arrayBuffer`
|
||||
- `blob`
|
||||
|
||||
## Sending requests with body
|
||||
|
||||
Despire axios, fetch and ky always accept **two** arguments for making requests (input and options). You have to pass request body in options:
|
||||
|
||||
For plain data or `Body`:
|
||||
|
||||
```diff
|
||||
-- this.$axios.post('/url', 'some data')
|
||||
++ this.$http.post('/url', { body: 'some data' })
|
||||
```
|
||||
|
||||
For JSON:
|
||||
|
||||
```diff
|
||||
-- this.$axios.post('/url', { name: 'foo' })
|
||||
++ this.$http.post('/url', { json: { name: 'foo' } })
|
||||
```
|
||||
|
||||
* `json` is a shortcut to `body` that sets `content-type` header and serializes JSON object.
|
|
@ -1,8 +1,16 @@
|
|||
## Options
|
||||
# Options
|
||||
|
||||
You can pass options using module options or `axios` section in `nuxt.config.js`
|
||||
You can pass options using module options or `http` section in `nuxt.config.js`
|
||||
|
||||
### `prefix`, `host` and `port`
|
||||
```js
|
||||
{
|
||||
http: {
|
||||
// HTTP options here
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `prefix`, `host`, `port`
|
||||
|
||||
This options are used for default values of `baseURL` and `browserBaseURL`.
|
||||
|
||||
|
@ -10,7 +18,7 @@ Can be customized with `API_PREFIX`, `API_HOST` (or `HOST`) and `API_PORT` (or `
|
|||
|
||||
Default value of `prefix` is `/`.
|
||||
|
||||
### `baseURL`
|
||||
## `baseURL`
|
||||
|
||||
* Default: `http://[HOST]:[PORT][PREFIX]`
|
||||
|
||||
|
@ -20,7 +28,7 @@ Environment variable `API_URL` can be used to **override** `baseURL`.
|
|||
|
||||
**Note:** `baseURL` and `proxy` doesn't work together, you need to use `prefix` instead.
|
||||
|
||||
### `browserBaseURL`
|
||||
## `browserBaseURL`
|
||||
|
||||
* Default: `baseURL` (or `prefix` when `options.proxy` is enabled)
|
||||
|
||||
|
@ -28,29 +36,17 @@ Base URL which is used and prepended to make requests in client side.
|
|||
|
||||
Environment variable `API_URL_BROWSER` can be used to **override** `browserBaseURL`.
|
||||
|
||||
### `https`
|
||||
## `https`
|
||||
|
||||
* Default: `false`
|
||||
|
||||
If set to `true`, `http://` in both `baseURL` and `browserBaseURL` will be changed into `https://`.
|
||||
|
||||
### `progress`
|
||||
|
||||
* Default: `true`
|
||||
|
||||
Integrate with Nuxt.js progress bar to show a loading bar while making requests. (Only on browser, when loading bar is available.)
|
||||
|
||||
You can also disable progress bar per request using `progress` config.
|
||||
|
||||
```js
|
||||
this.$axios.$get('URL', { progress: false })
|
||||
```
|
||||
|
||||
### `proxy`
|
||||
## `proxy`
|
||||
|
||||
* Default: `false`
|
||||
|
||||
You can easily integrate Axios with [Proxy Module](https://github.com/nuxt-community/proxy-module) and is much recommended to prevent CORS and deployment problems.
|
||||
You can easily integrate HTTP with [Proxy Module](https://github.com/nuxt-community/proxy-module) and is much recommended to prevent CORS and deployment problems.
|
||||
|
||||
**nuxt.config.js**
|
||||
|
||||
|
@ -60,7 +56,7 @@ You can easily integrate Axios with [Proxy Module](https://github.com/nuxt-commu
|
|||
'@nuxt/http'
|
||||
],
|
||||
|
||||
axios: {
|
||||
http: {
|
||||
proxy: true // Can be also an object with default options
|
||||
},
|
||||
|
||||
|
@ -81,44 +77,31 @@ proxy: {
|
|||
}
|
||||
```
|
||||
|
||||
### `retry`
|
||||
## `retry`
|
||||
|
||||
* Default: `false`
|
||||
|
||||
Automatically intercept failed requests and retries them whenever posible using [axios-retry](https://github.com/softonic/axios-retry).
|
||||
Automatically intercept failed requests and retry before failing.
|
||||
|
||||
By default, number of retries will be **3 times**, if `retry` value is set to `true`. You can change it by passing an object like this:
|
||||
By default, number of retries will be **2 times**, if `retry` value is set to `true`. You can change it by passing an object like this:
|
||||
|
||||
```js
|
||||
axios: {
|
||||
retry: { retries: 3 }
|
||||
http: {
|
||||
retry: 1
|
||||
}
|
||||
```
|
||||
|
||||
### `credentials`
|
||||
|
||||
* Default: `false`
|
||||
|
||||
Adds an interceptor to automatically set `withCredentials` config of axios when requesting to `baseURL`
|
||||
which allows passing authentication headers to backend.
|
||||
|
||||
### `debug`
|
||||
|
||||
* Default: `false`
|
||||
|
||||
Adds interceptors to log request and responses.
|
||||
|
||||
### `proxyHeaders`
|
||||
## `proxyHeaders`
|
||||
|
||||
* Default: `true`
|
||||
|
||||
In SSR context, sets client request header as axios default request headers.
|
||||
In SSR context, sets client request header as http 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.
|
||||
|
||||
> **NOTE:** If directing requests at a url protected by CloudFlare's CDN you should set this to false to prevent CloudFlare from mistakenly detecting a reverse proxy loop and returning a 403 error.
|
||||
|
||||
### `proxyHeadersIgnore`
|
||||
## `proxyHeadersIgnore`
|
||||
|
||||
* Default `['host', 'accept']`
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vuepress dev",
|
||||
"build": "vuepress build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vuepress": "^1.0.0-alpha.44"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
# Introduction
|
||||
|
||||
HTTP module for Nuxt.js provides a universal way to make HTTP requests to the API backend.
|
||||
|
||||
This module is a successor of [Axios Module](https://github.com/nuxt-community/axios-module) and behind the scenes use [ky-universal](https://github.com/sindresorhus/ky-universal) and [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to make HTTP requests. Please see [migration guide](./migration) if currently using axios module.
|
||||
|
||||
Starting with v2.5.0, Nuxt.js has built-in support for universal fetch. Using this module has serveral advantages and is mondatory in most of real-world use cases.
|
||||
|
||||
- Fluent [ky](https://github.com/sindresorhus/ky) API with more enhancenments and shortcuts
|
||||
- Highly customizable options support for BaseURL
|
||||
- Automatically proxy cookies and headers when making requests from server side
|
||||
- Best practices to avoid token sharing while making server side requests
|
||||
- Easy proxy support to avoid CORS problems and making deployment easier
|
||||
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
## Setup
|
||||
# Setup
|
||||
|
||||
Install with yarn:
|
||||
|
||||
|
@ -21,7 +20,7 @@ module.exports = {
|
|||
'@nuxt/http',
|
||||
],
|
||||
|
||||
axios: {
|
||||
http: {
|
||||
// proxyHeaders: false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,35 +1,92 @@
|
|||
## Usage
|
||||
# Usage
|
||||
|
||||
### Component
|
||||
## Making Requests
|
||||
|
||||
**`asyncData`**
|
||||
Available HTTP methods:
|
||||
|
||||
- `get`
|
||||
- `post`
|
||||
- `put`
|
||||
- `patch`
|
||||
- `head`
|
||||
- `delete`
|
||||
|
||||
For making a request use `$http.<method>(<url>, <options>)`. Returns a Promise that either rejects in case of network errors or resolves to a [Reponse](https://developer.mozilla.org/en-US/docs/Web/API/Response) object. You can use methods to convert response stream into usable data:
|
||||
|
||||
- `json`
|
||||
- `text`
|
||||
- `formData`
|
||||
- `arrayBuffer`
|
||||
- `blob`
|
||||
|
||||
**Example: Fetch a json file**
|
||||
|
||||
```js
|
||||
async asyncData({ $axios }) {
|
||||
const ip = await $axios.$get('http://icanhazip.com')
|
||||
await $http.get('https://unpkg.com/nuxt/package.json').json()
|
||||
```
|
||||
|
||||
Alternatively for json only you can use `$` prefixed shortcut:
|
||||
|
||||
```js
|
||||
await $http.$get('https://unpkg.com/nuxt/package.json')
|
||||
```
|
||||
|
||||
See [ky](https://github.com/sindresorhus/ky) docs for all available options.
|
||||
|
||||
### Sending Body
|
||||
|
||||
For sending body alongside with request, you can use either `json` or `body` options.
|
||||
`json` is a shortcut that serializes object using `JSON.stringify` and also sets appreciate `content-type` header.
|
||||
|
||||
**Example: Post with JSON body**
|
||||
|
||||
```js
|
||||
await $http.post('http://api.con', { json: { foo: 'bar' }})
|
||||
```
|
||||
|
||||
**Example: Post with FormData body**
|
||||
|
||||
```js
|
||||
const data = new FormData()
|
||||
data.append('name', 'foo')
|
||||
|
||||
await $http.post('http://api.com/submit', { data })
|
||||
```
|
||||
|
||||
## Using in `asyncData`
|
||||
|
||||
For `asyncData` and `fetch` you can access instance from context:
|
||||
|
||||
```js
|
||||
async asyncData({ $http }) {
|
||||
const ip = await $http.get('http://icanhazip.com').text()
|
||||
return { ip }
|
||||
}
|
||||
```
|
||||
|
||||
**`methods`/`created`/`mounted`/etc**
|
||||
## Using in Component Methods
|
||||
|
||||
Where you have access to `this`, you can use `this.$http`:
|
||||
|
||||
```js
|
||||
methods: {
|
||||
async fetchSomething() {
|
||||
const ip = await this.$axios.$get('http://icanhazip.com')
|
||||
const ip = await this.$http.get('http://icanhazip.com').text()
|
||||
this.ip = ip
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Store actions (including `nuxtServerInit`)
|
||||
## Using in Store
|
||||
|
||||
For store action you can also use `this.$http`:
|
||||
|
||||
```js
|
||||
// In store
|
||||
{
|
||||
actions: {
|
||||
async getIP ({ commit }) {
|
||||
const ip = await this.$axios.$get('http://icanhazip.com')
|
||||
const ip = await this.$http.get('http://icanhazip.com').text()
|
||||
commit('SET_IP', ip)
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +0,0 @@
|
|||
module.exports = {
|
||||
hooks: {
|
||||
'commit-msg': 'commitlint -E HUSKY_GIT_PARAMS',
|
||||
'pre-commit': 'yarn lint',
|
||||
'pre-push': 'yarn lint'
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
const path = require('path')
|
||||
const consola = require('consola')
|
||||
|
||||
const logger = consola.withScope('nuxt:axios')
|
||||
const logger = consola.withScope('nuxt:http')
|
||||
|
||||
function axiosModule(_moduleOptions) {
|
||||
function httpModule(_moduleOptions) {
|
||||
// Combine options
|
||||
const moduleOptions = { ...this.options.axios, ..._moduleOptions }
|
||||
const moduleOptions = { ...this.options.http, ..._moduleOptions }
|
||||
|
||||
// Default port
|
||||
const defaultPort =
|
||||
|
@ -35,9 +35,6 @@ function axiosModule(_moduleOptions) {
|
|||
const options = {
|
||||
baseURL: `http://${defaultHost}:${defaultPort}${prefix}`,
|
||||
browserBaseURL: null,
|
||||
credentials: false,
|
||||
debug: false,
|
||||
progress: true,
|
||||
proxyHeaders: true,
|
||||
proxyHeadersIgnore: ['accept', 'host', 'cf-ray', 'cf-connecting-ip'],
|
||||
proxy: false,
|
||||
|
@ -65,7 +62,9 @@ function axiosModule(_moduleOptions) {
|
|||
|
||||
// Normalize options
|
||||
if (options.retry === true) {
|
||||
options.retry = {}
|
||||
options.retry = 2
|
||||
} else if (!options.retry) {
|
||||
options.retry = 0
|
||||
}
|
||||
|
||||
// Convert http:// to https:// if https option is on
|
||||
|
@ -78,7 +77,7 @@ function axiosModule(_moduleOptions) {
|
|||
// Register plugin
|
||||
this.addPlugin({
|
||||
src: path.resolve(__dirname, 'plugin.js'),
|
||||
fileName: 'axios.js',
|
||||
fileName: 'http.js',
|
||||
options
|
||||
})
|
||||
|
||||
|
@ -90,12 +89,12 @@ function axiosModule(_moduleOptions) {
|
|||
])
|
||||
}
|
||||
|
||||
// Set _AXIOS_BASE_URL_ for dynamic SSR baseURL
|
||||
process.env._AXIOS_BASE_URL_ = options.baseURL
|
||||
// Set _HTTP_BASE_URL_ for dynamic SSR baseURL
|
||||
process.env._HTTP_BASE_URL_ = options.baseURL
|
||||
|
||||
logger.debug(`baseURL: ${options.baseURL}`)
|
||||
logger.debug(`browserBaseURL: ${options.browserBaseURL}`)
|
||||
}
|
||||
|
||||
module.exports = axiosModule
|
||||
module.exports = httpModule
|
||||
module.exports.meta = require('../package.json')
|
||||
|
|
240
lib/plugin.js
240
lib/plugin.js
|
@ -1,202 +1,102 @@
|
|||
import Axios from 'axios'
|
||||
<% if (options.retry) { %>import axiosRetry from 'axios-retry'<% } %>
|
||||
import KY from 'ky-universal'
|
||||
|
||||
// Axios.prototype cannot be modified
|
||||
const axiosExtra = {
|
||||
setHeader (name, value, scopes = 'common') {
|
||||
for (let scope of Array.isArray(scopes) ? scopes : [ scopes ]) {
|
||||
if (!value) {
|
||||
delete this.defaults.headers[scope][name];
|
||||
return
|
||||
}
|
||||
this.defaults.headers[scope][name] = value
|
||||
class HTTP {
|
||||
constructor(defaults, ky = KY) {
|
||||
this._defaults = {
|
||||
hooks: {},
|
||||
headers: {},
|
||||
retry: 0,
|
||||
...defaults
|
||||
}
|
||||
},
|
||||
setToken (token, type, scopes = 'common') {
|
||||
this._ky = ky
|
||||
}
|
||||
|
||||
setHeader(name, value) {
|
||||
if (!value) {
|
||||
delete this._defaults.headers[name];
|
||||
} else {
|
||||
this._defaults.headers[name] = value
|
||||
}
|
||||
}
|
||||
|
||||
setToken(token, type) {
|
||||
const value = !token ? null : (type ? type + ' ' : '') + token
|
||||
this.setHeader('Authorization', value, scopes)
|
||||
},
|
||||
this.setHeader('Authorization', value)
|
||||
}
|
||||
|
||||
_hook(name, fn) {
|
||||
if (!this._defaults.hooks[name]) {
|
||||
this._defaults.hooks[name] = []
|
||||
}
|
||||
this._defaults.hooks[name].push(fn)
|
||||
}
|
||||
|
||||
onRequest(fn) {
|
||||
this.interceptors.request.use(config => fn(config) || config)
|
||||
},
|
||||
this._hook('beforeRequest', fn)
|
||||
}
|
||||
|
||||
onResponse(fn) {
|
||||
this.interceptors.response.use(response => fn(response) || response)
|
||||
},
|
||||
onRequestError(fn) {
|
||||
this.interceptors.request.use(undefined, error => fn(error) || Promise.reject(error))
|
||||
},
|
||||
onResponseError(fn) {
|
||||
this.interceptors.response.use(undefined, error => fn(error) || Promise.reject(error))
|
||||
},
|
||||
this._hook('afterResponse', fn)
|
||||
}
|
||||
|
||||
onError(fn) {
|
||||
this.onRequestError(fn)
|
||||
this.onResponseError(fn)
|
||||
this._hook('onError', fn)
|
||||
}
|
||||
}
|
||||
|
||||
// Request helpers ($get, $post, ...)
|
||||
for (let method of ['request', 'delete', 'get', 'head', 'options', 'post', 'put', 'patch']) {
|
||||
axiosExtra['$' + method] = function () { return this[method].apply(this, arguments).then(res => res && res.data) }
|
||||
}
|
||||
for (let method of ['get', 'post', 'put', 'patch', 'head', 'delete']) {
|
||||
HTTP.prototype[method] = async function (input, options) {
|
||||
const _options = { ...this._defaults, ...options }
|
||||
|
||||
const extendAxiosInstance = axios => {
|
||||
for (let key in axiosExtra) {
|
||||
axios[key] = axiosExtra[key].bind(axios)
|
||||
}
|
||||
}
|
||||
if (/^https?/.test(input)) {
|
||||
delete _options.prefixUrl
|
||||
}
|
||||
|
||||
<% if (options.debug) { %>
|
||||
const log = (level, ...messages) => console[level]('[Axios]', ...messages)
|
||||
|
||||
const setupDebugInterceptor = axios => {
|
||||
// request
|
||||
axios.onRequestError(error => {
|
||||
log('error', 'Request error:', error)
|
||||
})
|
||||
|
||||
// response
|
||||
axios.onResponseError(error => {
|
||||
log('error', 'Response error:', error)
|
||||
})
|
||||
axios.onResponse(res => {
|
||||
log(
|
||||
'info',
|
||||
'[' + (res.status + ' ' + res.statusText) + ']',
|
||||
'[' + res.config.method.toUpperCase() + ']',
|
||||
res.config.url)
|
||||
|
||||
if (process.browser) {
|
||||
console.log(res)
|
||||
} else {
|
||||
console.log(JSON.stringify(res.data, undefined, 2))
|
||||
}
|
||||
|
||||
return res
|
||||
})
|
||||
}<% } %>
|
||||
|
||||
<% if (options.credentials) { %>
|
||||
const setupCredentialsInterceptor = axios => {
|
||||
// Send credentials only to relative and API Backend requests
|
||||
axios.onRequest(config => {
|
||||
if (config.withCredentials === undefined) {
|
||||
if (!/^https?:\/\//i.test(config.url) || config.url.indexOf(config.baseURL) === 0) {
|
||||
config.withCredentials = true
|
||||
try {
|
||||
const response = await this._ky[method](input, _options)
|
||||
return response
|
||||
} catch (error) {
|
||||
// Call onError hook
|
||||
if (_options.hooks.onError) {
|
||||
_options.hooks.onError.forEach(fn => fn(error))
|
||||
}
|
||||
// Throw error
|
||||
throw error
|
||||
}
|
||||
})
|
||||
}<% } %>
|
||||
|
||||
<% if (options.progress) { %>
|
||||
const setupProgress = (axios, ctx) => {
|
||||
if (process.server) {
|
||||
return
|
||||
}
|
||||
|
||||
// A noop loading inteterface for when $nuxt is not yet ready
|
||||
const noopLoading = {
|
||||
finish: () => { },
|
||||
start: () => { },
|
||||
fail: () => { },
|
||||
set: () => { }
|
||||
HTTP.prototype['$' + method] = function (input, options) {
|
||||
return this[method](input, options).then(res => res.json())
|
||||
}
|
||||
|
||||
const $loading = () => (window.$nuxt && window.$nuxt.$loading && window.$nuxt.$loading.set) ? window.$nuxt.$loading : noopLoading
|
||||
|
||||
let currentRequests = 0
|
||||
|
||||
axios.onRequest(config => {
|
||||
if (config && config.progress === false) {
|
||||
return
|
||||
}
|
||||
|
||||
currentRequests++
|
||||
})
|
||||
|
||||
axios.onResponse(response => {
|
||||
if (response && response.config && response.config.progress === false) {
|
||||
return
|
||||
}
|
||||
|
||||
currentRequests--
|
||||
if (currentRequests <= 0) {
|
||||
currentRequests = 0
|
||||
$loading().finish()
|
||||
}
|
||||
})
|
||||
|
||||
axios.onError(error => {
|
||||
if (error && error.config && error.config.progress === false) {
|
||||
return
|
||||
}
|
||||
|
||||
currentRequests--
|
||||
$loading().fail()
|
||||
$loading().finish()
|
||||
})
|
||||
|
||||
const onProgress = e => {
|
||||
if (!currentRequests) {
|
||||
return
|
||||
}
|
||||
const progress = ((e.loaded * 100) / (e.total * currentRequests))
|
||||
$loading().set(Math.min(100, progress))
|
||||
}
|
||||
|
||||
axios.defaults.onUploadProgress = onProgress
|
||||
axios.defaults.onDownloadProgress = onProgress
|
||||
}<% } %>
|
||||
}
|
||||
|
||||
export default (ctx, inject) => {
|
||||
// baseURL
|
||||
const baseURL = process.browser
|
||||
// prefixUrl
|
||||
const prefixUrl = process.browser
|
||||
? '<%= options.browserBaseURL %>'
|
||||
: (process.env._AXIOS_BASE_URL_ || '<%= options.baseURL %>')
|
||||
: (process.env._HTTP_BASE_URL_ || '<%= options.baseURL %>')
|
||||
|
||||
// Create fresh objects for all default header scopes
|
||||
// Axios creates only one which is shared across SSR requests!
|
||||
// https://github.com/mzabriskie/axios/blob/master/lib/defaults.js
|
||||
const headers = {
|
||||
common : {
|
||||
'Accept': 'application/json, text/plain, */*'
|
||||
},
|
||||
delete: {},
|
||||
get: {},
|
||||
head: {},
|
||||
post: {},
|
||||
put: {},
|
||||
patch: {}
|
||||
}
|
||||
|
||||
const axiosOptions = {
|
||||
baseURL,
|
||||
headers
|
||||
// Defaults
|
||||
const defaults = {
|
||||
retry: <%= parseInt(options.retry) %>,
|
||||
prefixUrl
|
||||
}
|
||||
|
||||
<% if (options.proxyHeaders) { %>
|
||||
// Proxy SSR request headers headers
|
||||
axiosOptions.headers.common = (ctx.req && ctx.req.headers) ? Object.assign({}, ctx.req.headers) : {}
|
||||
<% for (let h of options.proxyHeadersIgnore) { %>delete axiosOptions.headers.common['<%= h %>']
|
||||
defaults.headers = (ctx.req && ctx.req.headers) ? { ...ctx.req.headers } : {}
|
||||
<% for (let h of options.proxyHeadersIgnore) { %>delete defaults.headers['<%= h %>']
|
||||
<% } %><% } %>
|
||||
|
||||
if (process.server) {
|
||||
// Don't accept brotli encoding because Node can't parse it
|
||||
axiosOptions.headers.common['Accept-Encoding'] = 'gzip, deflate'
|
||||
defaults.headers['Accept-Encoding'] = 'gzip, deflate'
|
||||
}
|
||||
|
||||
// Create new axios instance
|
||||
const axios = Axios.create(axiosOptions)
|
||||
// Create new HTTP instance
|
||||
const http = new HTTP(defaults)
|
||||
|
||||
// Extend axios proto
|
||||
extendAxiosInstance(axios)
|
||||
|
||||
// Setup interceptors
|
||||
<% if (options.debug) { %>setupDebugInterceptor(axios) <% } %>
|
||||
<% if (options.credentials) { %>setupCredentialsInterceptor(axios)<% } %>
|
||||
<% if (options.progress) { %>setupProgress(axios, ctx) <% } %>
|
||||
<% if (options.retry) { %>axiosRetry(axios, <%= serialize(options.retry) %>)<% } %>
|
||||
|
||||
// Inject axios to the context as $axios
|
||||
ctx.$axios = axios
|
||||
inject('axios', axios)
|
||||
// Inject http to the context as $http
|
||||
ctx.$http = http
|
||||
inject('http', http)
|
||||
}
|
||||
|
|
19
package.json
19
package.json
|
@ -1,14 +1,12 @@
|
|||
{
|
||||
"name": "@nuxt/http",
|
||||
"version": "0.0.0",
|
||||
"description": "HTTP Module for Nuxt.js",
|
||||
"description": "Universal HTTP Module for Nuxt.js",
|
||||
"license": "MIT",
|
||||
"contributors": [
|
||||
"Pooya Parsa <pooya@pi0.ir>"
|
||||
],
|
||||
"author": "Pooya Parsa <pooya@pi0.ir>",
|
||||
"main": "lib/module.js",
|
||||
"types": "types/index.d.ts",
|
||||
"repository": "https://github.com/nuxt/http-module",
|
||||
"repository": "nuxt/http",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
|
@ -24,15 +22,13 @@
|
|||
],
|
||||
"dependencies": {
|
||||
"@nuxtjs/proxy": "^1.3.3",
|
||||
"axios": "^0.18.0",
|
||||
"axios-retry": "^3.1.2",
|
||||
"consola": "^2.5.6"
|
||||
"consola": "^2.5.6",
|
||||
"ky": "^0.9.0",
|
||||
"ky-universal": "^0.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "latest",
|
||||
"@babel/preset-env": "latest",
|
||||
"@commitlint/cli": "latest",
|
||||
"@commitlint/config-conventional": "latest",
|
||||
"@nuxtjs/eslint-config": "latest",
|
||||
"babel-eslint": "latest",
|
||||
"babel-jest": "latest",
|
||||
|
@ -45,9 +41,8 @@
|
|||
"eslint-plugin-promise": "latest",
|
||||
"eslint-plugin-standard": "latest",
|
||||
"eslint-plugin-vue": "latest",
|
||||
"husky": "latest",
|
||||
"jest": "latest",
|
||||
"nuxt-edge": "latest",
|
||||
"nuxt-edge": "^2.5.2-25896971.3b85dd97",
|
||||
"standard-version": "latest"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
const { Nuxt, Builder } = require('nuxt-edge')
|
||||
|
||||
const defaultConfig = require('./fixture/nuxt.config')
|
||||
|
||||
jest.setTimeout(60000)
|
||||
|
||||
async function setupNuxt(config) {
|
||||
const nuxt = new Nuxt({
|
||||
...defaultConfig,
|
||||
...config
|
||||
})
|
||||
|
||||
// Spy addTemplate
|
||||
nuxt.moduleContainer.addTemplate = jest.fn(nuxt.moduleContainer.addTemplate)
|
||||
|
||||
const builder = new Builder(nuxt)
|
||||
|
||||
await builder.validatePages()
|
||||
await builder.generateRoutesAndFiles()
|
||||
nuxt.builder = builder
|
||||
|
||||
await nuxt.ready()
|
||||
|
||||
return nuxt
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
setupNuxt
|
||||
}
|
|
@ -1,169 +0,0 @@
|
|||
jest.setTimeout(60000)
|
||||
|
||||
const { Nuxt, Builder } = require('nuxt-edge')
|
||||
const axios = require('axios')
|
||||
|
||||
const config = require('./fixture/nuxt.config')
|
||||
|
||||
let nuxt, addTemplate
|
||||
|
||||
const url = path => `http://localhost:3000${path}`
|
||||
|
||||
const setupNuxt = async (config) => {
|
||||
nuxt = new Nuxt(config)
|
||||
|
||||
// Spy addTemplate
|
||||
addTemplate = nuxt.moduleContainer.addTemplate = jest.fn(
|
||||
nuxt.moduleContainer.addTemplate
|
||||
)
|
||||
|
||||
const build = new Builder(nuxt)
|
||||
|
||||
await build.validatePages()
|
||||
await build.generateRoutesAndFiles()
|
||||
await nuxt.listen(3000)
|
||||
}
|
||||
|
||||
const testSuite = () => {
|
||||
test('baseURL', () => {
|
||||
expect(addTemplate).toBeDefined()
|
||||
const call = addTemplate.mock.calls.find(args => args[0].src.includes('plugin.js'))
|
||||
const options = call[0].options
|
||||
expect(options.baseURL.toString()).toBe('http://localhost:3000/test_api')
|
||||
expect(options.browserBaseURL.toString()).toBe('/test_api')
|
||||
})
|
||||
|
||||
test('asyncData', async () => {
|
||||
const html = (await axios.get(url('/asyncData'))).data
|
||||
expect(html).toContain('foo/bar')
|
||||
})
|
||||
|
||||
test('mounted', async () => {
|
||||
const window = await nuxt.renderAndGetWindow(url('/mounted'))
|
||||
window.onNuxtReady(() => {
|
||||
const html = window.document.body.innerHTML
|
||||
expect(html).toContain('foo/bar')
|
||||
})
|
||||
})
|
||||
|
||||
test('init', async () => {
|
||||
const window = await nuxt.renderAndGetWindow(url('/mounted'))
|
||||
window.onNuxtReady(() => {
|
||||
const $axios = window.$nuxt.$axios
|
||||
expect($axios.defaults.xsrfHeaderName).toBe('X-CSRF-TOKEN')
|
||||
})
|
||||
})
|
||||
|
||||
test('ssr', async () => {
|
||||
const makeReq = login => axios
|
||||
.get(url('/ssr' + (login ? '?login' : '')))
|
||||
.then(r => r.data)
|
||||
.then(h => /session-[0-9]+/.exec(h))
|
||||
.then(m => (m && m[0] ? m[0] : null))
|
||||
|
||||
const a = await makeReq()
|
||||
const b = await makeReq(true)
|
||||
const c = await makeReq()
|
||||
const d = await makeReq(true)
|
||||
|
||||
expect(a).toBeNull()
|
||||
expect(b).not.toBeNull()
|
||||
expect(c).toBeNull() // Important!
|
||||
expect(d).not.toBeNull()
|
||||
expect(b).not.toBe(d)
|
||||
})
|
||||
|
||||
test('ssr no brotli', async () => {
|
||||
const makeReq = login => axios
|
||||
.get(url('/ssr' + (login ? '?login' : '')))
|
||||
.then(r => r.data)
|
||||
.then(h => /encoding-\$(.*)\$/.exec(h))
|
||||
.then(m => (m && m[1] ? m[1] : null))
|
||||
|
||||
const result = await makeReq()
|
||||
|
||||
expect(result).toBe('gzip, deflate')
|
||||
})
|
||||
}
|
||||
|
||||
describe('module', () => {
|
||||
beforeAll(async () => {
|
||||
nuxt = new Nuxt(config)
|
||||
|
||||
// Spy addTemplate
|
||||
addTemplate = nuxt.moduleContainer.addTemplate = jest.fn(
|
||||
nuxt.moduleContainer.addTemplate
|
||||
)
|
||||
|
||||
await new Builder(nuxt).build()
|
||||
await nuxt.listen(3000)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await nuxt.close()
|
||||
})
|
||||
|
||||
testSuite()
|
||||
})
|
||||
|
||||
describe('other options', () => {
|
||||
beforeAll(async () => {
|
||||
config.axios = {
|
||||
prefix: '/test_api',
|
||||
proxy: {},
|
||||
credentials: true,
|
||||
https: true,
|
||||
retry: false
|
||||
}
|
||||
|
||||
await setupNuxt(config)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await nuxt.close()
|
||||
})
|
||||
|
||||
testSuite()
|
||||
})
|
||||
|
||||
describe('browserBaseURL', () => {
|
||||
beforeAll(async () => {
|
||||
config.axios = {
|
||||
browserBaseURL: '/test_api'
|
||||
}
|
||||
|
||||
await setupNuxt(config)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await nuxt.close()
|
||||
})
|
||||
|
||||
test('custom', () => {
|
||||
expect(addTemplate).toBeDefined()
|
||||
const call = addTemplate.mock.calls.find(args => args[0].src.includes('plugin.js'))
|
||||
const options = call[0].options
|
||||
expect(options.baseURL.toString()).toBe('http://localhost:3000/')
|
||||
expect(options.browserBaseURL.toString()).toBe('/test_api')
|
||||
})
|
||||
})
|
||||
|
||||
describe('empty config', () => {
|
||||
beforeAll(async () => {
|
||||
config.axios = {}
|
||||
|
||||
await setupNuxt(config)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await nuxt.close()
|
||||
})
|
||||
|
||||
test('preset baseURL and browserBaseURL', () => {
|
||||
expect(addTemplate).toBeDefined()
|
||||
const call = addTemplate.mock.calls.find(args => args[0].src.includes('plugin.js'))
|
||||
const options = call[0].options
|
||||
expect(options.baseURL.toString()).toBe('http://localhost:3000/')
|
||||
expect(options.browserBaseURL.toString()).toBe('http://localhost:3000/')
|
||||
})
|
||||
})
|
|
@ -0,0 +1,25 @@
|
|||
const { setupNuxt } = require('./_utils')
|
||||
|
||||
describe('browserBaseURL', () => {
|
||||
let nuxt
|
||||
|
||||
test('setup', async () => {
|
||||
nuxt = await setupNuxt({
|
||||
http: {
|
||||
browserBaseURL: '/test_api'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await nuxt.close()
|
||||
})
|
||||
|
||||
test('custom', () => {
|
||||
expect(nuxt.moduleContainer.addTemplate).toBeDefined()
|
||||
const call = nuxt.moduleContainer.addTemplate.mock.calls.find(args => args[0].src.includes('plugin.js'))
|
||||
const options = call[0].options
|
||||
expect(options.baseURL).toBe('http://localhost:3000/')
|
||||
expect(options.browserBaseURL).toBe('/test_api')
|
||||
})
|
||||
})
|
|
@ -0,0 +1,23 @@
|
|||
const { setupNuxt } = require('./_utils')
|
||||
|
||||
describe('empty config', () => {
|
||||
let nuxt
|
||||
|
||||
test('setup', async () => {
|
||||
nuxt = await setupNuxt({
|
||||
http: {}
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await nuxt.close()
|
||||
})
|
||||
|
||||
test('preset baseURL and browserBaseURL', () => {
|
||||
expect(nuxt.moduleContainer.addTemplate).toBeDefined()
|
||||
const call = nuxt.moduleContainer.addTemplate.mock.calls.find(args => args[0].src.includes('plugin.js'))
|
||||
const options = call[0].options
|
||||
expect(options.baseURL.toString()).toBe('http://localhost:3000/')
|
||||
expect(options.browserBaseURL.toString()).toBe('http://localhost:3000/')
|
||||
})
|
||||
})
|
|
@ -8,15 +8,18 @@ module.exports = {
|
|||
resourceHints: false
|
||||
},
|
||||
modules: [
|
||||
{ handler: require('../../') }
|
||||
require('../..')
|
||||
],
|
||||
serverMiddleware: ['~/api.js'],
|
||||
axios: {
|
||||
serverMiddleware: [
|
||||
'~/api.js'
|
||||
],
|
||||
http: {
|
||||
prefix: '/test_api',
|
||||
proxy: true,
|
||||
credentials: true,
|
||||
debug: true,
|
||||
retry: true
|
||||
retry: 1
|
||||
},
|
||||
plugins: ['~/plugins/axios']
|
||||
build: {
|
||||
terser: false
|
||||
},
|
||||
plugins: ['~/plugins/http']
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<script>
|
||||
export default {
|
||||
async asyncData({ app }) {
|
||||
let res = await app.$axios.$get('foo/bar')
|
||||
const res = await app.$http.$get('foo/bar')
|
||||
return {
|
||||
res
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ export default {
|
|||
|
||||
async mounted() {
|
||||
// Request with full url becasue we are in JSDom env
|
||||
this.res = await this.$axios.$get('http://localhost:3000/test_api/foo/bar')
|
||||
this.res = await this.$http.$get('http://localhost:3000/test_api/foo/bar')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
<template>
|
||||
<div>
|
||||
<div>session-{{ axiosSessionId }}</div>
|
||||
<div>encoding-${{ axiosEncoding }}$</div>
|
||||
<div>session-{{ httpSessionId }}</div>
|
||||
<div>encoding-${{ httpEncoding }}$</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// This will be intentically shared across requests
|
||||
let reqCtr = 1
|
||||
// This will be intentically shared across requests
|
||||
let reqCtr = 1
|
||||
|
||||
export default {
|
||||
async fetch({app, route}) {
|
||||
let doLogin = route.query.login !== undefined
|
||||
if (doLogin) {
|
||||
app.$axios.setHeader('sessionId', reqCtr++)
|
||||
}
|
||||
export default {
|
||||
computed: {
|
||||
httpSessionId() {
|
||||
return this.$http._defaults.headers.sessionId
|
||||
},
|
||||
computed: {
|
||||
axiosSessionId() {
|
||||
return this.$axios.defaults.headers.common.sessionId
|
||||
},
|
||||
|
||||
axiosEncoding() {
|
||||
return this.$axios.defaults.headers.common['Accept-Encoding']
|
||||
}
|
||||
httpEncoding() {
|
||||
return this.$http._defaults.headers['Accept-Encoding']
|
||||
}
|
||||
},
|
||||
fetch({ app, route }) {
|
||||
const doLogin = route.query.login !== undefined
|
||||
if (doLogin) {
|
||||
app.$http.setHeader('sessionId', reqCtr++)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
export default function ({ $axios, redirect }) {
|
||||
$axios.onRequest((config) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('SPY: ' + config.url)
|
||||
|
||||
$axios.defaults.xsrfHeaderName = 'X-CSRF-TOKEN'
|
||||
})
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
export default function ({ $http, redirect }) {
|
||||
$http.setHeader('xsrfHeaderName', 'X-CSRF-TOKEN')
|
||||
|
||||
$http.onRequest((options) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Request:', JSON.stringify(options))
|
||||
})
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
export default {
|
||||
actions: {
|
||||
nuxtServerInit({ commit }, ctx) {
|
||||
if (!ctx.$axios) {
|
||||
throw new Error('$axios is not defined!')
|
||||
if (!ctx.$http) {
|
||||
throw new Error('$http is not defined!')
|
||||
}
|
||||
|
||||
if (!ctx.app.$axios) {
|
||||
throw new Error('$axios is not defined!')
|
||||
if (!ctx.app.$http) {
|
||||
throw new Error('$http is not defined!')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
const fetch = require('node-fetch')
|
||||
const { setupNuxt } = require('./_utils')
|
||||
|
||||
const url = path => `http://localhost:3000${path}`
|
||||
|
||||
describe('module', () => {
|
||||
let nuxt
|
||||
|
||||
test('setup', async () => {
|
||||
nuxt = await setupNuxt()
|
||||
await nuxt.builder.build()
|
||||
await nuxt.listen(3000)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await nuxt.close()
|
||||
})
|
||||
|
||||
test('baseURL', () => {
|
||||
expect(nuxt.moduleContainer.addTemplate).toBeDefined()
|
||||
const call = nuxt.moduleContainer.addTemplate.mock.calls.find(args => args[0].src.includes('plugin.js'))
|
||||
const options = call[0].options
|
||||
expect(options.baseURL.toString()).toBe('http://localhost:3000/test_api')
|
||||
expect(options.browserBaseURL.toString()).toBe('/test_api')
|
||||
})
|
||||
|
||||
test('asyncData', async () => {
|
||||
const html = await fetch(url('/asyncData')).then(r => r.text())
|
||||
expect(html).toContain('foo/bar')
|
||||
})
|
||||
|
||||
test('mounted', async () => {
|
||||
const window = await nuxt.renderAndGetWindow(url('/mounted'))
|
||||
window.onNuxtReady(() => {
|
||||
const html = window.document.body.innerHTML
|
||||
expect(html).toContain('foo/bar')
|
||||
})
|
||||
})
|
||||
|
||||
test('init', async () => {
|
||||
const window = await nuxt.renderAndGetWindow(url('/mounted'))
|
||||
window.onNuxtReady(() => {
|
||||
const $http = window.$nuxt.$http
|
||||
expect($http._defaults.xsrfHeaderName).toBe('X-CSRF-TOKEN')
|
||||
})
|
||||
})
|
||||
|
||||
test('ssr', async () => {
|
||||
const makeReq = login => fetch(url('/ssr' + (login ? '?login' : '')))
|
||||
.then(r => r.text())
|
||||
.then(h => /session-[0-9]+/.exec(h))
|
||||
.then(m => (m && m[0] ? m[0] : null))
|
||||
|
||||
const a = await makeReq()
|
||||
const b = await makeReq(true)
|
||||
const c = await makeReq()
|
||||
const d = await makeReq(true)
|
||||
|
||||
expect(a).toBeNull()
|
||||
expect(b).not.toBeNull()
|
||||
expect(c).toBeNull() // Important!
|
||||
expect(d).not.toBeNull()
|
||||
expect(b).not.toBe(d)
|
||||
})
|
||||
|
||||
test('ssr no brotli', async () => {
|
||||
const makeReq = login => fetch(url('/ssr' + (login ? '?login' : '')))
|
||||
.then(r => r.text())
|
||||
.then(h => /encoding-\$(.*)\$/.exec(h))
|
||||
.then(m => (m && m[1] ? m[1] : null))
|
||||
|
||||
const result = await makeReq()
|
||||
|
||||
expect(result).toBe('gzip, deflate')
|
||||
})
|
||||
})
|
|
@ -1,35 +1,164 @@
|
|||
import { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||
import Vue from 'vue'
|
||||
import { ResponsePromise, Options, BeforeRequestHook, AfterResponseHook, HTTPError } from 'ky'
|
||||
import './vuex'
|
||||
|
||||
interface NuxtAxiosInstance extends AxiosInstance {
|
||||
$request<T = any>(config: AxiosRequestConfig): Promise<T>
|
||||
$get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>
|
||||
$delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>
|
||||
$head<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>
|
||||
$options<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>
|
||||
$post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
|
||||
$put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
|
||||
$patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
|
||||
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
||||
|
||||
setHeader(name: string, value?: string | false, scopes?: string | string[]): void
|
||||
setToken(token: string | false, type?: string, scopes?: string | string[]): void
|
||||
type JSONObject = { [key: string]: JSONValue };
|
||||
interface JSONArray extends Array<JSONValue> { }
|
||||
type JSONValue = string | number | boolean | null | JSONObject | JSONArray;
|
||||
|
||||
onRequest(callback: (config: AxiosRequestConfig) => void): void
|
||||
onResponse<T = any>(callback: (response: AxiosResponse<T>) => void): void
|
||||
onError(callback: (error: AxiosError) => void): void
|
||||
onRequestError(callback: (error: AxiosError) => void): void
|
||||
onResponseError(callback: (error: AxiosError) => void): void
|
||||
interface OptionsWithoutBody extends Omit<Options, 'body'> {
|
||||
method?: 'get' | 'head'
|
||||
}
|
||||
|
||||
interface OptionsWithBody extends Options {
|
||||
method?: 'post' | 'put' | 'delete'
|
||||
}
|
||||
|
||||
interface NuxtHTTPInstance {
|
||||
/**
|
||||
* Fetches the `input` URL with the option `{method: 'get'}`.
|
||||
*
|
||||
* @param input - `Request` object, `URL` object, or URL string.
|
||||
* @returns Promise with `Body` method added.
|
||||
*/
|
||||
get(input: Request | URL | string, options?: Omit<Options, 'body'>): ResponsePromise;
|
||||
|
||||
/**
|
||||
* Fetches the `input` URL with the option `{method: 'post'}`.
|
||||
*
|
||||
* @param input - `Request` object, `URL` object, or URL string.
|
||||
* @returns Promise with `Body` method added.
|
||||
*/
|
||||
post(input: Request | URL | string, options?: Options): ResponsePromise;
|
||||
|
||||
/**
|
||||
* Fetches the `input` URL with the option `{method: 'put'}`.
|
||||
*
|
||||
* @param input - `Request` object, `URL` object, or URL string.
|
||||
* @returns Promise with `Body` method added.
|
||||
*/
|
||||
put(input: Request | URL | string, options?: Options): ResponsePromise;
|
||||
|
||||
/**
|
||||
* Fetches the `input` URL with the option `{method: 'patch'}`.
|
||||
*
|
||||
* @param input - `Request` object, `URL` object, or URL string.
|
||||
* @returns Promise with `Body` method added.
|
||||
*/
|
||||
patch(input: Request | URL | string, options?: Options): ResponsePromise;
|
||||
|
||||
/**
|
||||
* Fetches the `input` URL with the option `{method: 'head'}`.
|
||||
*
|
||||
* @param input - `Request` object, `URL` object, or URL string.
|
||||
* @returns Promise with `Body` method added.
|
||||
*/
|
||||
head(input: Request | URL | string, options?: Omit<Options, 'body'>): ResponsePromise;
|
||||
|
||||
/**
|
||||
* Fetches the `input` URL with the option `{method: 'delete'}`.
|
||||
*
|
||||
* @param input - `Request` object, `URL` object, or URL string.
|
||||
* @returns Promise with `Body` method added.
|
||||
*/
|
||||
delete(input: Request | URL | string, options?: Options): ResponsePromise;
|
||||
|
||||
/**
|
||||
* Fetches the `input` URL with the option `{method: 'get'}`.
|
||||
*
|
||||
* @param input - `Request` object, `URL` object, or URL string.
|
||||
* @returns Promise that resolves to JSON parsed value.
|
||||
*/
|
||||
$get<T= JSONValue>(input: Request | URL | string, options?: Omit<Options, 'body'>): Promise<T>;
|
||||
|
||||
/**
|
||||
* Fetches the `input` URL with the option `{method: 'post'}`.
|
||||
*
|
||||
* @param input - `Request` object, `URL` object, or URL string.
|
||||
* @returns Promise that resolves to JSON parsed value.
|
||||
*/
|
||||
$post<T = JSONValue>(input: Request | URL | string, options?: Options): Promise<T>;
|
||||
|
||||
/**
|
||||
* Fetches the `input` URL with the option `{method: 'put'}`.
|
||||
*
|
||||
* @param input - `Request` object, `URL` object, or URL string.
|
||||
* @returns Promise that resolves to JSON parsed value.
|
||||
*/
|
||||
$put<T = JSONValue>(input: Request | URL | string, options?: Options): Promise<T>;
|
||||
|
||||
/**
|
||||
* Fetches the `input` URL with the option `{method: 'patch'}`.
|
||||
*
|
||||
* @param input - `Request` object, `URL` object, or URL string.
|
||||
* @returns Promise that resolves to JSON parsed value.
|
||||
*/
|
||||
$patch<T = JSONValue>(input: Request | URL | string, options?: Options): Promise<T>;
|
||||
|
||||
/**
|
||||
* Fetches the `input` URL with the option `{method: 'head'}`.
|
||||
*
|
||||
* @param input - `Request` object, `URL` object, or URL string.
|
||||
* @returns Promise that resolves to JSON parsed value.
|
||||
*/
|
||||
$head<T = JSONValue>(input: Request | URL | string, options?: Omit<Options, 'body'>): Promise<T>;
|
||||
|
||||
/**
|
||||
* Fetches the `input` URL with the option `{method: 'delete'}`.
|
||||
*
|
||||
* @param input - `Request` object, `URL` object, or URL string.
|
||||
* @returns Promise that resolves to JSON parsed value.
|
||||
*/
|
||||
$delete<T = JSONValue>(input: Request | URL | string, options?: Options): Promise<T>;
|
||||
|
||||
|
||||
/**
|
||||
* Set a header on all subsequent requests.
|
||||
* @param name - Header name.
|
||||
* @param value - Heade value.
|
||||
*/
|
||||
setHeader(name: string, value?: string | false): void
|
||||
|
||||
/**
|
||||
* Set `Authorization` header on all subsequent requests.
|
||||
* @param name - Header name.
|
||||
* @param value - Heade value.
|
||||
*/
|
||||
setToken(token: string | false, type?: string): void
|
||||
|
||||
|
||||
/**
|
||||
* Set a hook on `beforeRequest` (Before request is sent)
|
||||
*
|
||||
* This hook enables you to globally modify the requests right before it is sent. It will make no further changes to the request after this. The hook function receives the normalized options as the first argument. You could, for example, modify `options.headers` here.
|
||||
*/
|
||||
onRequest(hook: BeforeRequestHook): void
|
||||
|
||||
/**
|
||||
* Set a hook on `afterResponse` (After the response is received)
|
||||
*
|
||||
* This hook enables you to globally read and optionally modify the responses. The return value of the hook function will be used as the response object if it's an instance of [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response).
|
||||
*/
|
||||
onResponse(hook: AfterResponseHook): void
|
||||
|
||||
/**
|
||||
* Set a hook on `onError` (When request failed)
|
||||
*
|
||||
* This hook enables you to globally handle request errors.
|
||||
*/
|
||||
onError(hook: (HTTPError) => void): void
|
||||
}
|
||||
|
||||
declare module '@nuxt/vue-app' {
|
||||
interface Context {
|
||||
$axios: NuxtAxiosInstance
|
||||
$http: NuxtHTTPInstance
|
||||
}
|
||||
}
|
||||
|
||||
declare module 'vue/types/vue' {
|
||||
interface Vue {
|
||||
$axios: NuxtAxiosInstance
|
||||
$http: NuxtHTTPInstance
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { NuxtAxiosInstance } from '.'
|
||||
import { NuxtHTTPInstance } from '.'
|
||||
|
||||
declare module 'vuex' {
|
||||
interface Store<S> {
|
||||
$axios: NuxtAxiosInstance,
|
||||
$http: NuxtHTTPInstance,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue