Passing response header in Vuetable api-mode - vue.js

I am wondering if there is a way to use api-mode and still be able to pass a response header such as an authorisation token.
Vuetable consumes this api-url prop and use it to make a GET request. How do I pass in a response header when this happens?
<vuetable
ref="vuetable"
:api-url="apiBase"
:query-params="makeQueryParams"
:per-page="perPage"
pagination-path
:reactive-api-url="true"
:fields="fields"
:row-class="onRowClass"
#vuetable:pagination-data="onPaginationData"
#vuetable:cell-rightclicked="rightClicked"
>
I am hoping to pass in a response header, if it was axios it would be done like this.
const response = await axios.get(this.apiBase, {
headers: { Authorization: `Bearer ${$cookies.get('token')}`}
})
The documentation does say that it can be done by setting api-mode to false and use data-mode. That way, I will have full control of the api and data. But that means that I would sacrifice query-params that enables me to make use of the library's pagination and sorting function. Generally I would lose a lot of built in functions of vuetable if I go with data-mode instead of api-mode, so I would much prefer to go with api-mode.
Relevant documentation: https://www.vuetable.com/api/vuetable/properties.html#api-mode

I have found the answer from this thread in github.
<vuetable :http-options="httpOptions" ...
and in JS
export default {
data() {
return {
httpOptions: { headers: { Authorization: 'Bearer ' + TOKEN } },
...

Related

How to use Nuxt $auth inside an axios plugin (How to add Token to all axios requests)

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/

Apollo client + Next.js - Adding Authorization token to client request

GOAL
I want to populate my Authorization header with an access token. I want to store that access token in the Apollo cache as Auth0 explicitly state not to store access tokens in local storage (I don't know why the Apollo Client docs seem to think it's okay).
Failing that, I want to store my access token securely and be able to add it to each Apollo Client request to the Apollo Server.
const apolloClient = withApollo(({ctx, headers, initialState}) => {
const cache = new InMemoryCache();
// Code here to access local cache.
return new ApolloClient({
cache,
link: new HttpLink({
uri: <apollo server endpoint>,
headers: {
Authorization: `Bearer ${accessToken}`,
'Access-Control-Allow-Origin': 'http://localhost:3000/',
...headers
},
credentials: 'include'
}),
typeDefs,
resolvers
})
})
class MyApp extends App {
render() {
const { Component, pageProps, apollo } = this.props;
return (
<ApolloProvider client={apollo}>
<Component {...pageProps} />
</ApolloProvider>
);
}
}
export default apolloClient(MyApp);
TRIED
I've tried to access localStorage from within the withApollo function but it is SSR'd so I'm unable to access that. I was going to use the localStorage to set a boolean to check in the withApollo function so it'll know the access token has been added to the apollo cache after the redirect.
I've tried just using the cache with a key storing a boolean to check if the user has signed in which is set in the same function where the access token. If that is true I'd access the access token key and add that to the Authorization header. But I was getting issues with setting the initial state overriding the cache set in sign in function.
OTHER IDEAS
I thought I could pass the access token through in the ctx argument, but I'm not familiar enough with Next.js to figure out if that's valid way to do it.
I thought I could pass in a props to the withApollo function from a component, but that doesn't seem like it'd be possible.
QUESTIONS
What is the best way to store the access key so as to add it to the Apollo Client with each request?
I noticed some people are using a fetch polyfill, would that be appropriate for this situation? If so, how would that work?
How does the withApollo HOC work and why is it needed to work with Next.js? I've read some of the code behind this, but I don't fundamentally understand why it is needed.
I was able to find a solution to my question. I just didn't fully understand the Apollo Client and how to use all of the required packages.
SOLUTION
I used setContext from the apollo-link-context library. It's part of the link set of libraries that Apollo Client offers to customise the network request after a graphql operation has started. I set the header in the setContext function. Like so:
const authLink = setContext((_, { headers }) => {
// It is also possible to use the local storage method in here.
const data = cache.readQuery({
query: ACCESS_TOKEN
});
return {
headers: {
...headers,
authorization: accessToken ? `Bearer ${data.accessToken}` : ""
}
}
});
The following is outside the withApollo function.
const httpLink = new HttpLink({
uri: '<server uri>',
});
The following is inside the withApollo function.
return new ApolloClient({
cache,
link: authLink.concat(httpLink),
typeDefs,
resolvers
})
The docs for how setContext are here and the docs for the `apollo-link-context' are here.
I think I have a better solution so I'm gonna post it over here. I guess you want to configure your client properly instead of passing the token to each query. I remembered how I used react-query's client. You can do the same here.
My client looks like this:
import { ApolloClient, InMemoryCache } from '#apollo/client'
export const client = new ApolloClient({
uri: 'https://api.github.com/graphql',
headers: {
Authorization: `bearer ${localStorage.getItem('personal-access-token')}`
},
cache: new InMemoryCache()
})
If I don't have a token in local storage it'll be set to 'bearer null'. But after I get a new token you should get the Apollo Client's instance and edit Authorization header like this:
import { useApolloClient } from '#apollo/client'
const setToken = token => {
client.link.options.headers.Authorization = `bearer ${token}`
localStorage.setItem('personal-access-token', token)
}
I hope this helps someone.
P.S. Make sure to wrap the component which alters the Apollo Client's instance with ApolloProvider.

csrf token issue from Vuex to Django API backend

how to pass csrf token from vuex axios to Django API using a global interceptor in vuex. Currently, I am passing csrf with each post, put and delete request like
putResponseResults: function (context, postData) {
axios.post('/api/responses/', postData, { headers: { 'X-CSRFToken': Cookie.get('csrftoken')} })
.then(function (response) {
// console.log(response.data)
})
Is there any global way to pass csrf token using vuex, so I don't need to write it every time.
You can set a CSRF token as a default header (so that it will be attached to every subsequent request) as follows:
axios.defaults.headers.common['X-CSRFToken'] = 'your token';
More information on axios config defaults can be found in axios documentation.

Send plain text request body with apollo-link-rest

I am trying to send a POST request to an endpoint that takes a application/x-www-form-urlencoded Content-Type and a plain text string for the form data with the apollo-link-rest module and am having the hardest time.
In cURL form the request I want to make looks like this:
curl -X POST http://tld.com/search -d include_all=My%20Search%20Term
I have wrapped my main component in the graphql HOC from react-apollo like this.
export default graphql(gql`
mutation productSearch($input: string) {
search(input: $input) #rest(
path: "/search",
method: "post",
endpoint: "search",
bodySerializer: "search"
) {
total
}
}
`,
{
props: ({ mutate }) => ({
runSearch: (text: string) => {
if (mutate) {
mutate({
variables: {
input: `include_all=${encodeURIComponent(text)}`,
},
});
}
},
}),
})(SearchResults);
The search bodySerializer referenced in the query looks like this.
const searchSerializer = (data: any, headers: Headers) => {
headers.set('Content-Type', 'application/x-www-form-urlencoded');
return { body: data, headers };
};
And then have called the runSearch function like this in my component.
async componentDidMount() {
try {
const result = await this.props.runSearch(this.props.searchText);
} catch (error) {
// report error & show message
}
}
Now I realize I'm not doing anything with the results but there seems to be an unhandled promise rejection (that's what React Native is telling me with a yellow box warning) when running the search code. I'm examining the request with Reactotron as well and the request looks good, but it fails still. I'm wondering if I'm missing something with how I'm configuring apollo-link-rest or if there's a better way I can examine requests made from the Apollo client.
Any help here would be much appreciated. Thanks!
So it turns out that I had everything setup correctly above. Instead of it being an issue with react-apollo it was an issue with my Info.plist file. I hadn't enabled the ability of the iOS app to make HTTP requests. It was only allowing HTTPS. I fixed it with this entry in my Info.plist.
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

How to render HTML to the user of a step function endpoint?

I'm using serverless and https://github.com/horike37/serverless-step-functions to try and implement a system that is hit by a user, returns HTML based on a database entry for the params provided and then moves to a second function that writes to the database (without forcing the user to wait).
I think a step function in the right approach but I can't seem to get it to return HTML - it always returns a JSON body with the executionArn and startDate. e.g.
{
"executionArn": "arn:aws:states:us-west-2:.......etc...",
"startDate": 1513831673.779
}
Is it possible to have my html body return? At the moment my lambda function returns a simple h1 tag:
'use strict';
module.exports.requestHandler = (event, context, callback) => {
const response = {
statusCode: 200,
headers: {
'Content-Type': 'text/html'
},
body: `<h1>Success!</h1>`,
};
callback(null, response);
};
This is the state machine I'm aiming to create.
I would suggest going for a react/angular/vue frontend hosted e.g. on S3/CDN that uses serverless for backend queries only, instead of rendering dynamic HTML through Lambdas. The 'standard' approach allows you to build apps that are much more responsive and can benefit from e.g. CDNs.
See e.g. https://www.slideshare.net/mitocgroup/serverless-microservices-real-life-story-of-a-web-app-that-uses-angularjs-aws-lambda-and-more or https://serverless-stack.com/