NextJS 13 Cookie
Unanswered
Yellowstripe scad posted this in #help-forum
Yellowstripe scadOP
Hello.
Is there a cookie library that can be used by both client and server components?
Is there a cookie library that can be used by both client and server components?
6 Replies
@Yellowstripe scad Hello.
Is there a cookie library that can be used by both client and server components?
what exactly are you envisioning to do with coolies here?
Yellowstripe scadOP
Currently managing data from the backend with react-query and axios.
Below is the code useGetMe.ts.
Below is the code useGetMe.ts.
import { useQuery } from '@tanstack/react-query';
import { GetMeInput, GetMeOutput } from '@/apis/users/dtos/get-me.dto';
import { usersQueryKeys } from '@/apis/users/users.query-keys';
import apiClient from '@/libs/api-client';
export const getMe = async ({ currentLang }: GetMeInput): Promise<GetMeOutput> => {
const result = await apiClient({
url: `/v1/users/me?currentLang=${currentLang}`,
method: 'GET',
});
return result.data;
};
export default function useGetMe(initialData: GetMeOutput, getMeInput: GetMeInput) {
const {
isLoading,
data: meData,
refetch,
} = useQuery({ queryKey: usersQueryKeys.me(getMeInput.currentLang), queryFn: () => getMe(getMeInput), initialData });
return {
isGetMeLoading: isLoading,
meData,
refetchMe: refetch,
};
}.
The problem is with apiClient.ts.
This code was used normally until next12.
The problem is with apiClient.ts.
import axios from 'axios';
import Cookies from 'universal-cookie';
export const baseApiClient = axios.create({
baseURL: `${process.env.BACKEND_SERVER_URL}`,
});
baseApiClient.interceptors.request.use((request) => {
const cookies = new Cookies();
const token = cookies.get(process.env.JWT_COOKIE_NAME);
if (!request.headers['x-jwt']) {
request.headers['x-jwt'] = token || '';
}
return request;
});
async function axiosInterceptor({ ...options }): Promise<any> {
const onSuccess = (response: any) => response;
const onError = (error: any) => {
return Promise.reject(error);
};
try {
const response = await baseApiClient(options);
return onSuccess(response);
} catch (error) {
return onError(error);
}
}
export default axiosInterceptor;This code was used normally until next12.
However, the problem occurred when the server component and the client component were separated.
Previously, when importing cookies from clients, I used universal-cookie to import them.
However, I could not read the cookies in the browser while using server component, so I used the cookie included in next/headers provided by nextjs.
However, next/headers are not available on the client.
So, I am currently using the code after modifying it as below.
useGetMe.ts
As described above, we changed the method by directly finding and putting the cookie value from server component and client component.
But this approach seems very inefficient, and I'd like to apply cookies collectively for every calling api in apiClient.ts.
Previously, when importing cookies from clients, I used universal-cookie to import them.
However, I could not read the cookies in the browser while using server component, so I used the cookie included in next/headers provided by nextjs.
However, next/headers are not available on the client.
So, I am currently using the code after modifying it as below.
useGetMe.ts
import { useQuery } from '@tanstack/react-query';
import { GetMeInput, GetMeOutput } from '@/apis/users/dtos/get-me.dto';
import { usersQueryKeys } from '@/apis/users/users.query-keys';
import apiClient from '@/libs/api-client';
export const getMe = async ({ currentLang, userJwt }: GetMeInput): Promise<GetMeOutput> => {
const result = await apiClient({
url: `/v1/users/me?currentLang=${currentLang}`,
method: 'GET',
...(userJwt && {
headers: {
'x-jwt': userJwt,
},
}),
});
return result.data;
};
export default function useGetMe(initialData: GetMeOutput, getMeInput: GetMeInput) {
const {
isLoading,
data: meData,
refetch,
} = useQuery({ queryKey: usersQueryKeys.me(getMeInput.currentLang), queryFn: () => getMe(getMeInput), initialData });
return {
isGetMeLoading: isLoading,
meData,
refetchMe: refetch,
};
}As described above, we changed the method by directly finding and putting the cookie value from server component and client component.
But this approach seems very inefficient, and I'd like to apply cookies collectively for every calling api in apiClient.ts.
.
This code is my server component.
layout.tsx
This code is my server component.
layout.tsx
import React from 'react';
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';
import { getMyPartnerInformation } from '@/apis/partners/queries/useGetMyPartnerInformation';
import { getMe } from '@/apis/users/queries/useGetMe';
import LeftNavigation from '@/app/[lang]/(afterLogin)/_components/LeftNavigation';
import { Language } from '@/i18n/languages';
import styles from './layout.module.css';
type Props = {
children: React.ReactNode;
params: {
lang: Language;
};
};
export default async function AfterLoginLayout({ children, params }: Props) {
const { lang } = params;
const userJwt = cookies().get(process.env.JWT_COOKIE_NAME)?.value;
const { me } = await getMe({ currentLang: lang, userJwt });
if (!me) {
redirect(`/${lang}/login`);
}
if (me.role !== 'Partner' && me.role !== 'Admin') {
redirect(`/${lang}/unauthorized`);
}
const partnerData = await getMyPartnerInformation({ userJwt });
return (
<div className={styles.root}>
<LeftNavigation partnerData={partnerData} />
{children}
</div>
);
}.
This code is my client component.
This code is my client component.
'use client';
import Link from 'next/link';
import { GetMyPartnerInformationOutput } from '@/apis/partners/dtos/get-my-partner-information';
import useGetMyPartnerInformation from '@/apis/partners/queries/useGetMyPartnerInformation';
import useTranslation from '@/hooks/useTranslation';
import styles from './LeftNavigation.module.css';
type Props = {
partnerData: GetMyPartnerInformationOutput;
};
export default function LeftNavigation({ partnerData }: Props) {
const { lang } = useTranslation('common');
const { meData } = useGetMyPartnerInformation(partnerData);
return (
<nav className={styles.nav}>
<div className={styles.navItem}>
<Link href="/[lang]/dashboard" as={`/${lang}/dashboard`} className={styles.logoLink}>
<img src="/logo.png" alt="logo" />
</Link>
</div>
<div className={styles.navItem}>{meData.me?.companyName}</div>
</nav>
);
}