SomeDeveloper
SomeDeveloper

Reputation: 48

How to correctly use RTK Query (SSR) with Next.js to include tokens stored in cookies

I have an application that uses Next.js, Redux, RTK Query and next-redux-wrapper and I'm having an issue with cookies not being available in a Next.js API route once store.dispatch(getMedications.initiate()) runs - the cookie is undefined on server render, but can be read fine once the page loads.

On the same page I have a useGetMedicationsQuery hook that runs, which works completely fine and can access the cookies when the query is run, however whenever the store.dispatch(getMedications.initiate()) query is run server side i.e. in the getServerSideProps the token cookie is undefined.

/pages/api/medications/get-medications.js

import axios from 'axios';

export default async (req, res) => {
    const SERVICE_HOST = process.env.SERVICE_HOST;
    const cookies = req.cookies;
    const token = cookies.token; // undefined when initiated via store.dispatch(getMedications.initiate())

    try {
        const response = await axios.get(`${SERVICE_HOST}/patient/medications`, {
            headers: {
                'Authorization': `Bearer ${token}`
            }
        });

        res.status(200).json(response.data.data);
    } catch(error) {
        res.status(500).json(error.response);
    }
}

/services/medications.js

import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/dist/query/react";
import { HYDRATE } from "next-redux-wrapper";

export const medicationApi = createApi({
    reducerPath: "medicationApi",
    baseQuery: fetchBaseQuery({
        baseUrl: 'http://localhost:3001/api/medications/',
    }),
    keepUnusedDataFor: 3600,
    extractRehydrationInfo(action, { reducerPath }) {
        if (action.type === HYDRATE) {
            return action.payload[medicationApi];
        }
    },
    tagTypes: ['Medications'],
    endpoints: (build) => ({
        getMedications: build.query({
            query: () => `get-medications/`,
            providesTags: () => ['Medications']
        }),
    }),
})

export const {
    useGetMedicationsQuery,
    util: { getRunningOperationPromises }
} = medicationApi;

export const { getMedications } = medicationApi.endpoints;

/pages/medications.js

export const getServerSideProps = wrapper.getServerSideProps(
    (store) => async ({ req, res }) => {
        const WEBSITE_URL = process.env.WEBSITE_URL;
        const SERVICE_HOST = process.env.SERVICE_HOST;
        const COOKIE_DOMAIN = process.env.COOKIE_DOMAIN || '';

        const cookies = cookie.parse(req.headers.cookie || '');
        const token = cookies.token;

        if (!token) {
            return {
                redirect: {
                    destination: `${WEBSITE_URL}/login/${queryString(req.url)}`,
                    permanent: false,
                }
            }
        }

        try {
            ... some stuff

            store.dispatch(getMedications.initiate());

            await Promise.all(getRunningOperationPromises());

            return {
                props: {
                }
            }
        } catch(error) {
            createTokenCookie(res, COOKIE_DOMAIN, '')

            return {
                redirect: {
                    destination: `${WEBSITE_URL}/login/`,
                    permanent: false,
                }
            }
        }
    }
);

Upvotes: 0

Views: 4842

Answers (1)

carledwardfp
carledwardfp

Reputation: 11

extractRehydrationInfo(action, { reducerPath }) {
    if (action.type === HYDRATE) {
        return action.payload[medicationApi];
    }
},

this should be changed to

extractRehydrationInfo(action, { reducerPath }) {
    if (action.type === HYDRATE) {
        return action.payload[reducerPath];
    }
},

Upvotes: 1

Related Questions