christianmalek / vuex-rest-api

A utility to simplify the use of REST APIs with Vuex
http://vuex-rest-api.org
MIT License
382 stars 48 forks source link

How we can change Axios headers #34

Closed elceka closed 6 years ago

elceka commented 7 years ago

Hi Chris, please, how we can manage Axios headers at created Vapi instance? For example, if we need add Authorization header for API after successfully login of user or so on... Thank you.

christianmalek commented 7 years ago

At the moment it's not possible to change the properties of the passed axios instance.

What would be your favorite way to implement this? I thought about adding an additional param to the onSuccess and onError functions so that you can access the Vapi axios instance.

Example:

{
  onSuccess: (state, payload, axios) => {

    axios.defaults.headers.common['Authorization'] = payload.AUTH_TOKEN;
  }
}
elceka commented 7 years ago

Hi Chris, this solution is usable, but this change Axios instance in only one Resource. How we can change Axios instance from others Resources?

For example, we have Resource with name User. User resource have action Login and some others actions. Server API for Login action don't require an authentication HTTP header. If User login action has been successfully done, we can set authentication header for Axios instance of that User Resource, and after that we can call others User actions which server API requires authentication header, that is absolutely OK. But what, if we have more Resources and server API require authentication header for actions? We need set headers of all Axios instances of that all resources, but how?

I don't have any favorite way of implementation. But maybe we can use Axios interceptors, or adapters, ...

christianmalek commented 7 years ago

Per default vuex-rest-api uses the global axios instance. I didn't test it yet, but shouldn't this work with the implementation example I posted above?

If you need to use a specific axios instance you could pass it to every resource constructor so that changes in the axios instance from one resource would affect the other resources as well.

Also you would have access to the axios instance. So if you want to set an adapter or interceptor it would be no problem.

elceka commented 7 years ago

I use Vapi wrapper around Vapi for configuration some needed things and for configuration of Axios interceptors for handling with HTTP authentication header. All Resources is made on that wrapper. And then I use onSuccess function at the Resource User login action for store API token locally. Axios interceptors check local API token on each request and set HTTP header.

elceka commented 7 years ago

localAuth.js

const storeItem = 'auth'

export default {
  userIsAuthenticated () {
    return !!localStorage.getItem(storeItem)
  },
  storeUserData (user) {
    localStorage.setItem(storeItem, JSON.stringify(user))
  },
  getUserData () {
    if (this.userIsAuthenticated) {
      return JSON.parse(localStorage.getItem(storeItem))
    }

    return null
  },
  removeUserData () {
    localStorage.removeItem(storeItem)
  },
  apiToken () {
    if (this.userIsAuthenticated) {
      const usrData = JSON.parse(localStorage.getItem(storeItem))

      if (!!usrData) {
        if (usrData.hasOwnProperty('api_token')) {
          return usrData.api_token
        }
      }
    }

    return null
  }
}

ApiWrapper.js

import Axios from 'axios'
import { stringify } from 'qs'
import { Vapi } from 'vuex-rest-api'
import localAuth from 'localAuth'

const apiBaseURL = 'http://localhost:8003/api/frontend'

export default class VapiWrapper {
  constructor(baseURL = null, options = {}) {
    options.baseURL = baseURL || apiBaseURL

    options.axios = Axios.create({
      paramsSerializer: function ({limit, page, sort, filter_groups, includes, mode}) {
        return stringify(
          {limit, page, sort, filter_groups, includes, mode},
          {arrayFormat: 'indices'}
        )
      }
    })

    options.axios.interceptors.request.use(
      (config) => {
        config.headers.common['Accept'] = 'application/json'

        if (localAuth.userIsAuthenticated()) {
          config.headers.common['Authentication'] = 'Bearer ' + localAuth.apiToken()
        }

        return config
      },
      (response) => {
        return response
      }
    )

    options.queryParams = true

    return new Vapi(options)
  }
}

user.js

import localAuth from 'localAuth'
import VapiWrapper from 'VapiWrapper'

const resource = new VapiWrapper(
  null,
  {
    state: {
      user: null,
      users: []
    }
  }
)
.post({
  action: 'login',
  property: 'user',
  path: () => `/login`,
  onSuccess: (state, payload) => {
    localAuth.storeUserData(payload.data)
  }
})

const store = resource.getStore()

export {resource, store}
christianmalek commented 7 years ago

Mhm. I like this and think I'll adapt this Wrapper to the current version so that it's backwards-compatible. But don't expect it to happen this week. I'm short on time at the moment.

BTW: PRs are welcome. :)

apertureless commented 6 years ago

How about saving the axios instance on the vm like a plugin. You could then access it with $ and change your options.

christianmalek commented 6 years ago

@apertureless So you would say that the responsibility of storing the axios instance should be decoupled from vuex-rest-api and just store it in the vm? Seems reasonable to me.

apertureless commented 6 years ago

Yep. There are however various ways to to this. You could also do some kind of dependency injection / pass the axios instance in your plugin.

import Vapi from 'vuex-rest-api'
import axios from 'axios'

Vue.use(Vapi, {axios})

You should also think about marking axios as a peerDependency . The benefit is, that you don't have conflicting versions of axios in projects.

If I install Vapi in my project, which is using another version of axios and I am making somewhere else some requests I am using my axios instance with my version. If I am using Vapi it uses another version of axios which is installed with it. This could lead to problems.

And another benefit is, that you don't need to update the package with every axios release.

christianmalek commented 6 years ago

See https://github.com/christianmalek/vuex-rest-api/issues/35