What is cancelToken by axios and how I fix it? - axios

I made a React app and I making requests to the backend using Axios. I created a middleware in my backend for authorization and on the frontend side I'm trying to pass to every call that is made to the backend the auth token if exists in the localStorage. Before I added the logic for that everything worked perfectly, now every time I try to log in or register I get this in the console
TypeError: Cannot read properties of undefined (reading 'cancelToken')
at throwIfCancellationRequested (dispatchRequest.js:12:1)
at dispatchRequest (dispatchRequest.js:24:1)
at async auth.js:6:1
My index.js which handles every call to the backend looks like this:
import axios from 'axios';
const API = axios.create({
baseURL: 'http://localhost:3500'
})
API.interceptors.request.use((req) => {
if (localStorage.getItem('profile')) {
req.headers.Authorization = `Bearer ${JSON.parse(localStorage.getItem('profile')).token}`
}
})
export const fetchHunts = () => API.get('/hunts');
export const createHunt = (newHunt) => API.post('/hunts', newHunt);
export const updateHunt = (id, updatedHunt) => API.patch(`/hunts/${id}`, updatedHunt);
export const deleteHunt = (id) => API.delete(`/hunts/${id}`);
export const signInAdmin = (formData) => API.post('/admins/signinadmin', formData);
export const signUpAdmin = (formData) => API.post('/admins/signupadmin', formData);
Right now I am not logged in so there is no profile in the localStorage. I tried to add this, I found this here on stack overflow but didn't work
const CancelToken = Axios.CancelToken;
instance.interceptors.request.use(req => {
/* some logic */
const CancelToken = Axios.CancelToken;
return {
...req,
cancelToken: new CancelToken((cancel) => cancel('Cancel repeated request'))
};
});
but when I used this it only returned " Cancel repeated request " and did nothing. Do you know how can I fix that? Thank you in advance!

Based on the Axios Documentation - Interceptors, the interceptor function should return the req.
API.interceptors.request.use((req) => {
if(localStorage.getItem('profile')) {
req.headers.Authorization = `Bearer ${JSON.parse(localStorage.getItem('profile')).token}`;
}
return req;
})

My 2cents:
looks like it's easier NOT to make the request in the first place, if user is not authorized =)
Just add a global middleware on frontend to redirect user to auth page.
Here is an example in Vue, but you get the logic.
import { Middleware } from '#nuxt/types';
import { RoutesName } from '~/shared/repository/routes/routes-name';
const auth: Middleware = async (context) => {
const { route, store } = context;
const isAuthorized = store.getters['user/isAuthorized'];
const isAuthPage = route.name === RoutesName.auth;
if (!isAuthorized && !isAuthPage) {
return context.redirect(`/${RoutesName.auth}`);
}
if (isAuthorized && isAuthPage) {
return context.redirect('/');
}
};
export default auth;

Related

Custom queryFn reusing other endpoints

Using code from https://redux-toolkit.js.org/rtk-query/usage/customizing-queries#performing-multiple-requests-with-a-single-query
Note that I added the endpoint getRandomUser:
import {
createApi,
fetchBaseQuery,
FetchBaseQueryError,
} from '#reduxjs/toolkit/query'
import { Post, User } from './types'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/ ' }),
endpoints: (build) => ({
getRandomUser: builder.query<User, void>({
query: () => ({
url: `users/random`,
}),
}),
getRandomUserPosts: build.query<Post, void>({
async queryFn(_arg, _queryApi, _extraOptions, fetchWithBQ) {
// get a random user
const randomResult = await fetchWithBQ('users/random') // avoid repetition
if (randomResult.error) throw randomResult.error
const user = randomResult.data as User
const result = await fetchWithBQ(`user/${user.id}/posts`)
return result.data
? { data: result.data as Post }
: { error: result.error as FetchBaseQueryError }
},
}),
}),
})
Since in my example code I already have a getRandomUser endpoint defined, I would like to avoid repetition in getRandomUserPosts await fetchWithBQ('users/random') and directly call the endpoint getRandomUser.
I tried to access it with _queryApi.endpoints but it doesn't seem to be defined (I don't know if it can point to a key in the very same object endpoints) and even if it would I didn't know how to use it to replace the repetition.
How to approach these situations?

How to inject a stub function when using Hapi.js server.inject

I have a hapijs project which is using the hapi-mongodb plugin.
In the handler I am using the hapi-mongodb plugin to make db calls. See below
internals.getById = async (request, h) => {
try {
const db = request.mongo.db;
const ObjectId = request.mongo.ObjectID;
const query = {
_id: ObjectId(request.params.id)
};
const record = await db.collection(internals.collectionName).findOne(query);
//etc.....
I want to be able to test this using server.inject(), but I am not sure how to stub the request.mongo.db and the request.mongo.ObjectID
it('should return a 200 HTTP status code', async () => {
const server = new Hapi.Server();
server.route(Routes); //This comes from a required file
const options = {
method: 'GET',
url: `/testData/1`
};
//stub request.mongo.db and request.mongo.ObjectID
const response = await server.inject(options);
expect(response.statusCode).to.equal(200);
});
Any ideas?
I worked this out and realised that the mongo plugin decorates the server object which can be stubbed.

How to handle server side Authentication in nextjs?

I'm setting Token in localstorage and send it to every request with (axios), but the problem is when I use getServerSideProps token is not send because localStorage can't be accessed on server side.
I think I should use Cookies, I tried js-cookies but it didn't work on server as well.
Is there any solution to send token on server side fetching function as getServerSideProps and getStaticProps?
Localstorage is client-side only; use getInitialProps
function Page({ stars }) {
return <div>Next stars: {stars}</div>
}
Page.getInitialProps = async ({ req }) => {
let token;
// server
if (req) return { page: {} };
else {
// client
const token = localStorage.getItem("auth");
const res = await fetch('https://api.github.com/repos/vercel/next.js', { headers: { Authorization: token }});
const data = await res.json();
return { page: data };
}
};
export default Page
Just modify my code, normally it works

How do I get the response headers when making GET request with axios and nuxt?

I'm trying to get the CSRF Token from my backend. When using fetch I can simply go
const response = await fetch(`/account/csrf/`, {
credentials: "include",
});
const data = await response
let csrfToken = data.headers.get("X-CSRFToken"); // how is this done with axios?
I tried
const csrfToken = await this.$axios.$get(`/account/csrf/`, {
/*
what is the equivalent here?
I don't wish to *send* anything to the server, but rather retrieve, so I guess I can't use headers {}
*/
});
Instead to fetch with the $get() helper from nuxt module, you can use the raw get() method from Axios as below:
const { data, headers } = await this.$axios.get('/account/csrf/', {
withCredentials: true
});
const csrfToken = headers["X-CSRFToken"];

Using koa-jwt with koa-router

I am implementing the a Nextjs service with koa, koa-router and kow-jwt, but I'm confused with the routing setting with them.
My project have 2 pages, one is dashboard and the other is login. The dashboard need to pass the verification and the login not. If the auth failed, then redirect user to login page.
I've search on the Internet, and found some examples as following, none of them chain them together.
Nextjs custom server
kow-jwt
Please give me some advice to make them work well together.
const app = next({dev});
const handle = app.getRequestHandler();
app.prepare()
.then(() => {
const server = new koa();
const router = new koaRouter();
router.get('/login', async ctx => {
await app.render(ctx.req, ctx.res, '/login', ctx.query);
ctx.respond = false;
});
router.get('/dashboard',
jwt({
secret: config.graphqlSecret
}),
async ctx => {
await app.render(ctx.req, ctx.res, '/dashboard', ctx.query);
ctx.respond = false;
}
);
// what is the purpose of this route?
router.get('*', async ctx => {
await handle(ctx.req, ctx.res);
ctx.respond = false;
});
server.use(async (ctx, next) => {
try {
await next();
} catch (err) {
if (err.statusCode === 401) {
ctx.redirect('/login');
}
}
});
server.use(router.routes());
server.use(router.allowedMethods());
server.listen(3000);
});
with the code above, the behavior is
If I link to dashboard with and without jwt token, it always redirect to login page.
If I link to dashboard from menu (implement with <Link> in Nextjs), it shows the content of dashboard.
Thank you for your help.
You need to include the jwt part in your server.use, not within the router. Make two different routers, one with the open routes and one with the protected ones. Then set open routes, set jwt middleware and then set protected routes:
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare()
.then(() => {
const server = new Koa()
const router = new Router()
router.get('/login', async ctx => {
await app.render(ctx.req, ctx.res, '/login', ctx.query);
ctx.respond = false;
});
router.get('/dashboard', async ctx => {
await app.render(ctx.req, ctx.res, '/dashboard', ctx.query);
ctx.respond = false;
});
router.get('*', async ctx => {
await handle(ctx.req, ctx.res)
ctx.respond = false
})
// this will keep redirecting user to login until is logged in
// if you remove it, will get an auth error unless you go manually
// to the login path
server.use(async (ctx, next) => {
try {
await next();
} catch (err) {
if (err.statusCode === 401) {
ctx.redirect('/login');
}
}
});
// we need to do it this way because of the way nextjs works with '*' path
// Middleware below this line is only reached if JWT token is valid
server.use(jwt({ secret: 'shared-secret' }).unless({ path: [/^\/b/] }));
// specify in unless the unprotected path
server.use(jwt({secret: config.graphqlSecret}).unless({ path: [/^\/login/] })).use(router.allowedMethods());
// every route protected by default
server.use(router.routes())
server.listen(3000);
})