article

Monday, October 7, 2024

Nextjs 14 CRUD Create,Read,Update and Delete with upload and delete image Server-Side | Postgresql Prisma

Nextjs 14 CRUD Create,Read,Update and Delete with upload and delete image Server-Side | Postgresql Prisma

Next.js

Install requirements
Zod
Zod is a TypeScript-first schema declaration and validation library.
https://www.npmjs.com/package/zod

Prisma is an open-source next-generation ORM. It consists of the following parts: Prisma Client: Auto-generated and type-safe query builder
https://www.prisma.io

Install prisma/client
npm install @prisma/client
https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/generating-prisma-client

Generate Prisma Client with the following command:
npx prisma generate

Install prisma
npm install prisma --save-dev
https://www.prisma.io/docs/getting-started/quickstart

set up Prisma with the init command of the Prisma CLI:
npx prisma init --datasource-provider sqlite
to npx prisma init --datasource-provider postgres

Model data in the Prisma schema
prisma/schema.prisma

model Upload {
id String @id @default(cuid())
title String
image String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

Run a migration to create your database tables with Prisma Migrate
npx prisma migrate dev --name init

.env
DATABASE_URL="postgresql://postgres:admin@localhost:5432/postgresDB?schema=public"

https://www.prisma.io/docs/orm/prisma-client/queries/crud

https://nextjs.org/docs/app/building-your-application/routing/route-handlers

app\page.tsx
//app\page.tsx
import Image from "next/image";
import Link from "next/link";
import { DeleteButton, EditButton } from "@/components/button";
import { getImages } from "@/lib/data";

export default async function Home() {
  const getimages = await getImages();

  return (
    <div className="max-w-screen-lg mx-auto py-14">
      <h1 className="text-4xl font-bold">Nextjs 14 CRUD Create,Read,Update and Delete with upload and delete image Server-Side | Postgresql Prisma</h1>
      <div className="flex items-end justify-between m-12">
        <h1 className="text-4xl font-bold">Images</h1>
        <Link
          href="/create"
          className="py-3 px-6 bg-green-700 hover:bg-green-800 text-white"
        >
          Upload New Image
        </Link>
      </div>
      <div className="grid md:grid-cols-3 gap-5 mt-10">
        {getimages.map((item) => (
          <div key={item.id} className="max-w-sm border border-gray-200 rounded-md shadow">
            <div className="relative aspect-video">
              <Image
                src={`http://localhost:3000/assets/${item.image}`}
                alt={item.title}
                fill
                priority
                sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
                className="rounded-t-md object-cover"
              />
            </div>
            <div className="p-5">
              <h1 className="text-2xl font-bold text-gray-900 truncate">
                {item.title}
              </h1>
            </div>
            <div className="flex items-center justify-between">
              <EditButton id={item.id} />
              <DeleteButton id={item.id}/>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}
components\button.tsx
//components\button.tsx
"use client";

import { useFormStatus } from "react-dom";
import { clsx } from "clsx";
import { deleteData } from "@/lib/actions";
import Link from "next/link";

export const SubmitButton = ({ label }: { label: string }) => {
  const { pending } = useFormStatus();
  return (
    <button
      className={clsx(
        "bg-blue-700 text-white w-full font-medium py-2.5 px-6 text-base rounded-sm hover:bg-blue-600",
        {
          "opacity-50 cursor-progress": pending,
        }
      )}
      type="submit"
      disabled={pending}
    >
      {label === "upload" ? (
        <>{pending ? "Uploading..." : "Upload"}</>
      ) : (
        <>{pending ? "Updating..." : "Update"}</>
      )}
    </button>
  );
};

export const EditButton = ({ id }: { id: string }) => {
  return (
    <Link
      href={`edit/${id}`}
      className="py-3 text-sm text-white bg-blue-700 rounded-bl-md w-full hover:bg-blue-800 text-center"
    >
      Edit
    </Link>
  );
};

export const DeleteButton = ({ id }: { id: string }) => {
  const deleteDataWithId = deleteData.bind(null, id);
  return (
    <form
      action={deleteDataWithId}
      className="py-3 text-sm text-white bg-red-700 rounded-br-md w-full hover:bg-red-800 text-center"
    >
      <DeleteBtn />
    </form>
  );
};

const DeleteBtn = () => {
  const { pending } = useFormStatus();
  return (
    <button type="submit" disabled={pending}>
      {pending ? "Deleting..." : "Delete"}
    </button>
  );
};
lib\data.ts
//lib\data.ts
import { prisma } from "@/lib/prisma";

export const getImages = async () => {
  try {
    const result = await prisma.upload.findMany({
      orderBy: { createdAt: "desc" },
    });
    return result;
  } catch (error) {
    throw new Error("Failed to fetch data");
  }
};

export const getDataById = async (id: string) => {
  try {
    const result = await prisma.upload.findUnique({
      where: { id },
    });
    return result;
  } catch (error) {
    throw new Error("Failed to fetch data");
  }
};
lib\data.ts
//lib\prisma.ts
import { PrismaClient } from "@prisma/client";

declare global {
  var prisma: PrismaClient | undefined;
}

export const prisma = globalThis.prisma || new PrismaClient();

if (process.env.NODE_ENV !== "production") globalThis.prisma = prisma;
lib\actions.ts
//lib\actions.ts
"use server";

import { z } from "zod"; //https://www.npmjs.com/package/zod
import { prisma } from "@/lib/prisma";
import { writeFile } from "fs/promises";
import path from "path";
import { redirect } from 'next/navigation'
import { revalidatePath } from 'next/cache'
import { getDataById } from "@/lib/data";
import fs from 'fs';

const UploadSchema = z.object({
  title: z.string().min(1),
  image: z
    .instanceof(File)
    .refine((file) => file.size > 0, { message: "Image is required" })
    .refine((file) => file.size === 0 || file.type.startsWith("image/"), {
      message: "Only images are allowed",
    })
    .refine((file) => file.size < 4000000, {
      message: "Image must less than 4MB",
    }),
});

const EditSchema = z.object({
  title: z.string().min(1),
  image: z
    .instanceof(File)
    .refine((file) => file.size === 0 || file.type.startsWith("image/"), {
      message: "Only images are allowed",
    })
    .refine((file) => file.size < 4000000, {
      message: "Image must less than 4MB",
    })
    .optional(),
});

export const CreateData = async (prevState: unknown, formData: FormData) => {
    const validatedFields = UploadSchema.safeParse(
      Object.fromEntries(formData.entries())
    );

    if (!validatedFields.success) {
      return {
        error: validatedFields.error.flatten().fieldErrors,
      };
    }
    const file = formData.get("image");
    const { title } = validatedFields.data;

    try {
      const buffer = Buffer.from(await file.arrayBuffer());
      const filename =  file.name.replaceAll(" ", "_");
      console.log(filename);

      await writeFile(
        path.join(process.cwd(), "public/assets/" + filename),
        buffer
      );

      await prisma.upload.create({
          data: {
            title,
            image: filename,
          },
      });

      //return { message: "Success" };
    } catch (error) {
      console.log("Error occured ", error);
      return { message: "Failed" };
    }

  revalidatePath("/");
  redirect("/");
};

// Update
export const updateData = async (
  id: string,
  prevState: unknown,
  formData: FormData
) => {
  const validatedFields = EditSchema.safeParse(
    Object.fromEntries(formData.entries())
  );

  if (!validatedFields.success) {
    return {
      error: validatedFields.error.flatten().fieldErrors,
    };
  }

  const data = await getDataById(id);
  if (!data) return { message: "No Data Found" };

  const file = formData.get("image");
  const { title, image } = validatedFields.data;
  let imageFilename;
  
  if (!image || image.size <= 0) {
    imageFilename = data.image;
  } else {
    console.log(data.image);
    fs.unlink("public/assets/" + data.image, (err) => {
      if (err) {
        console.error('An error occurred:', err);
      } else {
        console.log('File deleted successfully!');
      }
    });

    const buffer = Buffer.from(await file.arrayBuffer());
    const filename =  file.name.replaceAll(" ", "_");
    console.log(filename);

    await writeFile(
      path.join(process.cwd(), "public/assets/" + filename),
      buffer
    );
    
    imageFilename = filename;
  }

  try {
    await prisma.upload.update({
      data: {
        title,
        image: imageFilename,
      },
      where: { id },
    });
  } catch (error) {
    return { message: "Failed to update data" };
  }

  revalidatePath("/");
  redirect("/");
};

export const deleteData = async (id: string) => {
  const data = await getDataById(id);
  if (!data) return { message: "No data found" };

  console.log(data.image);
  try { 
    fs.unlink("public/assets/" + data.image, (err) => {
      if (err) {
        console.error('An error occurred:', err);
      } else {
        console.log('File deleted successfully!');
      }
    });

    await prisma.upload.delete({
      where: { id },
    });
  } catch (error) {
    return { message: "Failed to delete data" };
  }

  revalidatePath("/");
};
app\create\page.tsx
//app\create\page.tsx
import UploadForm from "@/components/upload-form";

const UploadPage = () => {
  return (
    <div className="min-h-screen flex items-center justify-center bg-slate-100">
      <div className="bg-white rounded-sm shadow p-8">
        <h1 className="text-2xl font-bold mb-5">Upload Image</h1>
        <UploadForm />
      </div>
    </div>
  );
};

export default UploadPage;
components\upload-form.tsx
//components\upload-form.tsx
"use client";

import React from "react";
import { CreateData } from "@/lib/actions";
import { useFormState } from "react-dom";
import { SubmitButton } from "@/components/button";

const UploadForm = () => {
  const [state, formAction] = useFormState(CreateData, null);

  return (
    <form action={formAction}>   
      {/* Alert */}
      {state?.message ? (
        <div
          className="p-4 mb-4 text-sm text-green-800 rounded-lg bg-green-50"
          role="alert"
        >
          <div className="font-medium">{state?.message}</div>
        </div>
      ) : null}
      <div className="mb-4 pt-2">
        <input
          type="text"
          name="title"
          className="py-2 px-4 rounded-sm border border-gray-400 w-full"
          placeholder="Title..."
        />
        <div aria-live="polite" aria-atomic="true">
          <p className="text-sm text-red-500 mt-2">{state?.error?.title}</p>
        </div>
      </div>
      <div className="mb-4 pt-2">
        <input
          type="file"
          name="image"
          className="file:py-2 file:px-4 file:mr-4 file:rounded-sm file:border-0 file:bg-gray-200 hover:file:bg-gray-300 file:cursor-pointer border border-gray-400 w-full"
        />
        <div aria-live="polite" aria-atomic="true">
          <p className="text-sm text-red-500 mt-2">{state?.error?.image}</p>
        </div>
      </div>
      <div className="mb-4 pt-4">
        <SubmitButton label="upload" />
      </div>
    </form>
  );
};

export default UploadForm;
app\edit\[id]\page.tsx
//app\edit\[id]\page.tsx
import EditForm from "@/components/edit-form";
import { getDataById } from "@/lib/data";
import { notFound } from "next/navigation";

const EditPage = async ({ params }: { params: { id: string } }) => {
  const data = await getDataById(params.id);
  if (!data) return notFound();

  return (
    <div className="min-h-screen flex items-center justify-center bg-slate-100">
      <div className="bg-white rounded-sm shadow p-8">
        <h1 className="text-2xl font-bold mb-5">Update Image</h1>
        <EditForm data={data} />
      </div>
    </div>
  );
};

export default EditPage;
components\edit-form.tsx
//components\edit-form.tsx
"use client";
import React from "react";
import { updateData } from "@/lib/actions";
import { useFormState } from "react-dom";
import { SubmitButton } from "@/components/button";
import type { Upload } from "@prisma/client";
import Image from "next/image";

const EditForm = ({ data }: { data: Upload }) => {
  const [state, formAction] = useFormState(
    updateData.bind(null, data.id),
    null
  );

  return (
    <form action={formAction}>
      {state?.message ? (
        <div
          className="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50"
          role="alert"
        >
          <div className="font-medium">{state?.message}</div>
        </div>
      ) : null}

      <div className="mb-4 pt-2">
        <input
          type="text"
          name="title"
          className="py-2 px-4 rounded-sm border border-gray-400 w-full"
          placeholder="Title..."
          defaultValue={data.title}
        />
        <div aria-live="polite" aria-atomic="true">
          <p className="text-sm text-red-500 mt-2">{state?.error?.title}</p>
        </div>
              <Image
                src={`http://localhost:3000/assets/${data.image}`}
                alt={data.title}
                width={200}
                height={200}
              />
      </div>
      <div className="mb-4 pt-2">
        <input
          type="file"
          name="image"
          className="file:py-2 file:px-4 file:mr-4 file:rounded-sm file:border-0 file:bg-gray-200 hover:file:bg-gray-300 file:cursor-pointer border border-gray-400 w-full"
        />
        <div aria-live="polite" aria-atomic="true">
          <p className="text-sm text-red-500 mt-2">{state?.error?.image}</p>
        </div>
      </div>
      <div className="mb-4 pt-4">
        <SubmitButton label="update" />
      </div>
    </form>
  );
};

export default EditForm;
prisma\schema.prisma
//prisma\schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider  = "postgresql"
  url      = "postgresql://postgres:admin@localhost:5432/postgresDB?schema=public" 
}

model Upload {
  id        String   @id @default(cuid())
  title     String
  image     String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
.env DATABASE_URL="postgresql://postgres:admin@localhost:5432/postgresDB?schema=public"

run C:\nextjs>npm run dev

Tuesday, October 1, 2024

Nextjs 14 upload image Server-Side

Nextjs 14 upload image Server-Side

Next.js

Install requirements
Zod
Zod is a TypeScript-first schema declaration and validation library.
https://www.npmjs.com/package/zod
app\page.tsx
//app\page.tsx
import Image from "next/image";
import fs from "node:fs/promises";
import Link from "next/link";

export default async function Home() {
  const files = await fs.readdir("./public/assets");
  const images = files
    .map((file) => `/assets/${file}`);

  return (
    <div className="max-w-screen-lg mx-auto py-14">
      <div className="flex items-end justify-between">
        <h1 className="text-4xl font-bold">Images</h1>
        <Link
          href="/upload"
          className="py-3 px-6 bg-blue-600 hover:bg-blue-700 text-white"
        >
          Upload New Image
        </Link>
      </div>
      <div className="grid md:grid-cols-3 gap-5 mt-10">
        {images.map((image) => (
          <div key={image} className="max-w-sm border border-gray-200 rounded-md shadow">
            <div className="relative aspect-video">
              <Image
                key={image}
                src={image}
                alt={image}
                fill
                priority
                sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
                className="rounded-t-md object-cover"
              />
            </div>
          </div>
        ))}

      </div>
    </div>
  );
}
app\upload\page.tsx
//app\upload\page.tsx
import UploadForm from "@/components/upload-form";

const UploadPage = () => {
  return (
    <div className="min-h-screen flex items-center justify-center bg-slate-100">
      <div className="bg-white rounded-sm shadow p-8">
        <h1 className="text-2xl font-bold mb-5">Upload Image</h1>
        <UploadForm />
      </div>
    </div>
  );
};

export default UploadPage;
components\upload-form.tsx
//components\upload-form.tsx
"use client";

import React from "react";
import { uploadImage } from "@/lib/actions";
import { useFormState } from "react-dom";
import { SubmitButton } from "@/components/button";

const UploadForm = () => {
  const [state, formAction] = useFormState(uploadImage, null);

  return (
    <form action={formAction}>
      {/* Alert */}
      {state?.message ? (
        <div
          className="p-4 mb-4 text-sm text-green-800 rounded-lg bg-green-50"
          role="alert"
        >
          <div className="font-medium">{state?.message}</div>
        </div>
      ) : null}

      <div className="mb-4 pt-2">
        <input
          type="file"
          name="image"
          className="file:py-2 file:px-4 file:mr-4 file:rounded-sm file:border-0 file:bg-gray-200 hover:file:bg-gray-300 file:cursor-pointer border border-gray-400 w-full"
        />
        <div aria-live="polite" aria-atomic="true">
          <p className="text-sm text-red-500 mt-2">{state?.error?.image}</p>
        </div>
      </div>
      <div className="mb-4 pt-4">
        <SubmitButton label="upload" />
      </div>
    </form>
  );
};

export default UploadForm;
components\button.tsx
//components\button.tsx
"use client";
import { useFormStatus } from "react-dom";
import { clsx } from "clsx";

export const SubmitButton = ({ label }: { label: string }) => {
  const { pending } = useFormStatus();
  return (
    <button
      className={clsx(
        "bg-blue-700 text-white w-full font-medium py-2.5 px-6 text-base rounded-sm hover:bg-blue-600",
        {
          "opacity-50 cursor-progress": pending,
        }
      )}
      type="submit"
      disabled={pending}
    >
      {label === "upload" ? (
        <>{pending ? "Uploading..." : "Upload"}</>
      ) : (
        <>{pending ? "Updating..." : "Update"}</>
      )}
    </button>
  );
};
lib\actions.ts
//lib\actions.ts
"use server";

import { z } from "zod"; //https://www.npmjs.com/package/zod
import { writeFile } from "fs/promises";
import path from "path";
import { redirect } from 'next/navigation'
import { revalidatePath } from 'next/cache'

const UploadSchema = z.object({
  image: z
    .instanceof(File)
    .refine((file) => file.size > 0, { message: "Image is required" })
    .refine((file) => file.size === 0 || file.type.startsWith("image/"), {
      message: "Only images are allowed",
    })
    .refine((file) => file.size < 4000000, {
      message: "Image must less than 4MB",
    }),
});

export const uploadImage = async (prevState: unknown, formData: FormData) => {
    const validatedFields = UploadSchema.safeParse(
      Object.fromEntries(formData.entries())
    );

    if (!validatedFields.success) {
      return {
        error: validatedFields.error.flatten().fieldErrors,
      };
    }
    const file = formData.get("image");
    
    try {
      const buffer = Buffer.from(await file.arrayBuffer());
      const filename =  file.name.replaceAll(" ", "_");
      console.log(filename);

      await writeFile(
        path.join(process.cwd(), "public/assets/" + filename),
        buffer
      );
      //return { message: "Success" };
    } catch (error) {
      console.log("Error occured ", error);
      return { message: "Failed" };
    }

  revalidatePath("/");
  redirect("/");
};
run C:\nextjs>npm run dev

Sunday, July 28, 2024

React.js 18 Node Express CRUD (Create Read Update and Delete) | MySQL Tailwind CSS

React.js 18 Node Express CRUD (Create Read Update and Delete) | MySQL Tailwind CSS

https://expressjs.com/
Express JS
Fast, unopinionated, minimalist web framework for Node.js

Install
$ npm install express --savev PS C:\nodeproject> npm install express --save
https://expressjs.com/en/starter/hello-world.html

mysql
https://www.npmjs.com/package/mysql
$ npm install mysql
PS C:\nodeproject>npm install mysql

cors
CORS is a node.js package for providing a Connect/Express middleware that can be used to enable CORS with various options.
https://www.npmjs.com/package/cors
PS C:\nodeproject>npm i cors

bcrypt
https://www.npmjs.com/package/bcrypt
A library to help you hash passwords.
npm install bcrypt

run PS C:\nodeproject> node index.js
index.js
//index.js
const express = require('express')
const bodyParser = require('body-parser');
const cors = require('cors');
const bycrypt = require('bcrypt'); //npm install bcrypt https://www.npmjs.com/package/bcrypt
const db = require('./db');

const app = express()

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.use(cors({
      origin: 'http://localhost:3000',
      credentials: true
}));

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.post("/api/adduser", async (req, res) => {
  const sql ="INSERT INTO users (name,email,username,password) VALUES (?, ?, ?, ?)";

  const hashedPassword =  await bycrypt.hash(req.body.password, 10);
  
  const values = [req.body.name, req.body.email, req.body.username, hashedPassword];

  db.query(sql, values, (err, result) => {
    if (err)
      return res.json({ message: "Something unexpected has occured" + err });
    return res.json({ success: "New User added successfully" });
  });

});

app.get("/api/users", (req, res) => {
  const sql = "SELECT * FROM users";
  db.query(sql, (err, result) => {
    if (err) res.json({ message: "Server error" });
    return res.json(result);
  });
});

app.get("/api/getuser/:id", (req, res) => {
  const id = req.params.id;
  const sql = "SELECT * FROM users WHERE id= ?";
  db.query(sql, [id], (err, result) => {
    if (err) res.json({ message: "Server error" });
    return res.json(result);
  });
});

app.put("/api/edit/:id", (req, res) => {
  const id = req.params.id;
  const sql ="UPDATE users SET name=?, email=?, username=? WHERE id=?";
   
  const values = [
    req.body.name,
    req.body.email,
    req.body.username,
    id,
  ];
  db.query(sql, values, (err, result) => {
    if (err)
      return res.json({ message: "Something unexpected has occured" + err });
    return res.json({ success: "User updated successfully" });
  });
});

app.delete("/api/delete/:id", (req, res) => {
  const id = req.params.id;
  const sql = "DELETE FROM users WHERE id=?";
  const values = [id];
  db.query(sql, values, (err, result) => {
    if (err)
      return res.json({ message: "Something unexpected has occured" + err });
    return res.json({ success: "Student successfully Deleted" });
  });
});

app.listen(3001, () => {console.log('Server started on port 3001')});
db.js
//db.js
const mysql = require("mysql");
 
const db = mysql.createPool({
    socketPath: '/Applications/MAMP/tmp/mysql/mysql.sock',
    connectionLimit: 10,
    host: "localhost",
    user: "root",
    password: "root",
    database: "nodeexpressDB"
});
 
// Ping database to check for common exception errors.
db.getConnection((err, connection) => {
    if (err) {
        if (err.code === 'PROTOCOL_CONNECTION_LOST') {
            console.error('Database connection was closed.');
        }
        if (err.code === 'ER_CON_COUNT_ERROR') {
            console.error('Database has too many connections.');
        }
        if (err.code === 'ECONNREFUSED') {
            console.error('Database connection was refused.');
        }
    }
  
    if (connection) connection.release();
  
    return;
});
 
module.exports = db;
React JS
https://react.dev/learn/start-a-new-react-project

npx create-next-app@latest
npx create-react-app@latest {project name}
Create Project
C:\react-js>npx create-react-app@latest my-app
Run
C:\react-js\my-app> npm start

Install tailwindcss https://tailwindcss.com/docs/guides/create-react-app npm install -D tailwindcss npx tailwindcss init Install react-router-dom https://www.npmjs.com/package/react-router-dom Install axios
npm install axios
https://www.npmjs.com/package/axios
C:\react-js\my-app\src\App.js
//src\App.js
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom"; //npm i react-router-dom https://www.npmjs.com/package/react-router-dom
import Home from "./elements/Home";
import Create from "./elements/Create";
import Read from "./elements/Read";
import Edit from "./elements/Edit";

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
         <Route path="/create" element={<Create />} />
         <Route path="/read/:id" element={<Read />} />
         <Route path="/edit/:id" element={<Edit />} />
      </Routes>
    </BrowserRouter>
  );
}

export default App;
C:\react-js\my-app\src\elements\Home.jsx
//C:\react-js\my-app\src\elements\Home.jsx
import ListUser from './ListUser'
import { Link } from 'react-router-dom'
import { Suspense } from "react";

function Home() {

  return (
    <div className="w-screen py-20 flex justify-center flex-col items-center">
        <div className="flex items-center justify-between gap-1 mb-5">
            <h1 className="text-4xl font-bold">React.js 18 Node Express CRUD (Create Read Update and Delete) | MySQL Tailwind CSS</h1>
        </div> 
        <div className="overflow-x-auto py-10">
            <div className="mb-2 w-full text-right">
                <Link
                to="/create"
                className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-4 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">
                Add New User
                </Link>
            </div>
            <Suspense fallback="Loading...">
                <ListUser/>
            </Suspense>
        </div>
    </div>
  )
}

export default Home
C:\react-js\my-app\src\elements\ListUser.jsx
//C:\react-js\my-app\src\elements\ListUser.jsx
import axios from 'axios'; //npm install axios https://www.npmjs.com/package/react-axios
import { useEffect, useState } from "react";
import { Link } from 'react-router-dom'

export default function ListUser() {
    const [users, setUsers] = useState([]);
  
    useEffect(() => {
        getUsers();
    }, []);
   
    function getUsers() { 
        axios.get('http://localhost:3001/api/users').then(function(response) {
            console.log(response.data);
            setUsers(response.data);
        });
    }

    const deleteUser = (id) => {
        axios.delete(`http://localhost:3001/api/delete/${id}`).then(function(response){
            console.log(response.data);
            getUsers();
        });
    }

    return (
        <table className="table table-zebra">
            <thead className="text-sm text-gray-700 uppercase bg-gray-50">
                <tr>
                    <th className="py-3 px-6">#</th>
                    <th className="py-3 px-6">Name</th>
                    <th className="py-3 px-6">Email</th>
                    <th className="py-3 px-6">Username</th>
                    <th className="py-3 px-6 text-center">Actions</th>
                </tr>
            </thead>
            <tbody>
                {users.map((user, key) =>
                    <tr key={key} className="bg-white border-b">
                        <td className="py-3 px-6">{user.id}</td>
                        <td className="py-3 px-6">{user.name}</td>
                        <td className="py-3 px-6">{user.email}</td>
                        <td className="py-3 px-6">{user.username}</td>
                        <td className="flex justify-center gap-1 py-3">
                            <Link
                                to={`/read/${user.id}`} 
                                className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">
                                Read
                            </Link>
                            <Link className="focus:outline-none text-white bg-yellow-400 hover:bg-yellow-500 focus:ring-4 focus:ring-yellow-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:focus:ring-yellow-900" 
                                to={`edit/${user.id}/`}>
                                Edit
                            </Link>
                            <button onClick={() => deleteUser(user.id)} className="focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900">
                                Delete</button>
                        </td>
                    </tr>
                )}
            </tbody>
        </table>
    )
}
C:\react-js\my-app\src\elements\Create.jsx
//C:\react-js\my-app\src\elements\Create.jsx
import React, { useState } from "react";
import axios from 'axios' //npm install axios https://www.npmjs.com/package/axios
import {useNavigate} from 'react-router-dom'

const Addnewuser = () => {
    const [inputs, setInputs] = useState([]);
   
    const handleChange = (event) => {
        const name = event.target.name;
        const value = event.target.value;
        setInputs(values => ({...values, [name]: value}));
    }
  
    const navigate = useNavigate()
 
    const handleSubmit = (event) => {
        event.preventDefault();
  
        axios.post('http://localhost:3001/api/adduser', inputs).then(function(response){
            console.log(response.data);
            navigate('/')
        });
    }

    return (
    <div className="max-w-md mx-auto mt-5">
        <h1 className="text-2xl text-center mb-2">Add New User</h1>
        <div>
        <form onSubmit={handleSubmit}>
        <div className="mb-5">
          <label htmlFor="name" className="block text-sm font-medium text-gray-900">
            Name
          </label>
          <input
            type="text"
            name="name"
            id="name"
            className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500blue-500"
            placeholder="Name..."
            onChange={handleChange}
          />
        </div>
        <div className="mb-5">
          <label htmlFor="email" className="block text-sm font-medium text-gray-900">
            Email
          </label>
          <input
            type="email"
            name="email"
            id="email"
            className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500blue-500"
            placeholder="email..."
            onChange={handleChange}
          />
        </div>
        <div className="mb-5">
          <label htmlFor="username" className="block text-sm font-medium text-gray-900">
            Username
          </label>
          <input
            type="text"
            name="username"
            id="username"
            className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500blue-500"
            placeholder="Username..."
            onChange={handleChange}
          />
        </div>
        <div className="mb-5">
          <label htmlFor="password" className="block text-sm font-medium text-gray-900">
            Password
          </label>
          <input
            type="password"
            name="password"
            id="password"
            className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500blue-500"
            placeholder="Password..."
            onChange={handleChange}
          />
        </div>
        <button type="submit" className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
        Add New User</button> 
        </form>
        </div>
    </div>
  );
};
     
export default Addnewuser;
C:\react-js\my-app\src\elements\Read.jsx
//C:\react-js\my-app\src\elements\Read.jsx
import React, { useState, useEffect } from 'react';
import axios from 'axios' //npm install axios https://www.npmjs.com/package/axios
import { useParams } from "react-router-dom"; 

export default function ViewUser() {
    const {id}=useParams();
    
    console.log(id);
    
    const[user,setUser]=useState([]);
     
    useEffect(()=>{
        fetchUser();
    },[id]);
     
    const fetchUser=async()=>{
        try{
        const result=await axios.get("http://localhost:3001/api/getuser/"+id);
          console.log(result.data[0]);
          setUser(result.data[0])
     
        }catch(err){
            console.log("Something Wrong");
        }
    }
    
    return (
    <div className="max-w-2xl mx-auto mt-5">
      <h1 className="text-2xl text-center mb-2">View User</h1>
      <table className="table table-zebra">
          <thead className="text-sm text-gray-700 uppercase bg-gray-50">
            <tr>
              <th>S No.</th>
              <th>Name</th>
              <th>Email</th>         
              <th>Username</th>      
            </tr>
          </thead>
          <tbody>
            <tr>
                <td>{user.id}</td>
                <td>{user.name}</td>
                <td>{user.email}</td>
                <td>{user.username}</td>
            </tr>
          </tbody>
      </table>
    </div>
  );
}
C:\react-js\my-app\src\elements\Edit.jsx
//C:\react-js\my-app\src\elements\Edit.jsx
import React, { useState, useEffect } from 'react';
import axios from 'axios' //npm install axios https://www.npmjs.com/package/axios
import { useParams, useNavigate } from "react-router-dom"; 

export default function Edituser() {
    const [inputs, setInputs] = useState([]);
    const {id}=useParams();
    //console.log(id);
   
    useEffect(() => {
        getUser();
    }, []);
   
    function getUser() {
        axios.get(`http://localhost:3001/api/getuser/${id}`).then(function(response) {
            console.log(response.data[0]);
            setInputs(response.data[0]);
        });
    }
  
    const handleChange = (event) => {
        const name = event.target.name;
        const value = event.target.value;
        setInputs(values => ({...values, [name]: value}));
    }
  
    const navigate = useNavigate()
 
    const handleSubmit = (event) => {
        event.preventDefault();
   
        axios.put(`http://localhost:3001/api/edit/${id}`, inputs).then(function(response){
            console.log(response.data);
            navigate('/')
        });
           
    }
    return (
    <div className="max-w-md mx-auto mt-5">
      <h1 className="text-2xl text-center mb-2">Edit Form</h1>
            <form onSubmit={handleSubmit}> 
                <div className="mb-3 mt-3">
                    <label className="block text-sm font-medium text-gray-900"> ID:</label>
                    <input type="text" id="id" name="id" value={id} disabled />
                </div>
                <div className="mb-3 mt-3">
                    <label className="block text-sm font-medium text-gray-900"> Full Name:</label>
                    <input type="text" className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500blue-500" 
                    placeholder="Enter Your Full Name" name="name"
                    value={inputs.name || ''} 
                    onChange={handleChange}/>
                </div>
                <div className="mb-3 mt-3">
                    <label className="block text-sm font-medium text-gray-900">Email:</label>
                    <input type="text" className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500blue-500" 
                    id="email" placeholder="Enter email" name="email"
                    value={inputs.email || ''} 
                    onChange={ handleChange}/>
                </div>
                <div className="mb-3 mt-3">
                    <label className="block text-sm font-medium text-gray-900">Username:</label>
                    <input type="text" className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500blue-500" 
                    id="username" placeholder="Enter username" name="username"
                    value={inputs.username || ''} 
                    onChange={ handleChange}/>
                </div>
                <button type="submit" name="update"  className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
                    Update</button>
            </form>
    </div>
  );
}
Run C:\react-j\my-app>npm start
http://localhost:3000/

Saturday, July 27, 2024

Next.js 14 Node Express CRUD (Create Read Update and Delete) | MySQL

Next.js 14 Node Express CRUD (Create Read Update and Delete) | MySQL

https://expressjs.com/
Express JS
Fast, unopinionated, minimalist web framework for Node.js

Install
$ npm install express --savev PS C:\nodeproject> npm install express --save
https://expressjs.com/en/starter/hello-world.html

mysql
$ npm install mysql
PS C:\nodeproject>npm install mysql

cors
CORS is a node.js package for providing a Connect/Express middleware that can be used to enable CORS with various options.
https://www.npmjs.com/package/cors
PS C:\nodeproject>npm i cors

bcrypt
https://www.npmjs.com/package/bcrypt
A library to help you hash passwords.
npm install bcrypt

run PS C:\nodeproject> node index.js
index.js
//index.js
const express = require('express')
const bodyParser = require('body-parser');
const cors = require('cors');
const bycrypt = require('bcrypt'); //npm install bcrypt
const db = require('./db');

const app = express()

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.use(cors({
      origin: 'http://localhost:3000',
      credentials: true
}));

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.post("/api/adduser", async (req, res) => {
  const sql ="INSERT INTO users (name,email,username,password) VALUES (?, ?, ?, ?)";

  const hashedPassword =  await bycrypt.hash(req.body.password, 10);

  const values = [req.body.name, req.body.email, req.body.username, hashedPassword];
  db.query(sql, values, (err, result) => {
    if (err)
      return res.json({ message: "Something unexpected has occured" + err });
    return res.json({ success: "New User added successfully" });
  });
});

app.get("/api/users", (req, res) => {
  const sql = "SELECT * FROM users";
  db.query(sql, (err, result) => {
    if (err) res.json({ message: "Server error" });
    return res.json(result);
  });
});

app.get("/api/getuser/:id", (req, res) => {
  const id = req.params.id;
  const sql = "SELECT * FROM users WHERE id= ?";
  db.query(sql, [id], (err, result) => {
    if (err) res.json({ message: "Server error" });
    return res.json(result);
  });
});

app.put("/api/edit/:id", async (req, res) => {
  const id = req.params.id;
  const sql ="UPDATE users SET name=?, email=?, username=? WHERE id=?";
  
  const values = [
    req.body.name,
    req.body.email,
    req.body.username,
    id,
  ];
  db.query(sql, values, (err, result) => {
    if (err)
      return res.json({ message: "Something unexpected has occured" + err });
    return res.json({ success: "User updated successfully" });
  });
});

app.delete("/api/delete/:id", (req, res) => {
  const id = req.params.id;
  const sql = "DELETE FROM users WHERE id=?";
  const values = [id];
  db.query(sql, values, (err, result) => {
    if (err)
      return res.json({ message: "Something unexpected has occured" + err });
    return res.json({ success: "Student successfully Deleted" });
  });
});

app.listen(3001, () => {console.log('Server started on port 3001')});
db.js
//db.js
const mysql = require("mysql");

const db = mysql.createPool({
    socketPath: '/Applications/MAMP/tmp/mysql/mysql.sock',
    connectionLimit: 10,
    host: "localhost",
    user: "root",
    password: "root",
    database: "nodeexpressDB"
});

// Ping database to check for common exception errors.
db.getConnection((err, connection) => {
    if (err) {
        if (err.code === 'PROTOCOL_CONNECTION_LOST') {
            console.error('Database connection was closed.');
        }
        if (err.code === 'ER_CON_COUNT_ERROR') {
            console.error('Database has too many connections.');
        }
        if (err.code === 'ECONNREFUSED') {
            console.error('Database connection was refused.');
        }
    }
 
    if (connection) connection.release();
 
    return;
});

module.exports = db;
Next.js

Install requirements
npm install axios
https://www.npmjs.com/package/axios

app\page.js
//app\page.js
import ListUser from '@/components/ListUser'
import { Suspense } from "react";
import Link from "next/link";
 
export default function Home() {
  return (
    <div className="w-screen py-20 flex justify-center flex-col items-center">
      <div className="flex items-center justify-between gap-1 mb-5">
        <h1 className="text-4xl font-bold">Next.js 14 Node Express CRUD (Create Read Update and Delete) | MySQL</h1>
      </div>    
      <div className="overflow-x-auto">
          <div className="mb-2 w-full text-right">
            <Link
              href="/users/create"
              className="btn btn-primary">
              Add New User
            </Link>
          </div>
        <Suspense fallback="Loading...">
            <ListUser/>
          </Suspense>
      </div>  
    </div>
  );
}
app\users\create\page.jsx
//app\users\create\page.jsx
"use client";
 
import React, { useState } from "react";
import axios from 'axios' //npm install axios https://www.npmjs.com/package/axios
import { useRouter } from 'next/navigation';

const Addnewuser = () => {
    const [inputs, setInputs] = useState([]);
  
    const handleChange = (event) => {
        const name = event.target.name;
        const value = event.target.value;
        setInputs(values => ({...values, [name]: value}));
    }
 
    const router = useRouter();

    const handleSubmit = (event) => {
        event.preventDefault();
 
        axios.post('http://localhost:3001/api/adduser', inputs).then(function(response){
            console.log(response.data);
            router.push('/')
        });
    }
 
    return (
    <div className="max-w-md mx-auto mt-5">
        <h1 className="text-2xl text-center mb-2">Add New User</h1>
        <div>
        <form onSubmit={handleSubmit}>
        <div className="mb-5">
          <label htmlFor="name" className="block text-sm font-medium text-gray-900">
            Name
          </label>
          <input
            type="text"
            name="name"
            id="name"
            className="input input-bordered input-primary w-full max-w-xs"
            placeholder="Name..."
            onChange={handleChange}
          />
        </div>
        <div className="mb-5">
          <label htmlFor="email" className="block text-sm font-medium text-gray-900">
            Email
          </label>
          <input
            type="email"
            name="email"
            id="email"
            className="input input-bordered input-primary w-full max-w-xs"
            placeholder="email..."
            onChange={handleChange}
          />
        </div>
        <div className="mb-5">
          <label htmlFor="username" className="block text-sm font-medium text-gray-900">
            Username
          </label>
          <input
            type="text"
            name="username"
            id="username"
            className="input input-bordered input-primary w-full max-w-xs"
            placeholder="Username..."
            onChange={handleChange}
          />
        </div>
        <div className="mb-5">
          <label htmlFor="password" className="block text-sm font-medium text-gray-900">
            Password
          </label>
          <input
            type="password"
            name="password"
            id="password"
            className="input input-bordered input-primary w-full max-w-xs"
            placeholder="Password..."
            onChange={handleChange}
          />
        </div>
        <button type="submit" className="btn btn-primary">Add New User</button> 
      </form>
    </div>
    </div>
  );
};
    
export default Addnewuser;
app\users\edit\[id]\page.jsx
//app\users\edit\[id]\page.jsx
"use client";
   
import React, { useState, useEffect } from 'react';
import axios from 'axios' //npm install axios https://www.npmjs.com/package/axios
import { useParams, useRouter } from 'next/navigation'

export default function Edituser() {
    const [inputs, setInputs] = useState([]);
    const {id}=useParams();
    //console.log(id);
  
    useEffect(() => {
        getUser();
    }, []);
  
    function getUser() {
        axios.get(`http://localhost:3001/api/getuser/${id}`).then(function(response) {
            console.log(response.data[0]);
            setInputs(response.data[0]);
        });
    }
 
    const handleChange = (event) => {
        const name = event.target.name;
        const value = event.target.value;
        setInputs(values => ({...values, [name]: value}));
    }
 
    const router = useRouter();

    const handleSubmit = (event) => {
        event.preventDefault();
  
        axios.put(`http://localhost:3001/api/edit/${id}`, inputs).then(function(response){
            console.log(response.data);
            router.push('/')
        });
          
    }
    return (
    <div className="max-w-md mx-auto mt-5">
      <h1 className="text-2xl text-center mb-2">Edit Form</h1>
            <form onSubmit={handleSubmit}> 
                <div className="mb-3 mt-3">
                    <label className="block text-sm font-medium text-gray-900"> ID:</label>
                    <input type="text" id="id" name="id" value={id} disabled />
                </div>
                <div className="mb-3 mt-3">
                    <label className="block text-sm font-medium text-gray-900"> Full Name:</label>
                    <input type="text" className="input input-bordered input-primary w-full max-w-xs" placeholder="Enter Your Full Name" name="name"
                    value={inputs.name || ''} 
                    onChange={handleChange}/>
                </div>
                <div className="mb-3 mt-3">
                    <label className="block text-sm font-medium text-gray-900">Email:</label>
                    <input type="text" className="input input-bordered input-primary w-full max-w-xs" id="email" placeholder="Enter email" name="email"
                    value={inputs.email || ''} 
                    onChange={ handleChange}/>
                </div>
                <div className="mb-3 mt-3">
                    <label className="block text-sm font-medium text-gray-900">Username:</label>
                    <input type="text" className="input input-bordered input-primary w-full max-w-xs" id="username" placeholder="Enter username" name="username"
                    value={inputs.username || ''} 
                    onChange={ handleChange}/>
                </div>
                <button type="submit" name="update"  className="btn btn-primary">Update</button>
            </form>
    </div>
  );
}
app\users\read\[id]\page.jsx
//app\users\read\[id]\page.jsx
"use client";
   
import React, { useState, useEffect } from 'react';
import axios from 'axios' //npm install axios https://www.npmjs.com/package/axios
import { useParams } from 'next/navigation'
  
export default function ViewUser() {
    const {id}=useParams();
   
    console.log(id);
   
    const[user,setUser]=useState([]);
    
    useEffect(()=>{
        fetchUser();
    },[id]);
    
    const fetchUser=async()=>{
        try{
        const result=await axios.get("http://localhost:3001/api/getuser/"+id);
          console.log(result.data[0]);
          setUser(result.data[0])
    
        }catch(err){
            console.log("Something Wrong");
        }
    }
   
    return (
    <div className="max-w-2xl mx-auto mt-5">
      <h1 className="text-2xl text-center mb-2">View User</h1>
      <table className="table table-zebra">
          <thead className="text-sm text-gray-700 uppercase bg-gray-50">
            <tr>
              <th>S No.</th>
              <th>Name</th>
              <th>Email</th>         
              <th>Username</th>      
            </tr>
          </thead>
          <tbody>
            <tr>
                <td>{user.id}</td>
                <td>{user.name}</td>
                <td>{user.email}</td>
                <td>{user.username}</td>
            </tr>
          </tbody>
      </table>
    </div>
  );
}
app\users\components\ListUser.jsx
//app\users\components\ListUser.jsx
"use client";
 
import axios from 'axios';
import { useEffect, useState } from "react";
import Link from "next/link";
 
export default function ListUser() {
 
    const [users, setUsers] = useState([]);
 
    useEffect(() => {
        getUsers();
    }, []);
  
    function getUsers() { 
        axios.get('http://localhost:3001/api/users').then(function(response) {
            //console.log(response.data);
            setUsers(response.data);
        });
    }
 
    const deleteUser = (id) => {
        axios.delete(`http://localhost:3001/api/delete/${id}`).then(function(response){
            console.log(response.data);
            getUsers();
        });
    }
     
    return (
        <table className="table table-zebra">
            <thead className="text-sm text-gray-700 uppercase bg-gray-50">
                <tr>
                    <th className="py-3 px-6">#</th>
                    <th className="py-3 px-6">Name</th>
                    <th className="py-3 px-6">Email</th>
                    <th className="py-3 px-6">Username</th>
                    <th className="py-3 px-6 text-center">Actions</th>
                </tr>
            </thead>
            <tbody>
                {users.map((user, key) =>
                    <tr key={key} className="bg-white border-b">
                        <td className="py-3 px-6">{user.id}</td>
                        <td className="py-3 px-6">{user.name}</td>
                        <td className="py-3 px-6">{user.email}</td>
                        <td className="py-3 px-6">{user.username}</td>
                        <td className="flex justify-center gap-1 py-3">
                            <Link
                                href={`/users/read/${user.id}`} 
                                className="btn btn-success">
                                Read
                            </Link>
                            <Link className="btn btn-info" href={`users/edit/${user.id}/`}>
                                Edit
                            </Link>
                            <button onClick={() => deleteUser(user.id)} className="btn btn-error">Delete</button>
                        </td>
                    </tr>
                )}
            </tbody>
        </table>
    )
}
run C:\nextjs>npm run dev

Tuesday, July 23, 2024

Next.js 14 Node Express Login Register with Passportjs | Mysql

Next.js 14 Node Express Login Register with Passportjs | Mysql

https://expressjs.com/
Express JS
Fast, unopinionated, minimalist web framework for Node.js

Install
$ npm install express --savev PS C:\nodeproject> npm install express --save
https://expressjs.com/en/starter/hello-world.html

mysql
https://github.com/mysqljs/mysql
$ npm install mysql
PS C:\nodeproject>npm install mysql

cors
CORS is a node.js package for providing a Connect/Express middleware that can be used to enable CORS with various options.
https://www.npmjs.com/package/cors
PS C:\nodeproject>npm i cors

Passport
https://www.npmjs.com/package/passport
Passport is Express-compatible authentication middleware for Node.js.
npm i passport

express-session
https://www.npmjs.com/package/express-session
Create a session middleware with the given options.
npm install express-session

cookie-parser
https://www.npmjs.com/package/cookie-parser
Parse Cookie header and populate req.cookies with an object keyed by the cookie names.
npm install cookie-parser

bcrypt
https://www.npmjs.com/package/bcrypt
A library to help you hash passwords.
npm install bcrypt

passport-local
https://www.npmjs.com/package/passport-local
Passport strategy for authenticating with a username and password.
npm install passport-local

run PS C:\nodeproject> node index.js
index.js
//index.js
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const passport = require('passport'); //npm install passport https://www.npmjs.com/package/passport
const expressSession = require('express-session'); //npm install express-session  https://www.npmjs.com/package/express-session
const cookieParser = require('cookie-parser'); //npm install cookie-parser https://www.npmjs.com/package/cookie-parser
const bycrypt = require('bcrypt'); //npm install bcrypt https://github.com/kelektiv/node.bcrypt.js
const db = require('./db');

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressSession({ secret: 'cairocoders-ednalan', resave: false, saveUninitialized: false })); 

app.use(cors({
      origin: 'http://localhost:3000',
      credentials: true
}));

app.use(cookieParser('cairocoders-ednalan')); 

app.use(passport.initialize());
app.use(passport.session());
require('./passportConfig')(passport); 

//routes
app.post('/register', (req, res) => {
  
    const query = "INSERT INTO users (username, password) VALUES (?,?)";
    const query2 = "SELECT * FROM users where username = ?";
  
    db.query(query2, [req.body.username] ,async (err, rows) => {
      if (err) {console.log(err);}
      if (rows.length > 0) {res.send("User already exists");}
      if (rows.length === 0) {
        const hashedPassword =  await bycrypt.hash(req.body.password, 10);
        db.query(query, [req.body.username, hashedPassword], (err, rows) => {
          if (err) {console.log(err);}
          res.send("User created");
        });
      }
    })
})

app.post('/login', (req, res, next) => {
  passport.authenticate('local', (err, user, info) => { 
    if (err) {console.log(err);}
    if (!user) {res.send("User not found");}
    if (user) {
      req.login(user, (err) => {
        if (err) {console.log(err);}
        res.send("success");
        console.log(user);
      })
    }
  })(req, res, next); 
})

app.get('/getUser', (req, res) => {
  res.send(req.user);
  console.log(req.user);
})

app.listen(3001, () => {console.log('Server started on port 3001')});
db.js
//db.js
const mysql = require("mysql");

const db = mysql.createPool({
    socketPath: '/Applications/MAMP/tmp/mysql/mysql.sock',
    connectionLimit: 10,
    host: "localhost",
    user: "root",
    password: "root",
    database: "nodeexpressDB"
});

// Ping database to check for common exception errors.
db.getConnection((err, connection) => {
    if (err) {
        if (err.code === 'PROTOCOL_CONNECTION_LOST') {
            console.error('Database connection was closed.');
        }
        if (err.code === 'ER_CON_COUNT_ERROR') {
            console.error('Database has too many connections.');
        }
        if (err.code === 'ECONNREFUSED') {
            console.error('Database connection was refused.');
        }
    }
 
    if (connection) connection.release();
 
    return;
});

module.exports = db;
passportConfig.js
//passportConfig.js
const db = require('./db');
const bcrypt = require('bcrypt');
const localStrategy = require('passport-local').Strategy; //npm install passport-local https://www.npmjs.com/package/passport-local

module.exports = function(passport) {
    passport.use(
        new localStrategy((username, password, done) => {
            const query = "SELECT * FROM users where username = ?";
            db.query(query, [username] ,(err, rows) => {
                if(err)throw err;  
                if(rows.length === 0) {
                    return done(null, false);
                }
                bcrypt.compare(password, rows[0].password, (err, result) => {
                    if (err) throw err;
                    if (result === true) {
                        return done(null, rows[0]);
                    } 
                    else {
                        return done(null, false);
                    }
                })
            })
        }))


    passport.serializeUser((user, done) => {
        done(null, user.id);
    })


    passport.deserializeUser((id, done) => {
        const query = "SELECT * FROM users where id = ?";
        db.query(query, [id] ,(err, rows) => {
            if(err)throw err;  
            const userInfo = {
                id: rows[0].id,
                username: rows[0].username
            }
            done(null, userInfo);
        })
    }) 
}
Next.js

Install requirements
npm install axios
https://www.npmjs.com/package/axios

app\page.js
//
export default function Home() {
  return (
    <div className="w-screen py-20 flex justify-center flex-col items-center">
      <div className="flex items-center justify-between gap-1 mb-5">
        <h1 className="text-4xl font-bold">Next.js 14 Node Express Login Register with Passportjs | Mysql</h1>
      </div>    
      Homepage 
    </div>
  );
}
app\users\login\page.jsx
//app\users\login\page.jsx
"use client";

import { useState } from 'react'
import axios  from 'axios'
import Link from 'next/link';
import { useRouter } from 'next/navigation';

export default function Home() {

    const [loginUsername, setLoginUsername] = useState('')
    const [ loginPassword, setLoginPassword ] = useState('')  
    const[message, setMessage]= useState('');

    const router = useRouter();

    const login = () => {
        axios({
        method: 'post',
        data: {
            username: loginUsername,
            password: loginPassword
        },
        withCredentials: true,
        url: 'http://localhost:3001/login'
        })
        .then(res => {
          console.log(res)
          if (res.data == "success") {
              router.push('/users/profile')
          }else {
              setMessage(res.data); 
          }
        })
        .catch(err => {console.log(err)})
    }

    return (
    <div className="flex justify-center relative">
      <div className="mx-auto max-w-lg px-6 lg:px-8 absolute py-20">
        <h1 className="text-center text-3xl">Login </h1>
        <div className="rounded-2xl bg-white shadow-xl">
          <div className="lg:p-11 p-7 mx-auto">
            <div className="mb-11">
                <h1 className="text-gray-900 text-center font-manrope text-3xl font-bold leading-10 mb-2">Welcome Back</h1>
                <p className="text-gray-500 text-center text-base font-medium leading-6">Let’s get started with your 30 days free trail</p>
            </div>
            <input type="text" className="w-full h-12 text-gray-900 placeholder:text-gray-400 text-lg font-normal leading-7 rounded-full border-gray-300 border shadow-sm focus:outline-none px-4 mb-6" 
                placeholder="Username" 
                name="username"
                onChange={e => setLoginUsername(e.target.value )}
                />
            <input type="text" className="w-full h-12 text-gray-900 placeholder:text-gray-400 text-lg font-normal leading-7 rounded-full border-gray-300 border shadow-sm focus:outline-none px-4 mb-1" 
                placeholder="Password" 
                name="password"
                onChange={e => setLoginPassword(e.target.value )}
                />
            <Link href="#" className="flex justify-end mb-6">
              <span className="text-indigo-600 text-right text-base font-normal leading-6">Forgot Password?</span>
            </Link>
            <button  onClick={login} className="w-full h-12 text-white text-center text-base font-semibold leading-6 rounded-full hover:bg-indigo-800 transition-all duration-700 bg-indigo-600 shadow-sm mb-11">Login</button>
            <p className="text-red-700"><b>{ message }</b></p>
            <Link href="/users/register" className="flex justify-center text-gray-900 text-base font-medium leading-6"> Don’t have an account? <span className="text-indigo-600 font-semibold pl-3"> Sign Up</span>
            </Link>
          </div>
        </div>
      </div> 
    </div>
  );
}
app\users\register\page.jsx
//app\users\register\page.jsx
"use client";

import { useState } from 'react'
import axios  from 'axios'
import Link from 'next/link';
import { useRouter } from 'next/navigation';

export default function Home() {

    const [registerUsername, setRegisterUsername] = useState('')
    const [ registerPassword, setRegisterPassword ] = useState('')
    const[message, setMessage]= useState('');

    const router = useRouter();

    const register = () => {
        axios({
        method: 'post',
        data: {
            username: registerUsername,
            password: registerPassword
        },
        withCredentials: true,
        url: 'http://localhost:3001/register'
        })
        .then(res => {
            console.log(res)
            if (res.data == "User already exists") {
                setMessage(res.data); 
            }else {
                router.push('/')
            }
        })
        .catch(err => {console.log(err)})
    }


    return (
    <div className="flex justify-center relative">
      <div className="mx-auto max-w-lg px-6 lg:px-8 absolute py-20">
        <h1 className="text-center text-3xl">Register </h1>
        <div className="rounded-2xl bg-white shadow-xl">
          <div className="lg:p-11 p-7 mx-auto">
            <div className="mb-11">
                <h1 className="text-gray-900 text-center font-manrope text-3xl font-bold leading-10 mb-2">Create Account</h1>
                <p className="text-gray-500 text-center text-base font-medium leading-6">Get started with your free account</p>
            </div>
            <input type="text" className="w-full h-12 text-gray-900 placeholder:text-gray-400 text-lg font-normal leading-7 rounded-full border-gray-300 border shadow-sm focus:outline-none px-4 mb-6" 
                placeholder="Username" 
                name="username"
                onChange={e => setRegisterUsername(e.target.value )}
                />
            <input type="text" className="w-full h-12 text-gray-900 placeholder:text-gray-400 text-lg font-normal leading-7 rounded-full border-gray-300 border shadow-sm focus:outline-none px-4 mb-1" 
                placeholder="Password" 
                name="password"
                onChange={e => setRegisterPassword(e.target.value)}
                />
            <button  onClick={register} className="w-full h-12 text-white text-center text-base font-semibold leading-6 rounded-full hover:bg-indigo-800 transition-all duration-700 bg-indigo-600 shadow-sm mb-11">Submit</button>
            <p className="text-red-700"><b>{ message }</b></p>
            <Link href="/users/login" className="flex justify-center text-gray-900 text-base font-medium leading-6"> have an account? <span className="text-indigo-600 font-semibold pl-3"> Login</span>
            </Link>
          </div>
        </div>
      </div> 
    </div>
  );
}
app\users\profile\page.jsx
//app\users\profile\page.jsx
"use client";

import { useState } from 'react'
import axios  from 'axios'

export default function Home() {

    const [ user, setUser ] = useState(null)

    const getUser  = () => {
        axios({
        method: 'get',
        withCredentials: true,
        url: 'http://localhost:3001/getUser'
        }).then(res => {setUser(res.data.username)}).catch(err => {console.log(err)})
    }

    return (
    <div className="flex justify-center relative">
      <div className="mx-auto max-w-lg px-6 lg:px-8 absolute py-20">
        <h1 className="text-center text-3xl">Profile </h1>
        <div className="rounded-2xl bg-white shadow-xl">
          <button onClick={getUser} className="w-full h-12 text-white text-center text-base font-semibold leading-6 rounded-full hover:bg-indigo-800 transition-all duration-700 bg-indigo-600 shadow-sm mb-11">Submit</button>
          {user ? <h1>{user}</h1> : null}
        </div>
      </div> 
    </div>
  );
}
run C:\nextjs>npm run dev

Friday, July 19, 2024

Next.js 14 CRUD (Create Read Update and Delete) with PHP MySQL

Next.js 14 CRUD (Create Read Update and Delete) with PHP MySQL

Install nextjs
https://nextjs.org/
npx create-next-app@latest

install axios
npm install axios
https://www.npmjs.com/package/axios
app\page.js
//app\page.js
import ListUser from '@/components/ListUser'
import { Suspense } from "react";
import Link from "next/link";

export default function Home() {
  return (
    <div className="w-screen py-20 flex justify-center flex-col items-center">
      <div className="flex items-center justify-between gap-1 mb-5">
        <h1 className="text-4xl font-bold">Next.js 14 CRUD (Create Read Update and Delete) with PHP MySQL</h1>
      </div>    
      <div className="overflow-x-auto">
          <div className="mb-2 w-full text-right">
            <Link
              href="/users/create"
              className="btn btn-primary">
              Add New User
            </Link>
          </div>
        <Suspense fallback="Loading...">
            <ListUser/>
          </Suspense>
      </div>  
    </div>
  );
}
components\ListUser.jsx
//components\ListUser.jsx
"use client";

import axios from 'axios';
import { useEffect, useState } from "react";
import Link from "next/link";

export default function ListUser() {

    const [users, setUsers] = useState([]);

    useEffect(() => {
        getUsers();
    }, []);
 
    function getUsers() { 
        axios.get('http://localhost:8888/devproject/nexjsphpmysl/api/').then(function(response) {
            console.log(response.data);
            setUsers(response.data);
        });
    }

    const deleteUser = (id) => {
        axios.delete(`http://localhost:8888/devproject/nexjsphpmysl/api/${id}/delete`).then(function(response){
            console.log(response.data);
            getUsers();
        });
    }
    
    return (
        <table className="table table-zebra">
            <thead className="text-sm text-gray-700 uppercase bg-gray-50">
                <tr>
                    <th className="py-3 px-6">#</th>
                    <th className="py-3 px-6">Name</th>
                    <th className="py-3 px-6">Email</th>
                    <th className="py-3 px-6">Mobile</th>
                    <th className="py-3 px-6 text-center">Actions</th>
                </tr>
            </thead>
            <tbody>
                {users.map((user, key) =>
                    <tr key={key} className="bg-white border-b">
                        <td className="py-3 px-6">{user.id}</td>
                        <td className="py-3 px-6">{user.name}</td>
                        <td className="py-3 px-6">{user.email}</td>
                        <td className="py-3 px-6">{user.mobile}</td>
                        <td className="flex justify-center gap-1 py-3">
                            <Link
                                href={`/users/view/${user.id}`} 
                                className="btn btn-success">
                                View
                            </Link>
                            <Link className="btn btn-info" href={`users/edit/${user.id}/`}>
                                Edit
                            </Link>
                            <button onClick={() => deleteUser(user.id)} className="btn btn-error">Delete</button>
                        </td>
                    </tr>
                )}
            </tbody>
        </table>
    )
}
app\users\create\page.jsx
//app\users\create\page.jsx
"use client";

import React, { useState } from "react";
import axios from 'axios' //npm install axios https://www.npmjs.com/package/axios

const Addnewuser = () => {
    const [inputs, setInputs] = useState([]);
 
    const handleChange = (event) => {
        const name = event.target.name;
        const value = event.target.value;
        setInputs(values => ({...values, [name]: value}));
    }

    const handleSubmit = (event) => {
        event.preventDefault();

        axios.post('http://localhost:8888/devproject/nexjsphpmysl/api/save', inputs).then(function(response){
            console.log(response.data);
            window.location.href = '/';
        });
    }

    return (
    <div className="max-w-md mx-auto mt-5">
        <h1 className="text-2xl text-center mb-2">Add New User</h1>
        <div>
        <form onSubmit={handleSubmit}>
        <div className="mb-5">
          <label htmlFor="name" className="block text-sm font-medium text-gray-900">
            Name
          </label>
          <input
            type="text"
            name="name"
            id="name"
            className="input input-bordered input-primary w-full max-w-xs"
            placeholder="Name..."
            onChange={handleChange}
          />
        </div>
        <div className="mb-5">
          <label htmlFor="email" className="block text-sm font-medium text-gray-900">
            Email
          </label>
          <input
            type="email"
            name="email"
            id="email"
            className="input input-bordered input-primary w-full max-w-xs"
            placeholder="email..."
            onChange={handleChange}
          />
        </div>
        <div className="mb-5">
          <label htmlFor="mobile" className="block text-sm font-medium text-gray-900">
            Mobile
          </label>
          <input
            type="text"
            name="mobile"
            id="mobile"
            className="input input-bordered input-primary w-full max-w-xs"
            placeholder="mobile..."
            onChange={handleChange}
          />
        </div>
        <button type="submit" className="btn btn-primary">Add New User</button> 
      </form>
    </div>
    </div>
  );
};
   
export default Addnewuser;
app\users\edit\[id]\page.jsx
//app\users\edit\[id]\page.jsx
"use client";
  
import React, { useState, useEffect } from 'react';
import axios from 'axios' //npm install axios https://www.npmjs.com/package/axios
import { useParams } from 'next/navigation'
 
export default function Edituser() {
    const [inputs, setInputs] = useState([]);
    const {id}=useParams();
    console.log(id);
 
    useEffect(() => {
        getUser();
    }, []);
 
    function getUser() {
        axios.get(`http://localhost:8888/devproject/nexjsphpmysl/api/${id}`).then(function(response) {
            console.log(response.data);
            setInputs(response.data);
        });
    }

    const handleChange = (event) => {
        const name = event.target.name;
        const value = event.target.value;
        setInputs(values => ({...values, [name]: value}));
    }

    const handleSubmit = (event) => {
        event.preventDefault();
 
        axios.put(`http://localhost:8888/devproject/nexjsphpmysl/api/${id}/edit`, inputs).then(function(response){
            console.log(response.data);
            window.location.href = '/';
        });
         
    }
    return (
    <div className="max-w-md mx-auto mt-5">
      <h1 className="text-2xl text-center mb-2">Edit Form</h1>
            <form onSubmit={handleSubmit}> 
                <div className="mb-3 mt-3">
                    <label className="block text-sm font-medium text-gray-900"> ID:</label>
                    <input type="text" id="id" name="id" value={id} disabled />
                </div>
                <div className="mb-3 mt-3">
                    <label className="block text-sm font-medium text-gray-900"> Full Name:</label>
                    <input type="text" className="input input-bordered input-primary w-full max-w-xs" placeholder="Enter Your Full Name" name="name"
                    value={inputs.name || ''} 
                    onChange={handleChange}/>
                </div>
                <div className="mb-3 mt-3">
                    <label className="block text-sm font-medium text-gray-900">Email:</label>
                    <input type="text" className="input input-bordered input-primary w-full max-w-xs" id="email" placeholder="Enter email" name="email"
                    value={inputs.email || ''} 
                    onChange={ handleChange}/>
                </div>
                <div className="mb-3 mt-3">
                    <label className="block text-sm font-medium text-gray-900">Mobile:</label>
                    <input type="text" className="input input-bordered input-primary w-full max-w-xs" id="mobile" placeholder="Enter mobile" name="mobile"
                    value={inputs.mobile || ''} 
                    onChange={ handleChange}/>
                </div>
                <button type="submit" name="update"  className="btn btn-primary">Update</button>
            </form>
    </div>
  );
}
app\users\view\[id]\page.jsx
//app\users\view\[id]\page.jsx
"use client";
  
import React, { useState, useEffect } from 'react';
import axios from 'axios' //npm install axios https://www.npmjs.com/package/axios
import { useParams } from 'next/navigation'
 
export default function ViewUser() {
    const {id}=useParams();
  
    console.log(id);
  
    const[user,setUser]=useState([]);
   
    useEffect(()=>{
        fetchUser();
    },[id]);
   
    const fetchUser=async()=>{
        try{
        const result=await axios.get("http://localhost:8888/devproject/nexjsphpmysl/api/"+id);
          console.log(result.data);
          setUser(result.data)
   
        }catch(err){
            console.log("Something Wrong");
        }
    }
  
    return (
    <div className="max-w-2xl mx-auto mt-5">
      <h1 className="text-2xl text-center mb-2">View User</h1>
      <table className="table table-zebra">
          <thead className="text-sm text-gray-700 uppercase bg-gray-50">
            <tr>
              <th>S No.</th>
              <th>Name</th>
              <th>Email</th>         
              <th>Mobile</th>      
            </tr>
          </thead>
          <tbody>
            <tr>
                <td>{user.id}</td>
                <td>{user.name}</td>
                <td>{user.email}</td>
                <td>{user.mobile}</td>
            </tr>
          </tbody>
      </table>
    </div>
  );
}
devproject/nexjsphpmysl/api/index.php
//devproject/nexjsphpmysl/api/index.php
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: *");
header("Access-Control-Allow-Methods: *");

include 'DbConnect.php';
$objDb = new DbConnect;
$conn = $objDb->connect();

$method = $_SERVER['REQUEST_METHOD'];
switch ($method) {
    case "GET":
        $sql = "SELECT * FROM users";
        $path = explode('/', $_SERVER['REQUEST_URI']);
        if (isset($path[4]) && is_numeric($path[4])) {
            $sql .= " WHERE id = :id";
            $stmt = $conn->prepare($sql);
            $stmt->bindParam(':id', $path[4]);
            $stmt->execute();
            $users = $stmt->fetch(PDO::FETCH_ASSOC);
        } else {
            $stmt = $conn->prepare($sql);
            $stmt->execute();
            $users = $stmt->fetchAll(PDO::FETCH_ASSOC);
        }

        echo json_encode($users);
        break;

    case "POST":
        $user = json_decode(file_get_contents('php://input'));
        $sql = "INSERT INTO users(id, name, email, mobile, created_at) VALUES(null, :name, :email, :mobile, :created_at)";
        $stmt = $conn->prepare($sql);
        $created_at = date('Y-m-d');
        $stmt->bindParam(':name', $user->name);
        $stmt->bindParam(':email', $user->email);
        $stmt->bindParam(':mobile', $user->mobile);
        $stmt->bindParam(':created_at', $created_at);

        if ($stmt->execute()) {
            $response = ['status' => 1, 'message' => 'Record created successfully.'];
        } else {
            $response = ['status' => 0, 'message' => 'Failed to create record.'];
        }
        echo json_encode($response);
        break;

    case "PUT":
        $user = json_decode(file_get_contents('php://input'));
        $sql = "UPDATE users SET name= :name, email =:email, mobile =:mobile, updated_at =:updated_at WHERE id = :id";
        $stmt = $conn->prepare($sql);
        $updated_at = date('Y-m-d');
        $stmt->bindParam(':id', $user->id);
        $stmt->bindParam(':name', $user->name);
        $stmt->bindParam(':email', $user->email);
        $stmt->bindParam(':mobile', $user->mobile);
        $stmt->bindParam(':updated_at', $updated_at);

        if ($stmt->execute()) {
            $response = ['status' => 1, 'message' => 'Record updated successfully.'];
        } else {
            $response = ['status' => 0, 'message' => 'Failed to update record.'];
        }
        echo json_encode($response);
        break;

    case "DELETE":
        $sql = "DELETE FROM users WHERE id = :id";
        $path = explode('/', $_SERVER['REQUEST_URI']);

        $stmt = $conn->prepare($sql);
        $stmt->bindParam(':id', $path[4]);

        if ($stmt->execute()) {
            $response = ['status' => 1, 'message' => 'Record deleted successfully. ' . $path[4] . ' '];
        } else {
            $response = ['status' => 0, 'message' => 'Failed to delete record.'];
        }
        echo json_encode($response);
        break;
}
devproject/nexjsphpmysl/api/DbConnect.php
//devproject/nexjsphpmysl/api/DbConnect.php
<?php
class DbConnect
{
    private $server = 'localhost';
    private $dbname = 'nextjsdb';
    private $user = 'root';
    private $pass = 'root';

    public function connect()
    {
        try {
            $conn = new PDO('mysql:host=' . $this->server . ';dbname=' . $this->dbname, $this->user, $this->pass);
            $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            return $conn;
        } catch (\Exception $e) {
            echo "Database Error: " . $e->getMessage();
        }
    }
}

devproject/nexjsphpmysl/api/.htaccess
RewriteEngine On
 
# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
Run C:\myapp-js>npm start

Check API Results in Postman
https://www.postman.com/downloads/
POST method : http://localhost/devproject/nexjsphpmysl/api/save
Get method : http://localhost/devproject/nexjsphpmysl/api/
PUT method : http://localhost/devproject/nexjsphpmysl/api/1
DELTE method : http://localhost/devproject/nexjsphpmysl/api/1

Wednesday, July 17, 2024

Next.js 14 Fetch data from the database using PHP API Mysql

Next.js 14 Fetch data from the database using PHP API Mysql

Install nextjs npx create-next-app@latest https://nextjs.org/docs/getting-started/installation
app\page.js
//app\page.js 
import UserDataComponent from '@/components/UserDataComponent'
import { Suspense } from "react";

export default async function Home() {
  return (
    <div className="w-screen py-20 flex justify-center flex-col items-center">
      <div className="flex items-center justify-between gap-1 mb-5">
        <h1 className="text-4xl font-bold">Next.js 14 Fetch data from the database using PHP API Mysql</h1>
      </div>    
      <div className="overflow-x-auto">
        <Suspense fallback="Loading...">
            <UserDataComponent/>
          </Suspense>
      </div>  
    </div>
  );
}
components\UserDataComponent.js
//components\UserDataComponent.js
"use client";

import Axios from 'axios';
import React, { useEffect, useState } from "react";

const UserDataComponent = () => {

    const [userData,setUserData]= useState([]);

    useEffect(() => {
        const fetchUsers = async () => {
            Axios.get('http://localhost:8888/devproject/getUserData.php')
            .then(function (data) {
                //handle success
                console.log('userData',data)
                setUserData(data.data)
            })
            .catch(function (error) {
                //handle error
                console.log(error)
            });
        }
        fetchUsers()
    }, [])
    

    return (
        <div>
        <h1>User  List</h1>
        <table className="table table-zebra">
        <thead className="text-sm text-gray-700 uppercase bg-gray-50">
            <tr>
            <th className="py-3 px-6">#</th>
            <th className="py-3 px-6">Name</th>
            <th className="py-3 px-6">Email</th>
            <th className="py-3 px-6">Password</th>
            </tr>
        </thead>
        <tbody>
            {userData.map((rs, index) => (
            <tr key={rs.id} className="bg-white border-b">
                <td className="py-3 px-6">{rs.id}</td>
                <td className="py-3 px-6">{rs.name}</td>
                <td className="py-3 px-6">{rs.email}</td>
                <td className="py-3 px-6">{rs.password}</td>
            </tr>
            ))}
        </tbody>
        </table>
        </div>
    )
}

export default UserDataComponent
run C:\nextjs>npm run dev

dbcon.php
//dbcon.php
<?php
//$mysqli = new mysqli("hostName", "dbUserId", "dbPassword", "dbName");
$mysqli = new mysqli("localhost", "root", "root", "devproject");
?>
getUserData.php
//getUserData.php
<?php
 //getUserData.php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: access");
header("Access-Control-Allow-Methods: GET");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

//Database Connection
include 'dbcon.php';
$value=array();
$Sql_Query ="SELECT * from users";
$result = $mysqli->query($Sql_Query);
if (@$result->num_rows >0) {
    while($row[] = $result->fetch_assoc()) {
        $item = $row;
        $json = json_encode($item,JSON_PRETTY_PRINT);
    }
    echo @$json;
} else {
    echo json_encode($value,JSON_PRETTY_PRINT);;
}

$mysqli->close();
?>

Thursday, July 4, 2024

Next.js 14 NextAuth.js Authentication Login Register profile MongoDB | Tailwindcss

Next.js 14 NextAuth.js Authentication Login Register profile MongoDB | Tailwindcss

Install nextjs npx create-next-app@latest https://nextjs.org/docs/getting-started/installation

Install the following

NextAuth.js
npm install next-auth
https://www.npmjs.com/package/next-auth/v/4.23.1
NextAuth.js is a complete open source authentication solution for Next.js applications.

Mongoose
npm install mongoose
https://www.npmjs.com/package/mongoose

bcrypt.js
npm install bcryptjs
https://www.npmjs.com/package/bcryptjs

app\page.js
//app\page.js
import Image from "next/image";
import Link from "next/link";

export default async function Home() {
  return (
    <>
    <nav id="header" className="fixed w-full z-30 top-0 text-white">
      <div className="w-full container mx-auto flex flex-wrap items-center justify-between mt-0 py-2">
        <div className="pl-4 flex items-center">
          <span className="toggleColour text-white no-underline hover:no-underline font-bold text-2xl lg:text-4xl">
            Cairocoders
          </span>
        </div>
        <div className="w-full flex-grow lg:flex lg:items-center lg:w-auto hidden mt-2 lg:mt-0 bg-white lg:bg-transparent text-black p-4 lg:p-0 z-20" id="nav-content">
          <ul className="list-reset lg:flex justify-end flex-1 items-center">
            <li className="mr-3">
              <Link className="inline-block py-2 px-4 text-black font-bold no-underline" href={"/"}>
                Home
              </Link>
            </li>
            <li className="mr-3">
              <Link className="inline-block text-black no-underline hover:text-gray-800 hover:text-underline py-2 px-4" href={"/"}>
                About
              </Link>
            </li>
          </ul>
            <Link className="mx-auto lg:mx-0 hover:underline bg-white text-gray-800 font-bold rounded-full mt-4 lg:mt-0 py-4 px-8 shadow opacity-75 focus:outline-none focus:shadow-outline transform transition hover:scale-105 duration-300 ease-in-out" 
              href={"/login"}>
                Login
            </Link>
        </div>
      </div>
      <hr className="border-b border-gray-100 opacity-25 my-0 py-0" />
    </nav>

 <div>
  {/*Hero*/}
  <div className="pt-24">
    <div className="container px-3 mx-auto flex flex-wrap flex-col md:flex-row items-center">
      {/*Left Col*/}
      <div className="flex flex-col w-full md:w-2/5 justify-center items-start text-center md:text-left">
        <p className="uppercase tracking-loose w-full">What business are you?</p>
        <h1 className="my-4 text-5xl font-bold leading-tight">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam
        </h1>
        <p className="leading-normal text-2xl mb-8">
          Snunc commodo posuere et sit amet ligula
        </p>
        <button className="mx-auto lg:mx-0 hover:underline bg-white text-gray-800 font-bold rounded-full my-6 py-4 px-8 shadow-lg focus:outline-none focus:shadow-outline transform transition hover:scale-105 duration-300 ease-in-out">
          Subscribe
        </button>
      </div>
      {/*Right Col*/}
      <div className="w-full md:w-3/5 py-6 text-center">
        <Image
              src="/hero.png"
              alt="hero"
              className="w-full md:w-4/5 z-50"
              width={613}
              height={529}
              priority
        />
      </div>
    </div>
  </div>
  <div className="relative -mt-12 lg:-mt-24">
    <svg viewBox="0 0 1428 174" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
      <g stroke="none" strokeWidth={1} fill="none" fillRule="evenodd">
        <g transform="translate(-2.000000, 44.000000)" fill="#FFFFFF" fillRule="nonzero">
          <path d="M0,0 C90.7283404,0.927527913 147.912752,27.187927 291.910178,59.9119003 C387.908462,81.7278826 543.605069,89.334785 759,82.7326078 C469.336065,156.254352 216.336065,153.6679 0,74.9732496" opacity="0.100000001" />
          <path d="M100,104.708498 C277.413333,72.2345949 426.147877,52.5246657 546.203633,45.5787101 C666.259389,38.6327546 810.524845,41.7979068 979,55.0741668 C931.069965,56.122511 810.303266,74.8455141 616.699903,111.243176 C423.096539,147.640838 250.863238,145.462612 100,104.708498 Z" opacity="0.100000001" />
          <path d="M1046,51.6521276 C1130.83045,29.328812 1279.08318,17.607883 1439,40.1656806 L1439,120 C1271.17211,77.9435312 1140.17211,55.1609071 1046,51.6521276 Z" id="Path-4" opacity="0.200000003" />
        </g>
        <g transform="translate(-4.000000, 76.000000)" fill="#FFFFFF" fillRule="nonzero">
          <path d="M0.457,34.035 C57.086,53.198 98.208,65.809 123.822,71.865 C181.454,85.495 234.295,90.29 272.033,93.459 C311.355,96.759 396.635,95.801 461.025,91.663 C486.76,90.01 518.727,86.372 556.926,80.752 C595.747,74.596 622.372,70.008 636.799,66.991 C663.913,61.324 712.501,49.503 727.605,46.128 C780.47,34.317 818.839,22.532 856.324,15.904 C922.689,4.169 955.676,2.522 1011.185,0.432 C1060.705,1.477 1097.39,3.129 1121.236,5.387 C1161.703,9.219 1208.621,17.821 1235.4,22.304 C1285.855,30.748 1354.351,47.432 1440.886,72.354 L1441.191,104.352 L1.121,104.031 L0.457,34.035 Z" />
        </g>
      </g>
    </svg>
  </div>
  <section className="bg-white border-b py-8">
    <div className="container max-w-5xl mx-auto m-8">
      <h2 className="w-full my-2 text-5xl font-bold leading-tight text-center text-gray-800">
        Title
      </h2>
    </div>
  </section>
</div>

<footer className="bg-white">
  <div className="container mx-auto px-8">
    <div className="w-full flex flex-col md:flex-row py-6">
      <div className="flex-1 mb-6 text-black">
        <span className="text-pink-600 no-underline hover:no-underline font-bold text-2xl lg:text-4xl">
          Cairocoders
        </span>
      </div>
      <div className="flex-1">
        <p className="uppercase text-gray-500 md:mb-6">Links</p>
        <ul className="list-reset mb-6">
          <li className="mt-2 inline-block mr-2 md:block md:mr-0">
            <a href="#" className="no-underline hover:underline text-gray-800 hover:text-pink-500">FAQ</a>
          </li>
          <li className="mt-2 inline-block mr-2 md:block md:mr-0">
            <a href="#" className="no-underline hover:underline text-gray-800 hover:text-pink-500">Help</a>
          </li>
          <li className="mt-2 inline-block mr-2 md:block md:mr-0">
            <a href="#" className="no-underline hover:underline text-gray-800 hover:text-pink-500">Support</a>
          </li>
        </ul>
      </div>
      <div className="flex-1">
        <p className="uppercase text-gray-500 md:mb-6">Social</p>
        <ul className="list-reset mb-6">
          <li className="mt-2 inline-block mr-2 md:block md:mr-0">
            <a href="#" className="no-underline hover:underline text-gray-800 hover:text-pink-500">Facebook</a>
          </li>
          <li className="mt-2 inline-block mr-2 md:block md:mr-0">
            <a href="#" className="no-underline hover:underline text-gray-800 hover:text-pink-500">Linkedin</a>
          </li>
          <li className="mt-2 inline-block mr-2 md:block md:mr-0">
            <a href="#" className="no-underline hover:underline text-gray-800 hover:text-pink-500">Twitter</a>
          </li>
        </ul>
      </div>
    </div>
  </div>
  </footer>

    </>
  );
}
app\layout.js
//app\layout.js
import { AuthProvider } from "./Providers";
import { Inter } from "next/font/google";
import "./globals.css";

const inter = Inter({ subsets: ["latin"] });

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body className="leading-normal tracking-normal text-white gradient">
        <AuthProvider>{children}</AuthProvider>
      </body>
    </html>
  );
}
app\Providers.js
//app\Providers.js
"use client";

import { SessionProvider } from "next-auth/react";

export const AuthProvider = ({ children }) => {
  return <SessionProvider>{children}</SessionProvider>;
};
app\login\page.jsx
//app\login\page.jsx
import LoginForm from "@/components/LoginForm";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
import { authOptions } from "../api/auth/[...nextauth]/route";

export default async function Home() {
  const session = await getServerSession(authOptions);

  if (session) redirect("/dashboard");

  return (
    <main>
      <LoginForm /> 
    </main>
  );
}
app\register\page.jsx
//app\register\page.jsx
import RegisterForm from "@/components/RegisterForm";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
import { authOptions } from "../api/auth/[...nextauth]/route";

export default async function Register() {
  const session = await getServerSession(authOptions);

  if (session) redirect("/dashboard");

  return <RegisterForm />;
}
app\dashboard\page.jsx
//app\dashboard\page.jsx
import UserInfo from "@/components/UserInfo";
import Link from "next/link";

export default function Dashboard() {
  return (
    <div className="container mx-auto px-4 place-items-center h-screen p-5">
      <UserInfo />
    </div>    
  );
}
app\api\auth\[...nextauth]\route.js
//app\api\auth\[...nextauth]\route.js
import { connectMongoDB } from "@/lib/mongodb";
import User from "@/models/user";
import NextAuth from "next-auth/next";
import CredentialsProvider from "next-auth/providers/credentials";
import bcrypt from "bcryptjs";

export const authOptions = {
  providers: [
    CredentialsProvider({
      name: "credentials",
      credentials: {},

      async authorize(credentials) {
        const { email, password } = credentials;

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

          if (!user) {
            return null;
          }

          const passwordsMatch = await bcrypt.compare(password, user.password);

          if (!passwordsMatch) {
            return null;
          }

          return user;
        } catch (error) {
          console.log("Error: ", error);
        }
      },
    }),
  ],
  session: {
    strategy: "jwt",
  },
  secret: process.env.NEXTAUTH_SECRET,
  pages: {
    signIn: "/",
  },
};

const handler = NextAuth(authOptions);

export { handler as GET, handler as POST };
app\api\register\route.js
//app\api\register\route.js
import { connectMongoDB } from "@/lib/mongodb";
import User from "@/models/user";
import { NextResponse } from "next/server";
import bcrypt from "bcryptjs";

export async function POST(req) {
  try {
    const { name, email, password } = await req.json();
    const hashedPassword = await bcrypt.hash(password, 10);
    await connectMongoDB();
    await User.create({ name, email, password: hashedPassword });

    return NextResponse.json({ message: "User registered." }, { status: 201 });
  } catch (error) {
    return NextResponse.json(
      { message: "An error occurred while registering the user." },
      { status: 500 }
    );
  }
}
app\api\userExists\route.js
//app\api\userExists\route.js
import { connectMongoDB } from "@/lib/mongodb";
import User from "@/models/user";
import { NextResponse } from "next/server";

export async function POST(req) {
  try {
    await connectMongoDB();
    const { email } = await req.json();
    const user = await User.findOne({ email }).select("_id");
    console.log("user: ", user);
    return NextResponse.json({ user });
  } catch (error) {
    console.log(error);
  }
}
components\LoginForm.jsx
//components\LoginForm.jsx
"use client";

import Link from "next/link";
import { useState } from "react";
import { signIn } from "next-auth/react";
import { useRouter } from "next/navigation";

export default function LoginForm() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [error, setError] = useState("");

  const router = useRouter();

  const handleSubmit = async (e) => {
    e.preventDefault();

    try {
      const res = await signIn("credentials", {
        email,
        password,
        redirect: false,
      });

      if (res.error) {
        setError("Invalid Credentials");
        return;
      }

      router.replace("dashboard");
    } catch (error) {
      console.log(error);
    }
  };

  return (
    <div className="grid place-items-center h-screen">
      <div className="shadow-lg p-5 rounded-lg border-t-4 border-green-400 lg:w-5/12">
        <h1 className="text-xl font-bold my-4">Login</h1>

        <form onSubmit={handleSubmit} className="flex flex-col gap-3">
          <div class="flex items-center text-lg mb-6 md:mb-8">
            <svg class="absolute ml-3" width="24" viewBox="0 0 24 24">
              <path d="M20.822 18.096c-3.439-.794-6.64-1.49-5.09-4.418 4.72-8.912 1.251-13.678-3.732-13.678-5.082 0-8.464 4.949-3.732 13.678 1.597 2.945-1.725 3.641-5.09 4.418-3.073.71-3.188 2.236-3.178 4.904l.004 1h23.99l.004-.969c.012-2.688-.092-4.222-3.176-4.935z"/>
            </svg>
            <input
              onChange={(e) => setEmail(e.target.value)}
              type="text"
              className="bg-gray-200 rounded pl-12 py-2 md:py-4 focus:outline-none w-full text-black"
              placeholder="Email"
            />
          </div>

          <div class="flex items-center text-lg mb-6 md:mb-8">
            <svg class="absolute ml-3" viewBox="0 0 24 24" width="24">
              <path d="m18.75 9h-.75v-3c0-3.309-2.691-6-6-6s-6 2.691-6 6v3h-.75c-1.24 0-2.25 1.009-2.25 2.25v10.5c0 1.241 1.01 2.25 2.25 2.25h13.5c1.24 0 2.25-1.009 2.25-2.25v-10.5c0-1.241-1.01-2.25-2.25-2.25zm-10.75-3c0-2.206 1.794-4 4-4s4 1.794 4 4v3h-8zm5 10.722v2.278c0 .552-.447 1-1 1s-1-.448-1-1v-2.278c-.595-.347-1-.985-1-1.722 0-1.103.897-2 2-2s2 .897 2 2c0 .737-.405 1.375-1 1.722z"/>
            </svg>
            <input
              onChange={(e) => setPassword(e.target.value)}
              type="password"
              class="bg-gray-200 rounded pl-12 py-2 md:py-4 focus:outline-none w-full text-black"
              placeholder="Password"
            />
          </div>
          <button className="bg-green-600 text-white p-2 md:p-4 font-bold cursor-pointer px-6 py-2 rounded">
            Login
          </button>
          {error && (
            <div className="bg-red-500 text-white w-fit text-sm py-1 px-3 rounded-md mt-2 w-full p-2 md:p-4">
              {error}
            </div>
          )}

          <Link className="text-sm mt-3 text-right" href={"/register"}>
            Don't have an account? <span className="underline">Register</span>
          </Link>
        </form>
      </div>
    </div>
  );
}
components\RegisterForm.jsx
//components\RegisterForm.jsx
"use client";

import Link from "next/link";
import { useState } from "react";
import { useRouter } from "next/navigation";

export default function RegisterForm() {
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [error, setError] = useState("");

  const router = useRouter();

  const handleSubmit = async (e) => {
    e.preventDefault();

    if (!name || !email || !password) {
      setError("All fields are necessary.");
      return;
    }

    try {
      const resUserExists = await fetch("api/userExists", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ email }),
      });

      const { user } = await resUserExists.json();

      if (user) {
        setError("User already exists.");
        return;
      }

      const res = await fetch("api/register", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          name,
          email,
          password,
        }),
      });

      if (res.ok) {
        const form = e.target;
        form.reset();
        router.push("/");
      } else {
        console.log("User registration failed.");
      }
    } catch (error) {
      console.log("Error during registration: ", error);
    }
  };

  return (
    <div className="grid place-items-center h-screen">
      <div className="shadow-lg p-5 rounded-lg border-t-4 border-green-400 lg:w-5/12">
        <h1 className="text-xl font-bold my-4">Register</h1>

        <form onSubmit={handleSubmit} className="flex flex-col gap-3">
          <div class="flex items-center text-lg mb-6 md:mb-8">
            <svg xmlns="http://www.w3.org/2000/svg" class="absolute ml-3" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle>
            </svg>
            <input
              onChange={(e) => setName(e.target.value)}
              type="text"
              className="bg-gray-200 rounded pl-12 py-2 md:py-4 focus:outline-none w-full text-black"
              placeholder="Full Name"
            />
          </div>
          <div class="flex items-center text-lg mb-6 md:mb-8">
            <svg xmlns="http://www.w3.org/2000/svg" class="absolute ml-3" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline>
            </svg>
            <input
              onChange={(e) => setEmail(e.target.value)}
              type="text"
              className="bg-gray-200 rounded pl-12 py-2 md:py-4 focus:outline-none w-full text-black"
              placeholder="Email"
            />
          </div>
          <div class="flex items-center text-lg mb-6 md:mb-8">
            <svg xmlns="http://www.w3.org/2000/svg" class="absolute ml-3" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
              <path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
            </svg>
            <input
              onChange={(e) => setPassword(e.target.value)}
              type="password"
              class="bg-gray-200 rounded pl-12 py-2 md:py-4 focus:outline-none w-full text-black"
              placeholder="Password"
            />
          </div>

          <button className="bg-green-600 text-white p-2 md:p-4 font-bold cursor-pointer px-6 py-2 rounded">
            Register
          </button>

          {error && (
            <div className="bg-red-500 text-white w-fit text-sm py-1 px-3 rounded-md mt-2 w-full p-2 md:p-4">
              {error}
            </div>
          )}

          <Link className="text-sm mt-3 text-right" href={"/login"}>
            Already have an account? <span className="underline">Login</span>
          </Link>
        </form>
      </div>
    </div>
  );
}
components\UserInfo.jsx
//components\UserInfo.jsx
"use client";

import { signOut } from "next-auth/react";
import { useSession } from "next-auth/react";

export default function UserInfo() {
  const { data: session } = useSession();

  return (
    <div className="p-5">
        <div>
          Name: <span className="font-bold">{session?.user?.name}</span>
        </div>
        <div>
          Email: <span className="font-bold">{session?.user?.email}</span>
        </div>
        <button
          onClick={() => signOut()}
          className="bg-red-500 text-white font-bold px-6 py-2 mt-3"
        >
          Log Out
        </button>
    </div>
  );
}
lib\mongodb.js
//lib\mongodb.js
import mongoose from "mongoose";

export const connectMongoDB = async () => {
  try {
    await mongoose.connect(process.env.MONGODB_URI);
    console.log("Connected to MongoDB");
  } catch (error) {
    console.log("Error connecting to MongoDB: ", error);
  }
};
models\user.js
//models\user.js
import mongoose, { Schema, models } from "mongoose";

const userSchema = new Schema(
  {
    name: {
      type: String,
      required: true,
    },
    email: {
      type: String,
      required: true,
    },
    password: {
      type: String,
      required: true,
    },
  },
  { timestamps: true }
);

const User = models.User || mongoose.model("User", userSchema);
export default User;
middleware.js
//middleware.js
export { default } from "next-auth/middleware";

export const config = { matcher: ["/dashboard"] };
.env
//.env
MONGODB_URI=mongodb://127.0.0.1/nextjs14
NEXTAUTH_SECRET=cairocoders
run C:\nextjs>npm run dev

Related Post