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 )