How to set cookies in Next js

36 views Asked by At

I'm trying to build an e-commerce application with Next.JS and a node.JS API at the backend. Currently I'm trying to implement the login option where I need to set a token as a cookie so then my API routes will be protected. I'm implementing on the Next.JS but I'm always getting errors between Server and Client components, since I want to set a Cookie ( only available with Server Components ) and then go to the previous page with Router ( Only available with Client Components ) I tried to use Nookies, but even if my cookie is set on the localhost:3000 storage, when I try to parse it, the array of cookies is always null. I also tried to set the cookie directly from the API in node.js. ( localhost:8090 ) Despite the cookie being set on Postman, it is not being set on my website ( localhost:3000) What is the best approach ?

src/app/login/page.tsx

import FormWrapper from "@/pages/Login/FormWrapper";
import { submitLogin } from "@/util/submitLogin";
import { Container } from "@mui/material";

type Inputs = {
    email: string,
    password: string,
}

export default async function page () {


    async function onSubmit(data: Inputs): Promise<any> {
        const res = await submitLogin(data);
        return res;
    }
    
  return (
    <Container>
        <FormWrapper onSubmit={onSubmit} />
    </Container>
  )
}

src/pages/login/FormWrapper.tsx

"use client";
import Login from '@/pages/Login/Login';
import Register from '@/pages/Login/Register';
import {  Grid, Stack, Typography } from '@mui/material'
import React, {useState} from 'react'

import { Apple } from '@mui/icons-material'
import Facebook from '../../../public/socials/facebook.png';
import Google from '../../../public/socials/google.png';
import Image from 'next/image';

export default function FormWrapper(props : {onSubmit: (data: any) => Promise<null>}) {

    const [isLogin, setIsLogin] = useState(true);

    const title = isLogin ? 'Login to your account' : 'Create your account';
   

    return (
        <Grid container sx={{border : '1px solid grey', borderRadius: 4, marginY : 10}}>
            <Grid item xs={12} md={6} className={"platform-image"}>
            </Grid>
            <Grid item xs={12} md={6} alignItems={"center"} >
                <Stack paddingX={"30px"} paddingY={"80px"} rowGap={"30px"} alignItems={"center"}>
                    <div className='logo'>
                        <svg width="325" height="146" viewBox="0 0 325 146" fill="none" xmlns="http://www.w3.org/2000/svg">
                          
                        </svg>
                    </div>
                    <Typography variant='h3' >{title}</Typography>
                    <Stack direction={"row"} columnGap={"30px"} width={'100%'}>
                        <div className="platform-login">
                            <Apple style={{color:'black', fontSize:"30px"}} />
                        </div>
                        <div className="platform-login">
                            <Image src={Google} alt="google logo" height={25} />
                        </div>
                        <div className="platform-login">
                            <Image src={Facebook} alt="facebook logo" height={25} />
                        </div>              
                    </Stack>
                    <div style={{width:'80%', textAlign:'center', display:'flex',alignItems:'center',justifyContent:'center'}}>
                        <div style={{height:1, backgroundColor:'#C6C6C6', width:'100%'}}></div>
                        <Typography variant='body1' color={"#959595"} bgcolor={"white"} position={"absolute"} paddingX={"10px"}>or</Typography>
                    </div>
                    <div style={{width:'100%'}}>
                        {
                            isLogin ?
                                <Login setIsLogin={setIsLogin} onSubmit={props.onSubmit}/> : 
                                <Register setIsLogin={setIsLogin}  />
                            
                        }
                    </div>
                </Stack>
            </Grid>
        </Grid>
    )
}

src/pages/login/Login.tsx

"use client";
import { Dispatch, SetStateAction, useState } from "react";
import { useForm } from "react-hook-form";
import { Visibility, VisibilityOff } from "@mui/icons-material";
import { Box, Button, IconButton, InputAdornment,  OutlinedInput, TextField, Typography } from "@mui/material";
import { useRouter } from "next/navigation";


type Inputs = {
    email: string,
    password: string,
}

export default function Login(props: {setIsLogin: Dispatch<SetStateAction<boolean>>, onSubmit: (data: Inputs) => Promise<null>}) {
    const {
        register,
        handleSubmit,
      } = useForm<Inputs>();
    
    const [showPassword, setShowPassword] = useState(false);

    const handleClickShowPassword = () => setShowPassword((show) => !show);

    const handleMouseDownPassword = (event: React.MouseEvent<HTMLButtonElement>) => {
        event.preventDefault();
    };

    const router = useRouter();

    const handleOnSubmit = (data: Inputs) => {
        props.onSubmit(data);
        router.back();
    }
    
    return (
        <Box width={"100%"} gap={"15px"} display={'flex'} flexDirection={'column'} >
             <form onSubmit={handleSubmit(props.onSubmit)}>
                <Box className='input-email' width={"100%"}>
                    <Typography variant="body1" mb={"10px"}>Email</Typography>
                    <TextField 
                        {...register("email", {required: true})}
                        placeholder='[email protected]' 
                        variant='outlined' 
                        fullWidth 
                        required
                        />
                </Box>
                <Box className='input-password' width={"100%"} mb={"15px"}>
                    <Box display={"flex"} flexDirection={"row"} justifyContent={"space-between"} alignItems={"center"} width={"100%"} mb={"10px"}>
                        <Typography variant="body1">Password</Typography>
                        <a style={{color:"#676767", fontSize:"14px"}} href='/forgot-password'>Forgot Password?</a>
                    </Box>
                    <OutlinedInput 
                        {...register("password", {required: true, minLength: 8})} 
                        required 
                        fullWidth
                        type={showPassword ? 'text' : 'password'}
                        endAdornment={
                            <InputAdornment position="end">
                                <IconButton
                                aria-label="toggle password visibility"
                                onClick={handleClickShowPassword}
                                onMouseDown={handleMouseDownPassword}
                                >
                                {showPassword ? <VisibilityOff /> : <Visibility />}
                                </IconButton> 
                            </InputAdornment>
                        }
                    />
                    
                </Box>
                <Button type="submit" variant='contained' color='primary' fullWidth>Login</Button>
            </form>
            <Typography textAlign={'center'} mt={"30px"}>Don't have an account? <span onClick={() => props.setIsLogin(false)} style={{cursor:'pointer', textDecoration:'underline'}}>Register Now</span></Typography>
        </Box>
    );
}

src/util/submitLogin.tsx

import api from "@/resources/api";
import { cookies } from "next/headers";


export async function submitLogin(data: { email: string, password: string }) {
    "use server";
    const res = await api.post('/auth/login', 
        {
            email: data.email,
            password: data.password
        },
        { withCredentials: true }
        )
    
    if(res.status === 200) {
        cookies().set('token', res.data.token, { path: '/' , expires: new Date(Date.now() + 1000*60*60*24*7)})
    }
    return res;

}

Thanks in advance !

I tried to use Nookies, but even if my cookie is set on the localhost:3000 storage, when I try to parse it, the array of cookies is always null. I also tried to set the cookie directly from the API in node.js. ( localhost:8090 )

0

There are 0 answers