I'm using Axios in my VueJS application and I want to add a default GET param in my request. I send my API-KEY through the URL ?api-key=secret and I don't want to specify this parameter each time.
I see in the documentation that we can set Global custom defaults. With that we don't have to specify the header each time. But can we do the same for get param ?
I struggled to get this to work with axios instances using the two most commonly suggested methods:
// method 1: setting axios.defaults.params at the class level
axios.defaults.params = {}
axios.defaults.params['api-key'] = secret
and
// method 2: setting the `params` attribute at an instance level
const axClient = axios.create({
baseURL: process.env.VUE_APP_BASE_URL,
params: {
api-key: process.env.VUE_APP_API_KEY
}
});
I did however, manage to get it working nicely using interceptors like this:
// create an instance with default properties
const axClient = axios.create({
baseURL: process.env.VUE_APP_BASE_URL,
});
axClient.interceptors.request.use((config) => {
// use config.params if it has been set
config.params = config.params || {};
// add any client instance specific params to config
config.params['api-key'] = process.env.VUE_APP_API_KEY;
return config;
});
The benefit of this approach is that the instance level params can be dynamic/computed per request if needed.
As a (slightly contrived) example, if you needed to add a JWT to each request (including any logic to fetch it from your storage method of choice) and even react to the logic around that. So in this toy example, if the user doesn't have a JWT in storage, redirect them to the login page instead of making the request.
Here it is:
axios.defaults.params = {}
axios.defaults.params['api-key'] = secret
If you need to call a function before each axios request, you should use an interceptor.
Related
Im looking to use $auth inside my Nuxt project, specially inside an axios plugin.
Here is my code:
plugins/api.js
export default function ({ $axios }, inject) {
const api = $axios.create({
headers: {
common: {
Accept: 'text/plain, */*',
},
},
})
// Set baseURL to something different
api.setBaseURL('http://localhost:4100/')
// Inject to context as $api
inject('api', api)
}
Now the problem comes when I try to use $auth from #nuxtjs/auth-next package.
As stated in the docs:
This module globally injects $auth instance, meaning that you can
access it anywhere using this.$auth. For plugins, asyncData, fetch,
nuxtServerInit and Middleware, you can access it from context.$auth.
I tried the following:
This results in $auth being undefined
export default function ({ $axios, $auth }, inject) {
This one was near
export default function ({ $axios, app }, inject) {
console.log(app) //This one logs $auth in the object logged
console.log(app.$auth) // I don't understand why but this one returns undefined
My main goal here is to make use of this.$auth.strategy.token.get()and pass it (if the token exists of course) to the headers of every request made using this.$api
I have been looking for similar questions and answers but none has helped me to solve this, I could just add the token every time I write this.$api but that would increase the code unnecessarily.
Thanks in advance to all the people for your time and help.
EDIT:
Okay, now I made a test. and the next code is actually logging the $auth object correctly, it seems some time is needed to make it work but now Im afraid that using setTimeout could cause an error because I can't know exactly how much time is needed for $auth to be available.
export default function ({ $axios, app }, inject) {
setTimeout(() => {
console.log('After timeout', app.$auth)
}, 50)
EDIT 2:
So now I have made more tests, and using 0 milliseconds instead of 50 works too, so I will use setTimeout with 0 milliseconds for now, I hope anyone find a better solution or explain why $auth is not available before using setTimeout so I can decide what to do with my code.
EDIT 3:
After trying to wrap all my previous code inside setTimeout I noticed that the code fails, so that isn't a solution.
I have found a solution so I will post it so that every person that could have the same problem in the future can solve it.
It turns out that I could easily solve it using interceptors.
export default function ({ $axios, app }, inject) {
// At this point app.$auth is undefined. (Unless you use setTimeout but that is not a solution)
//Create axios instance
const api = $axios.create({
headers: {
common: {
Accept: 'application/json', //accept json
},
},
})
// Here is the magic, onRequest is an interceptor, so every request made will go trough this, and then we try to access app.$auth inside it, it is defined
api.onRequest((config) => {
// Here we check if user is logged in
if (app.$auth.loggedIn) {
// If the user is logged in we can now get the token, we get something like `Bearer yourTokenJ9F0JFODJ` but we only need the string without the word **Bearer**, So we split the string using the space as a separator and we access the second position of the array **[1]**
const token = app.$auth.strategy.token.get().split(' ')[1]
api.setToken(token, 'Bearer') // Here we specify the token and now it works!!
}
})
// Set baseURL to something different
api.setBaseURL('http://localhost:4100/')
// Inject to context as $api
inject('api', api)
}
Also Nuxt Auth itself has provided a solution for this issue:
https://auth.nuxtjs.org/recipes/extend/
My API KEY = 2356yhtujkiw
I am using AXIOS on VUEJS as for get/post request.
API document says to add that API key as on header of all requests.
I tried setting it as axios.defaults.headers.common['header'] = '2356yhtujkiw'; but it did not work.
What's the proper way to define API KEY on header ?
it can get confusing when you're starting out, and certain terms, like header could mean different things depending on context.
There are several ways you can achieve axios calls in vue.
There's the "easy" one, where you add stuff right into a component
you can integrate it into vuex
create a custom helper/wrapper function (to use in a component or vuex)
use a the vue axios plugin
On top of that there's more than one ways to implement axios
reuse global instance
create new instance for every call
without seeing your code it's hard to know which way you're using it, but I'm going to try to give steps for a way that should make it easy enough to replicate
create a api.js in your src folder with:
import axios from 'axios'
let TOKEN = null;
export default {
setHeader(val){
TOKEN = val;
},
fetchUsers: () => {
const instance = axios.create({
baseURL: 'https://api.example.com',
headers: {
header: TOKEN
}
});
// or: instance.defaults.headers.common['header'] = TOKEN;
return instance.get('/users')
.then((result) => {
this.users = result.data
})
}
}
In a component, or Vuex, you can then...
import api from '../api.js'
// set header
api.setHeader('abc123')
// make api call
api.fetchUsers().then(r=> {console.log(r)});
This (though untested code) should work...
It's not the cleanest way of using it, but should be easy to implement in existing code.
TL;DR;
The reason axios.defaults.headers.common['header'] = '2356yhtujkiw'; doesn't work is likely because you've already created the instance, and are re-using it. Updating the default would only apply for subsequent instances created. The example above gets around that, by not using any defaults, and just inserting the headers in every new instance, which is created for every new call.
I am having trouble getting dependency injection working for my AuthorizerService. Obviously, dep-inj is not ready until after Aurelia "starts", but I wasn't sure how to access it.
main.js:
aurelia.container.registerInstance(HttpClient, http.c());
// set your interceptors to take cookie data and put into header
return aurelia.start().then(() => {
let Authorizer = new AuthorizerService();
aurelia.container.registerInstance(AuthorizerService, Authorization);
console.log('Current State: %o', Authorizer.auth);
Authorizer.checkCookieAndPingServer().then(() => { console.log('Current State: %o', Authorizer.auth); aurelia.setRoot(PLATFORM.moduleName('app')); }, () => { aurelia.setRoot(PLATFORM.moduleName('login-redirect')); });
});
Now the problem is that if I do "new AuthorizerService()" then "this.http.fetch()" is not available in AuthorizerService.js.
Am I meant to pass "http.c()" (which delivers the HttpClient instance) as a parameter inside:
checkCookieAndPingServer(http.c())
or is there another way?
Can I delete "new AuthorizerService()" and just do (I made this up):
aurelia.container.getInstance(AuthorizerService);
Somehow FORCE it to do dependency-injection and retrieve the "registered Instance" of "http.c()"?
I can't just check cookie. I have to ping server for security and the server will set the cookie.
I think this is all sorts of wrong, because I need a global parameter that just is false by default, then it does the query to backend server and setsRoot accordingly. Perhaps only query backend in the "login page"? Okay but then I would need to do "setRoot(backtoApp); aurelia.AlsoSetLoggedIn(true);" inside login module. But when I setRoot(backtoApp) then it just starts all over again.
In other words, when setRoot(login); then setRoot(backToApp); <-- then AuthorizerService instance doesn't have its proper data set (such as loggedIn=true).
EDIT: Better Solution maybe:
main.js:
return aurelia.start().then(() => {
let Authorizer = aurelia.container.get(AuthorizerService);
let root = Authorizer.isAuthenticated() ? PLATFORM.moduleName('app') : PLATFORM.moduleName('login');
console.log('Current State: %o', Authorizer.auth);
aurelia.setRoot(root);
});
Authorizer.js
constructor(http) {
this.http = http;
this.auth = {
isAuthenticated: false,
user: {}
}
}
"this.auth" is no longer static. No longer "static auth = { isAuthenticated: false }" which was some example code I had found.
So now "auth" gets set inside "login" module. But this means the "login" module is displayed every single time the app loads briefly, before being redirected back to "setRoot(backToApp)"
If the class you want to get the instance is purely based on service classes and has no dependencies on some Aurelia plugins, it doesn't need to wait until Aurelia has started to safely invoke the container.
For your example:
aurelia.container.getInstance(AuthorizerService);
It can be
aurelia.container.get(AuthorizerService);
And you should not use new AuthorizerService(), as you have noticed in your question.
I've created a HapiJS project, using my own MVC pattern.
When I want to log from inside my controllers in some cases. Currently when I want to log from my controllers I simply invoke request.log. I'm using Good as a logging plugin.
For example:
const user = function(req, res){
// do stuff
req.log(['info'], 'some log info here');
};
module.exports = {
user,
};
How can I log from inside my models where I have no request object? I don't want to have to pass in my request object into the methods of the model.
If you plan to register models as plug in, you will have access to the server object and so, you will be able to use server.methods
EDIT
In my company we declare routes as plug in (see code below)
exports.register = function (server, options, next) {
server.route({
method: 'POST',
path: '/FOO/BAR'
handler(request, reply) {}
});
return next();
};
exports.register.attributes = {
name: 'routes-foobar'
};
And we register as such :
server.register([
require('./route-foo-bar'),
...,
]);
This way we have the server objects in our route
What I would do in your case is register my models as server methods and use them in my routes.
The same goes for logging.
I would register my log function as a server method and call them from inside my models
I don't know if it's the good way to do that but that's a least a working one
In the main.js file i have set the vue resource to use auth headers with every requests:
Vue.use( VueResource )
let auth = validToken()
if( auth ) {
Vue.http.interceptors.push( ( request, next ) => {
request.headers.set( 'Authorization', auth.token )
request.headers.set( 'Accept', 'application/json' )
next()
} )
}
And in the logout, i am trying to delete the header this way,
resetVueRsr: () => {
this.$http.headers.common['Authorization'] = null
}
And when a user log in, then resetting the new token to the global Vue resource like this,
this.$http.headers.common['Authorization'] = res.body.token
But once a new user is logging in the site, the vue resource still sending previous token with each request. What i am missing here?
I checked the source, and it looks like all defined interceptors are added in some internal array, and then used for requests.
According to your code sample, you obtain an object with initial token somehow and then pass it inside your arrow function, so JS creates a closure for this to make this object available inside the function, and I think a lifetime of this closure is equal to a lifetime of your app. I assume that you have a sort of SPA.
Also from the source it looks like interceptors are not related to common headers, so your way of deleting and resetting won't change your interceptor.