0% found this document useful (0 votes)
4 views15 pages

React Next Ecom Free

The document outlines the process of building a full-stack React eCommerce application using Next.js, NextAuth, and NextAPI, including authentication and authorization features. It provides step-by-step instructions for setting up the project, connecting to MongoDB, creating user models, and implementing registration and login functionalities. Additionally, it covers the configuration of NextAuth for user sessions and the use of Bootstrap for styling the application.

Uploaded by

clintonkj
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views15 pages

React Next Ecom Free

The document outlines the process of building a full-stack React eCommerce application using Next.js, NextAuth, and NextAPI, including authentication and authorization features. It provides step-by-step instructions for setting up the project, connecting to MongoDB, creating user models, and implementing registration and login functionalities. Additionally, it covers the configuration of NextAuth for user sessions and the use of Bootstrap for styling the application.

Uploaded by

clintonkj
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 15

ReactNextEcomFree.

md 9/7/2023

Full Stack React Ecommerce with NextJs NextAuth


NextAPI
Build a massive ecommerce applications using Next.js, NextAuth, NextAPI and deploy to Vercel. Stay in
touch with me Ryan Dhungel on twitter.com/@kaloraat for more..
Full Stack React Ecommerce with NextJs NextAuth NextAPI
Full Authentication & Authorization using NextAuth
Create nextjs project
Using bootstrap material css
Create navigation
Nextjs API routes
Signup to mongodb
Using ENV variables
Connect to mongondb
Create user model
Register API route
Register form
Regisger API request
Login page
Email and password login with next auth
NextAuth configuration
Provide user session from next auth
Access logged in user info
Loading page
404 Page not found
User dashboard
Protecting pages
Redirect back to intended page
What is next?
Full Authentication & Authorization using NextAuth
Create nextjs project
mkdir nextcom
cd nextcom
npx create-next-app@latest
// use . to create project inside nextcom
// pick typescript
// run project using
npm run dev

Using bootstrap material css


1 / 15
ReactNextEcomFree.md 9/7/2023

// remove all css from globals.css and page.module.css

// app/layout.js
import "./globals.css";

export const metadata = {


title: "Create Next App",
description: "Generated by create next app",
};

export default function RootLayout({ children }) {


return (
<html lang="en">
<body>{children}</body>
</html>
);
}

// app/page.js
export default function Home() {
return (
<main>
<div>
<h1>Home</h1>
</div>
</main>
);
}

// install bootstrap-material
// npm i bootstrap-material-design

// import in layout
import "bootstrap-material-design/dist/css/bootstrap-material-
design.min.css";

// try some bootstrap-material class names


// app/page.js
export default function Home() {
return (
<main>
<div>
<h1 className="d-flex justify-content-center align-items-center
vh-100 text-uppercase">
Home
</h1>
</div>
</main>
);
}

2 / 15
ReactNextEcomFree.md 9/7/2023

Create navigation
// components/nav/TopNav.js
import Link from "next/link";

export default function TopNav() {


return (
<nav className="nav shadow p-2 justify-content-between mb-3">
<Link className="nav-link" href="/">
🛒
NEXTCOM
</Link>

<div className="d-flex">
<Link className="nav-link" href="/login">
Login
</Link>
<Link className="nav-link" href="/register">
Register
</Link>
</div>
</nav>
);
}

// import in layout
import TopNav from "@/components/nav/TopNav";
// ...

export default function RootLayout({ children }) {


return (
<html lang="en">
<body>
<TopNav />
{children}
</body>
</html>
);
}

Nextjs API routes


// app/api/route.js
import { NextResponse } from "next/server";

export async function GET(req) {


return NextResponse.json({ time: new Date().toLocaleString() });
}

// try visiting
// http://localhost:3000/api
3 / 15
ReactNextEcomFree.md 9/7/2023

Signup to mongodb
Signup to mongo atlas to get a connection string A tutorial link
Using ENV variables
Use custom config file along with next.config.js to use env variables so that it works perfectly once
deployed to vercel

// config.js
const DB_URI =
process.env.NODE_ENV === "production"
? "mongodb+srv://ryan:xxx@nextecom.xxx.mongodb.net/?
retryWrites=true&w=majority"
: "mongodb://localhost:27017/nextecom";

module.exports = {
DB_URI,
};

// next.config.js
const config = require("./config");

/** @type {import('next').NextConfig} */


const nextConfig = {
env: {
DB_URI: config.DB_URI,
},
};

module.exports = nextConfig;

Connect to mongondb
// npm i mongoose mongoose-unique-validator

// utils/dbConnect.js
import mongoose from "mongoose";

const dbConnect = async () => {


if (mongoose.connection.readyState >= 1) {
return;
}
mongoose.connect(process.env.DB_URI);
};

export default dbConnect;

4 / 15
ReactNextEcomFree.md 9/7/2023

Create user model


// models/user
import mongoose from "mongoose";
import uniqueValidator from "mongoose-unique-validator";

const userSchema = new mongoose.Schema(


{
name: {
type: String,
required: [true, "Please enter your name"],
trim: true,
minLength: 1,
maxLength: 20,
},
email: {
type: String,
required: true,
index: true,
lowercase: true,
unique: true,
trim: true,
minLength: 5,
maxLength: 20,
},
password: String,
role: {
type: String,
default: "user",
},
image: String,
resetCode: {
data: String,
expiresAt: {
type: Date,
default: () => new Date(Date.now() + 10 * 60 * 1000), // 10
minutes in milliseconds
},
},
},
{ timestamps: true }
);

userSchema.plugin(uniqueValidator);

export default mongoose.models.User || mongoose.model("User", userSchema);

Register API route


5 / 15
ReactNextEcomFree.md 9/7/2023

// npm i bcrypt

// app/api/register/route.js
import { NextResponse } from "next/server";
import dbConnect from "@/utils/dbConnect";
import User from "@/models/user";
import bcrypt from "bcrypt";

export async function POST(req) {


const body = await req.json();
// console.log("body in register api => ", body);

await dbConnect();

try {
const { name, email, password } = body;

await new User({


name,
email,
password: await bcrypt.hash(password, 10),
}).save();

return NextResponse.json({ success: "Registered Successfully" });


} catch (err) {
console.log(err);
return NextResponse.json({ err: err.message }, { status: 500 });
}
}

// try using postman


// send name, email, password as json format in re.body

Register form
// app/register/page.js
"use client";
import { set } from "mongoose";
import { useState } from "react";
import { FormEvent } from "react";

export default function Register() {


const [name, setName] = useState("Ryan");
const [email, setEmail] = useState("ryan@gmail.com");
const [password, setPassword] = useState("rrrrrr");
const [loading, setLoading] = useState(false);

const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {


try {
e.preventDefault();

6 / 15
ReactNextEcomFree.md 9/7/2023

setLoading(true);
console.table({ name, email, password });
} catch (err) {
console.log(err);
setLoading(false);
}
};

return (
<main>
<div className="container">
<div className="row d-flex justify-content-center align-items-
center vh-100">
<div className="col-lg-5 bg-light p-5 shadow">
<h2 className="mb-4">Register</h2>

<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
className="form-control mb-4"
placeholder="Your name"
/>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="form-control mb-4"
placeholder="Your email"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="form-control mb-4"
placeholder="Your password"
/>
<button
className="btn btn-primary btn-raised"
disabled={loading || !name || !email || !password}
>
{loading ? "Please wait.." : "Submit"}
</button>
</form>
</div>
</div>
</div>
</main>
);
}

7 / 15
ReactNextEcomFree.md 9/7/2023

Regisger API request


// config.js
const API =
process.env.NODE_ENV === "production"
? "https://xxx.vercel.com/api"
: "http://localhost:3000/api";

module.exports = {
// ...
API,
};

// next.config.js
const nextConfig = {
env: {
DB_URI: config.DB_URI,
API: config.API,
},
};

// npm i react-hot-toast

// layout.tsx
import { Toaster } from "react-hot-toast";
// ...
<body>
<TopNav />
<Toaster />
{children}
</body>;
// ...

// app/register/page
import toast from "react-hot-toast";
import { useRouter } from "next/navigation";

// ...
const router = useRouter();

const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {


try {
e.preventDefault();
setLoading(true);

const response = await fetch(`${process.env.API}/register`, {


method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name,

8 / 15
ReactNextEcomFree.md 9/7/2023

email,
password,
}),
});

const data = await response.json();

if (!response.ok) {
toast.error(data.err);
setLoading(false);
} else {
toast.success(data.success);
router.push("/login");
}
} catch (err) {
console.log(err);
setLoading(false);
}
};

Login page
// app/login/page
"use client";
import { set } from "mongoose";
import { useState } from "react";
import { FormEvent } from "react";
import toast from "react-hot-toast";
import { useRouter } from "next/navigation";

export default function Register() {


const [email, setEmail] = useState("ryan@gmail.com");
const [password, setPassword] = useState("rrrrrr");
const [loading, setLoading] = useState(false);

const router = useRouter();

const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {


e.preventDefault();
//
};

return (
<main>
<div className="container">
<div className="row d-flex justify-content-center align-items-
center vh-100">
<div className="col-lg-5 bg-light p-5 shadow">
<h2 className="mb-4">Login</h2>

<form onSubmit={handleSubmit}>

9 / 15
ReactNextEcomFree.md 9/7/2023

<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="form-control mb-4"
placeholder="Your email"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="form-control mb-4"
placeholder="Your password"
/>
<button
className="btn btn-primary btn-raised"
disabled={loading || !email || !password}
>
{loading ? "Please wait.." : "Submit"}
</button>
</form>
</div>
</div>
</div>
</main>
);
}

Email and password login with next auth


// npm i net-auth

// app/login/page
import { signIn } from "next-auth/react";

// ...
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
setLoading(true);

const result = await signIn("credentials", {


redirect: false,
email,
password,
});

setLoading(false);

if (result?.error) {
toast.error(result?.error);
} else {

10 / 15
ReactNextEcomFree.md 9/7/2023

toast.success("Login success");
router.push("/");
}
};
// without next-auth config, you get redirected to '/api/auth/error'

NextAuth configuration
// config.js
// name "NEXTAUTH_SECRET" is important, dont rename it
const NEXTAUTH_SECRET = "YOUR_SECRET";

// utils/authOptions.js
import CredentialsProvider from "next-auth/providers/credentials";
import User from "@/models/user";
import bcrypt from "bcrypt";
import dbConnect from "@/utils/dbConnect";

export const authOptions = {


session: {
strategy: "jwt",
},
providers: [
CredentialsProvider({
async authorize(credentials, req) {
dbConnect();

const { email, password } = credentials;

const user = await User.findOne({ email });

if (!user) {
throw new Error("Invalid email or password");
}

// If the user has no password (i.e., they signed up via a social


network), throw an error
if (!user?.password) {
throw new Error("Please login via the method you used to sign
up");
}

const isPasswordMatched = await bcrypt.compare(


password,
user?.password
);

if (!isPasswordMatched) {
throw new Error("Invalid email or password");
}

11 / 15
ReactNextEcomFree.md 9/7/2023

return user;
},
}),
],
secret: process.env.NEXT_AUTH_SECRET,
pages: {
signIn: "/login",
},
};

// use authOptions in [...nextauth]/route


// app/api/auth/[...nextauth]/route
import NextAuth from "next-auth";

import { authOptions } from "@/utils/authOptions";

const handler = NextAuth(authOptions);

export { handler as GET, handler as POST };

Provide user session from next auth


// SessionProvider in Layout
"use client";
import "./globals.css";
import "bootstrap-material-design/dist/css/bootstrap-material-
design.min.css";
import TopNav from "@/components/nav/TopNav";
import { Toaster } from "react-hot-toast";
import { SessionProvider } from "next-auth/react";

export default function RootLayout({ children }) {


return (
<html lang="en">
<SessionProvider>
<body>
<TopNav />
<Toaster />
{/* children props/components can be server rendered */}
{children}
</body>
</SessionProvider>
</html>
);
}

Access logged in user info


12 / 15
ReactNextEcomFree.md 9/7/2023

// components/nav/TopNav
import Link from "next/link";
import { useSession, signOut } from "next-auth/react";

export default function TopNav() {


const { data, status, loading } = useSession();

// console.log(data, status);

return (
<nav className="nav shadow p-2 justify-content-between mb-3">
<Link className="nav-link" href="/">
🛒 NEXTCOM
</Link>

{status === "authenticated" ? (


<div className="d-flex">
<Link className="nav-link" href="/dashboard/user">
{data?.user?.name}
</Link>
<a
className="nav-link pointer"
onClick={() => signOut({ callbackUrl: "/login" })}
>
Logout
</a>
</div>
) : (
<div className="d-flex">
<Link className="nav-link" href="/login">
Login
</Link>
<Link className="nav-link" href="/register">
Register
</Link>
</div>
)}
</nav>
);
}

// globals.css
.pointer {
cursor: pointer;
}

Loading page
This is default loading page in nextjs
13 / 15
ReactNextEcomFree.md 9/7/2023

// app/loading.js
export default function Loading() {
return (
<div className="d-flex justify-content-center align-items-center vh-
100 text-danger">
LOADING
</div>
);
}

// using session 'loading' status


// TopNav
return (
<nav className="nav shadow p-2 justify-content-between mb-3">
<Link className="nav-link" href="/">
🛒 NEXTCOM
</Link>

{status === "authenticated" ? (


<div className="d-flex">{/* */}</div>
) : status === "loading" ? (
<div className="d-flex">
<a className="nav-link text-danger">Loading</a>
</div>
) : (
<div className="d-flex">{/* */}</div>
)}
</nav>
);

404 Page not found


// app/not-found.js
export default function NotFound() {
return (
<div className="d-flex justify-content-center align-items-center vh-
100 text-danger">
404
</div>
);
}

User dashboard
// app/dashboard/user/page
export default function UserDashboard() {
return (

14 / 15
ReactNextEcomFree.md 9/7/2023

<div className="container">
<div className="row">
<div className="col">
<p className="lead">Dashboard</p>
<hr />
...
</div>
</div>
</div>
);
}
// this page is accessible to anyone

Protecting pages
Protect dashboard pages

// middleware.js
export { default } from "next-auth/middleware";
export const config = { matcher: ["/dashboard/:path*"] };

Redirect back to intended page


// login
import { useRouter, useSearchParams } from "next/navigation";

const router = useRouter();


const searchParams = useSearchParams();
const callbackUrl = searchParams.get("callbackUrl") || "/";

// handleSubmit()
router.push(callbackUrl);

What is next?
Unfortunately, Udemy only allows 2 hours of content on free course. But do not worry, If you are enjoying
this course, I will make sure you will have access to the rest of the course materials soon in future.
You can stay in touch with me on Twitter to learn more.

15 / 15

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy