Hello everyone, welcome to Webzone tech tips I am Zidane, (my nickname: ιΈ‘θ)
Good day to you. This series will share with you how to build a web application with server, client side, and you will learn many useful things on this project.
As the title you will learn how to
- Use NestJs build a server connect with a MongoDB (use JWT for authentication, how to query on server, create schema, entity, controller, service)
- Use NextJS build a client side with TailwindCss fast build component, Zustand state management (easy manage state), Axios Request API.
After check all this full series, I believe you will have a mindset how to build a website app from backend to frontend. You will understand how to become a full stack developer through this todo web app
[Project
Day 7] Full Tutorial TodoList Apps with NextJS Zustand Axios
TailwindCSS React NestJS Mongoose JWT - Next Auth guideline
On day 7: We will focus on client side - how to work with authenticaiton with NextJS. we will use Next/Auth components for do it.
If you not yet check first articles, you can check here:
Day 1: Setup NestJS project structure and connect mongoDB π link here
Day 2: Setup CURD for NestJS π link here
Day 3: Working with JWT on NestJS π link here
Day 4: Working on Mongoose Pagination Queryπ link here
Day 5: Understand how to use @Body, @Param, @Query - postman π link here
Day 6: Understand how to implement a component, eg: Implement Toast message Component π link here
Day 7: today will learn about Next Auth for apply auth on social network, username, password π link here
π Tiktok
π Facebook:
OK let's start now
First we need to define a credentials for auth/next in
pages/api/auth/...nextauth.tsx
if use social network login we need to define: GoogleProvider / FacebookProvider as below template
as below source code I am using loginMember API call from NestJS server (day 1, day 2)
import NextAuth from "next-auth";
import FacebookProvider from "next-auth/providers/facebook";
import GoogleProvider from "next-auth/providers/google";
import CredentialsProvider from "next-auth/providers/credentials";
import { loginMember } from "../api.member";
export default NextAuth({
providers: [
CredentialsProvider({
id: "credentials",
name: "TodoList Account",
credentials: {
username: { label: "username", type: "text" },
password: { label: "Password", type: "password" },
},
async authorize(credentials, req) {
const response = await loginMember(
credentials.username,
credentials.password
);
if (response?.statusCode === 200) { // get result from server
return Promise.resolve(response.params);
} else {
throw new Error(
JSON.stringify({ message: response, status: false })
);
}
},
}),
],
pages: {
signIn: "/auth/login",
},
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60, // 30 days
},
secret: process.env.SECRET || "secret_key_here",
callbacks: {
async signIn({ user, account, profile }) {
if (account.provider !== "credentials") {
}
return true;
},
async jwt({ token, user, account, profile, isNewUser }) {
if (account) {
token.accessToken = user; // save token from server
}
return Promise.resolve(token);
},
async session({ session, token } : any) {
session.accessToken = token.accessToken;
return Promise.resolve(session);
},
},
});
So we need a loginMember function, we store this at
pages/api/api.member.ts
Above code we will call a AxiosClient
import { AxiosError } from 'axios';
import axiosClient from '../../utils/axiosClient';
import { envConfig } from './../../utils/config';
export const loginMember = async(
username: string,
password: string
) => {
let url = `${envConfig.API_PATH}/users/login`
console.log("url: ", url)
try {
const response = await axiosClient({
method: "POST",
url,
data: {
username, password
}
})
const data = response.data;
return data;
} catch (error: any) {
return error.message
}
}
Here is define AxiosClient detail code
ultis/AxiosClient.tsx
import axios, { AxiosRequestConfig, AxiosResponse } from "axios"
import { envConfig, sitePrefix } from "./config"
const headers = {
"Content-Type": "application/json"
}
const axiosClient = axios.create({
baseURL: envConfig.API_PATH,
withCredentials: false,
headers
})
// before send to server
axiosClient.interceptors.request.use(
(config: AxiosRequestConfig) : any => {
return config;
},
(err) => {
return Promise.reject(err)
}
)
// add a response interceptor
axiosClient.interceptors.response.use(
function (response: AxiosResponse) {
const msg = response?.data?.message;
const code = response?.data?.code;
if (code === 401) {
let key = sitePrefix + "token"
window.localStorage.setItem(key, "")
window.sessionStorage.setItem(key, "")
window.location.href = window.location.origin;
}
return response
},
function (error) {
if (error.response && error.response.data) {
return Promise.reject(error.response.data)
}
return Promise.reject(error.message)
},
)
export default axiosClient
and envConfig from ultis/config.tsx
interface IEnvConfig {
DEVELOPMENT: {},
STAGING: {},
PRODUCTION: {}
}
const configEnv: IEnvConfig = {
DEVELOPMENT: {
API_PATH: "https://localhost:8000",
},
STAGING: {
API_PATH: "https://localhost:8001",
},
PRODUCTION: {
}
}
const env = process.env.REACT_APP_ENV || "DEVELOPMENT"
export const sitePrefix = "todo_";
export const envConfig = configEnv[env];
console.log(envConfig)
pages/auth/login.tsx, on this file we need import
import { signIn } from "next-auth/react";
signIn("credentials", {
redirect: false,
username: values["username"],
password: values["password"],
})
.then((result) => {
if (!result.error) {
setMessage({
isError: false,
msg: "Success",
});
} else {
let rel = JSON.parse(result.error);
setMessage({
isError: true,
msg: rel?.message,
});
}
})
.catch((error) => console.log(error));
}}
After that, don't forgot to update pages/_app.tsx with below content
import '../styles/globals.css'
import '../styles/style.css'
import type { AppProps } from 'next/app'
import type { NextComponentType } from 'next' //Import Component type
//Add custom appProp type then use union to add it
type CustomAppProps = AppProps & {
Component: NextComponentType & {auth?: boolean} // add auth type fo fix
// build
}
import { SessionProvider, signIn, useSession } from 'next-auth/react'
import { useEffect } from 'react'
function MyApp({
Component,
pageProps: { session, ...pageProps },
router,
}: CustomAppProps) {
return (
<SessionProvider session={ session }>
{ Component.auth ? (
<Auth>
<Component {...pageProps} />
</Auth>
) : (
<Component {...pageProps} />
)
}
</SessionProvider>
)
}
function Auth({ children }: any) {
const { data: session, status } = useSession()
const isUser = !!session?.user;
useEffect(() => {
if (status === "loading") return;
if (!isUser) signIn();
}, [isUser, status])
if (isUser) {
return children
}
return <div></div>
}
export default MyApp