Search code examples
javascriptcookiesaxiossveltesveltekit

How to access a cookie created via Sveltkit cookies from axios interceptor


I've got a Login form with the following action (login/+page.server.js):

export const actions = {

    default: async ({ request, cookies }) => {

        const data = Object.fromEntries(await request.formData());
        
        // validate and create DTO.
        const loginData = loginDTO(data);

        // if errors returned from building the DTO (validation errors) then return the object.
        if (loginData?.errors && !emptyObject(loginData.errors)) {
            return {
                "data": loginData.data,
                "errors": loginData.errors,
            };
        }


        // perform call to log in a user.
        const response = await login(loginData);
        // const response = await loginUser(loginData);
        if (response.success) {
            // save token to a cookie
            cookies.set(
                'token',
                response.res.data.data,
                {
                    path: '/',
                    maxAge: 60 * 60 * 25 * 365,
                    httpOnly: false,
                    secure: false,
                }
            )

            throw redirect(302, '/')
        }

        return {
            "data": loginData.data,
            "errors": response.errors,
        }
    }
}

basically this is making a request to the backend authenticating a user an a JWT token is returned. So far so good, the token is returned and using Sveltekit's cookies I can successfully set the cookie, when I inspect Chrome's application tab (in dev tools) I can see the cookie there.

dev tools img

Now, I need to attach the value of the cookie to my requests. I'm using axios. To do so I created an interceptor:

import axios from "axios";
import Cookies from "js-cookie";

// let Cookies2 = Cookies.noConflict();
export const app = axios.create({
    baseURL: "http://127.0.0.1:8080/api/v1",
    timeout: 1000,
    params: {},
    // withCredentials: true,
})
// Request interceptor. Whenever a new request goes out it will be caught
// by the interceptor and whatever code is defined will be executed before continuing its flow.
app.interceptors.request.use(
    config => {
        
        console.log("here in http middleware...")
        let token = Cookies.get()
        console.log("token: ", token);
        config.headers['Content-Type'] = 'application/json';
        return config;
    },
    error => {
        Promise.reject(error)
    }
);

But I've got a couple issues:

  1. I haven't been able to use Sveltekit's cookies object, don't know how to import it here.
  2. Since the cookie is visible client-side, I decided to install js-cookie to access it, but the log statement I've got in the interceptor is always coming back empty, meaning that it's not picking up the cookie.

Any idea what's going on? or any other way I can achieve what I'm trying to do?

I could also scrap the whole cookies approach and simply use a store, what are the cons and pros of each approach?

Thanks.


Solution

    1. I haven't been able to use Sveltekit's cookies object, don't know how to import it here.

    The cookies API is only exposed to the server part of SvelteKit. This is to encourage keeping sensitive data like JWT tokens secret. If you must access this cookie on the client-side, return it from the server load() function and access it via the data prop on the +page.svelte component like this example. (However the preferred method is to use that JWT only on the server side, which should be possible 99% of the time.)

    1. Since the cookie is visible client-side, I decided to install js-cookie to access it, but the log statement I've got in the interceptor is always coming back empty, meaning that it's not picking up the cookie.

    Perhaps the cookie domain is set for localhost:5173, but you are trying to access it from a different domain? Like 127.0.0.1:5173 or 127.0.0.1:8080? I don't think there's enough information to discern what is happening.

    • Where is the second code block running, and what is it trying to do?
    • What is serving 127.0.0.1:8080? A different, non-SvelteKit server?
    • Why do you need to use axios? (SvelteKit prefers native fetch(), and provides a special augmented version on both the server and client. The augmentations include forwarding cookies.)

    any other way I can achieve what I'm trying to do?

    It's not clear what your ultimate goal is, but it feels like you could move the logic from the client to the server side.


    update: (Additional explanation based on comments)

    SvelteKit +server routes are well-documented:

    • If you add a file /src/routes/api/+server.js (with appropriate exports), that api is accessible at http://localhost:5173/api/
    • After your login/+page.server.js successfully sets the JWT cookie, the JWT cookie is automatically sent with all requests to http://localhost:5173/api/ (or any other route).
    • You can use that cookie with 3rd party API's by calling the API from the server (for example: /src/routes/api/+server.js). Simply add the cookie to the fetch() config options. The exact method may vary depending on the API, but it's usually something like adding a header like Authorization: Bearer [YOUR_TOKEN].

    Note: +server.js files are different from +page.server.js files.

    • They serve different purposes and should export different types of functions.
    • SvelteKit routing files all start with +, so fileName.server.js will not have any effect on routing.

    update 2: How to fetch from +page, +server routes

    You should use the special SvelteKit fetch from the RequestEvent. Notice fetch is part of the input to load():

    // src/routes/blog/+page.js
    export async function load({ fetch, setHeaders }) {
        const url = `https://cms.example.com/articles.json`;
        const response = await fetch(url);
        return response.json();
    }