article

Showing posts with label NodeJS-ExpressJS. Show all posts
Showing posts with label NodeJS-ExpressJS. Show all posts

Friday, February 7, 2025

Nextjs 15 CRUD (Create, Read, Update and Delete) with Pagination Tanstack Query | Node Express MongoDB Atlas

Nextjs 15 CRUD (Create, Read, Update and Delete) with Pagination Tanstack Query | Node Express MongoDB Atlas

expressjs_com/
Express JS
Fast, unopinionated, minimalist web framework for Node.js

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

mongoose
mongoosejs/docs/
npm install mongoose --save

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

run PS C:\nodeproject> node index.js
index.js
//index.js
const express = require('express')
const mongoose = require('mongoose')
const cors = require('cors')
const UserModel = require('./User')
   
const app = express()
const port = 3001
  
app.use(cors())
app.use(express.json())
   
main().catch(err => console.log(err));
  
async function main() {
    try {                     //"mongodb+srv://"username":"password"@cluster0.x45tgvn.mongodb.net/"databasename"?retryWrites=true&w=majority&appName=Cluster0"
        await mongoose.connect('mongodb+srv://cairocoders:123456@cluster0.x45tgvn.mongodb.net/expressdb?retryWrites=true&w=majority&appName=Cluster0', {});
        console.log("CONNECTED TO DATABASE SUCCESSFULLY");
    } catch (error) {
        console.error('COULD NOT CONNECT TO DATABASE:', error.message);
    }
}

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

//Pagination
app.get("/pagination", async (req, res) => {
  let { page, limit } = req.query;
  if (!page) page = 1;
  if (!limit) limit = 2;

  const skip = (page - 1) * 2;
  const users = await UserModel.find().sort({ "_id":-1})
    .skip(skip)
    .limit(limit);

  const total = await UserModel.find().countDocuments()  
  res.header('Access-Control-Expose-Headers', 'X-Total-Count')
  res.header('X-Total-Count', total)
  res.send({ page: page, limit: limit, users: users });
});

app.get('/users', (req, res) => {
    UserModel.find()
    .then(users => res.json(users))
    .catch(err => res.json(err))
})

app.post('/users/create', (req, res) => {
    UserModel.create(req.body)
    .then(user => res.json(user))
    .catch(err => res.json(err))
})

app.get('/users/get/:id', (req, res) => {
    const id = req.params.id
    UserModel.findById({_id: id})
    .then(post => res.json(post))
    .catch(err => console.log(err))
})

app.put('/users/update/:id', (req, res) => {
    const id = req.params.id;
    UserModel.findByIdAndUpdate({_id: id}, {
        name: req.body.name,
        email: req.body.email,
        age: req.body.age
    }).then(user => res.json(user))
    .catch(err => res.json(err))
})

app.delete('/users/delete/:id', (req, res) => {
    const id = req.params.id;
    UserModel.findByIdAndDelete({_id: id})
    .then(response => res.json(response))
    .catch(err => res.json(err))
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})
User.js
//User.js
const mongoose = require('mongoose')
   
const UserSchema = new mongoose.Schema({
    name: String,
    email: String,
    age: Number
})
   
const UserModel = mongoose.model("users", UserSchema)
   
module.exports = UserModel;
Install nextjs npx create-next-app@latest nextjs_org/docs/getting-started/installation

Install react-query

npm i @tanstack/react-query
tanstack_com/query/latest/docs/framework/react/installation
app\page.tsx
//app\page.tsx
"use client";

import { useQuery } from "@tanstack/react-query"; //npm i @tanstack/react-query tanstack_com/query/latest/docs/framework/react/installation
import UserList from "./components/UserList";
import { getUserList } from "./api/users";
import AddUser from './components/AddUser'
import LoadingSpinner from "./components/LoadingSpinner";
import Paginationnumber from "./components/Paginationnumber";
import { useSearchParams } from 'next/navigation'

export default function Home() {

    const searchParams = useSearchParams()
    const page = searchParams.get('page')

    //console.log(page);
    //const page = 1 
    const currentPage = Number(page) || 1;

    const { isLoading, data, isError, isFetching,  error } = useQuery({
        queryKey: ["users", currentPage],
        queryFn: () => getUserList(currentPage)
    });
    //console.log(data);
    if (isLoading) return "Loading...";
    if (isError) return `Error: ${error.message}`;

    const ITEMS_PER_PAGE = 3;
    const totalPages = Math.ceil(Number(data.totalData) / ITEMS_PER_PAGE);
  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 pl-10 pr-5">
            <h1 className="text-4xl font-bold">Nextjs 15 CRUD (Create, Read, Update and Delete) with Pagination Tanstack Query | Node Express MongoDB Atlas</h1>
        </div> 
        <div className="overflow-x-auto py-10">
            <AddUser/>
            <UserList userlist={data.userlist} />
            <div className="flex items-center justify-between my-5">
                <Paginationnumber totalPages={totalPages} />
                {isFetching ? <LoadingSpinner /> : null}
            </div>
        </div>
    </div>
  );
}
app\layout.tsx
//app\layout.tsx
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import QueryProvider from "./components/QueryProvider";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

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

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        <QueryProvider>{children}</QueryProvider>
      </body>
    </html>
  );
}
app\components\QueryProvider.tsx
//app\components\QueryProvider.tsx
"use client";

import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
import { useState } from "react";

interface Props {
  children: React.ReactNode;
}

export default function QueryProvider({ children }: Props) {
  const [queryClient] = useState(() => new QueryClient());
  return (
    <QueryClientProvider client={queryClient}> {children} </QueryClientProvider>
  );
}
app\components\AddUser.tsx
//app\components\AddUser.tsx
import { useMutation, useQueryClient } from "@tanstack/react-query"
import UserForm from "./UserForm"
import { v4 as uuidv4 } from 'uuid'; //npm i uuid 
import { createUser } from "../api/users"

const AddUser = () => {
  const queryClient = useQueryClient();

  const createUserMutation = useMutation({
    mutationFn: createUser,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users']});
      console.log("success!")
    }
  });

  const handleAddPost = (user) => {
    createUserMutation.mutate({
      id: uuidv4(),
      ...user
    })
  }
  
  return (
    <div className="max-w-md mx-auto mt-5 mb-5">
      <h1 className="text-2xl text-center mb-2">Add New User</h1>
      <UserForm onSubmit={handleAddPost} initialValue={{}} />
    </div>
  )
}
 
export default AddUser
app\components\UserForm.tsx
//app\components\UserForm.tsx
import { useState } from "react"
 
const UserForm = ({ onSubmit, initialValue }) => {
  const [user, setUser] = useState({
    name: initialValue.name || "",
    email: initialValue.email || "",
    age: initialValue.age || "" 
  });
 
  const handleChangeInput = (e) => {
    setUser({
      ...user,
      [e.target.name]: e.target.value
    })
  }
 
  const renderField = (label) => (
    <div>
      <label className="block text-sm font-medium text-gray-900">{label}</label>
      <input onChange={handleChangeInput} type="text" name={label.toLowerCase()} value={user[label.toLowerCase()]} 
        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"
      />
    </div>
  );
 
  const handleSubmit = (e) => {
    e.preventDefault();
    onSubmit(user);
    setUser({
      name: "",
      email: "",
      age: ""
    })
 
  }
 
  return (
    <form onSubmit={handleSubmit}>
      <div className="mb-5">{renderField('Name')}</div>
      <div className="mb-5">{renderField('Email')}</div>
      <div className="mb-5">{renderField('Age')}</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"
      >
        Submit</button>
    </form>
  )
}
 
export default UserForm
app\components\UserList.tsx
//app\components\UserList.tsx
import Link from "next/link";
import { useMutation, useQueryClient } from "@tanstack/react-query"; //npm i @tanstack/react-query 
import { deleteUser } from "../api/users";

const UserList = ({ userlist }) => {
    const queryClient = useQueryClient();

    const deleteUserMutation = useMutation({
      mutationFn: deleteUser,
      onSuccess: () => {
        queryClient.invalidateQueries({ queryKey: ['users']});
      }
    });

    const handleDelete = (id) => {
      deleteUserMutation.mutate(id)
    }

  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">Age</th>
                    <th className="py-3 px-6 text-center">Actions</th>
                </tr>
            </thead>
            <tbody>
                {userlist.map((user) => (
                    <tr key={user._id} className="bg-white border-b text-black">
                        <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.age}</td>
                        <td className="flex justify-center gap-1 py-3">
                            <Link 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"
                                href={`/user/${user._id}`}>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"
                                href={`/user/edit/${user._id}/`}>
                                Edit
                            </Link>
                            <button onClick={() => handleDelete(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>
  )
}
 
export default UserList
app\components\LoadingSpinner.tsx
//app\components\LoadingSpinner.tsx
export default function LoadingSpinner() {
    return (
        <h1>Loading...</h1>
    )
}
app\components\Paginationnumber.tsx
//app\components\Paginationnumber.tsx
"use client";
 
import Link from "next/link";
import { HiChevronLeft, HiChevronRight } from "react-icons/hi"; //npm install react-icons --save npmjs_com/package/react-icons
import { usePathname, useSearchParams } from "next/navigation";
import clsx from "clsx"; //npm i clsx npmjs_com/package/clsx
 
export const generatePagination = (currentPage: number, totalPages: number) => {
  if (totalPages <= 7) {
    return Array.from({ length: totalPages }, (_, i) => i + 1);
  }
 
  if (currentPage <= 3) {
    return [1, 2, 3, "...", totalPages - 1, totalPages];
  }
 
  if (currentPage >= totalPages - 2) {
    return [1, 2, 3, "...", totalPages - 2, totalPages - 1, totalPages];
  }
 
  return [
    1,
    "...",
    currentPage - 1,
    currentPage,
    currentPage + 1,
    "...",
    totalPages,
  ];
};

const Paginationnumber = ({ totalPages }: { totalPages: number }) => {
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const currentPage = Number(searchParams.get("page")) || 1;
 
  const createPageURL = (pageNumber: string | number) => {
    const params = new URLSearchParams(searchParams);
    params.set("page", pageNumber.toString());
    return `${pathname}?${params.toString()}`;
  };
 
  const allPages = generatePagination(currentPage, totalPages);
 
  const PaginationNumber = ({
    page,
    href,
    position,
    isActive,
  }: {
    page: number | string;
    href: string;
    position?: "first" | "last" | "middle" | "single";
    isActive: boolean;
  }) => {
    const className = clsx(
      "flex h-10 w-10 items-center justify-center text-sm border",
      {
        "rounded-l-sm": position === "first" || position === "single",
        "rounded-r-sm": position === "last" || position === "single",
        "z-10 bg-blue-100 border-blue-500 text-white bg-blue-700": isActive,
        "hover:bg-blue-700": !isActive && position !== "middle",
        "text-gray-300 pointer-events-none": position === "middle",
      }
    );
 
    return isActive && position === "middle" ? (
      <div className={className}>{page}</div>
    ) : (
      <Link href={href} className={className}>
        {page}
      </Link>
    );
  };
   
  const PaginationArrow = ({
    href,
    direction,
    isDisabled,
  }: {
    href: string;
    direction: "left" | "right";
    isDisabled?: boolean;
  }) => {
    const className = clsx(
      "flex h-10 w-10 items-center justify-center text-sm border",
      {
        "pointer-events-none text-gray-300": isDisabled,
        "hover:bg-gray-100": !isDisabled,
        "mr-2": direction === "left",
        "ml-2": direction === "right",
      }
    );
 
    const icon =
      direction === "left" ? (
        <HiChevronLeft size={20} />
      ) : (
        <HiChevronRight size={20} />
      );
 
    return isDisabled ? (
      <div className={className}>{icon}</div>
    ) : (
      <Link href={href} className={className}>
        {icon}
      </Link>
    );
  };
 
  return (
    <div className="inline-flex">
      <PaginationArrow
        direction="left"
        href={createPageURL(currentPage - 1)}
        isDisabled={currentPage <= 1}
      />
      <div className="flex -space-x-px">
        {allPages.map((page, index) => {
          let position: "first" | "last" | "single" | "middle" | undefined;
 
          if (index === 0) position = "first";
          if (index === allPages.length - 1) position = "last";
          if (allPages.length === 1) position = "single";
          if (page === "...") position = "middle";
 
          return (
            <PaginationNumber
              key={index}
              href={createPageURL(page)}
              page={page}
              position={position}
              isActive={currentPage === page} 
            />
          );
        })}
      </div>
      <PaginationArrow
        direction="right"
        href={createPageURL(currentPage + 1)}
        isDisabled={currentPage >= totalPages}
      />
    </div>
  );
};
 
export default Paginationnumber;
app\user\[id]\page.tsx
//app\user\[id]\page.tsx
"use client";

import { useQuery } from "@tanstack/react-query"; //npm i @tanstack/react-query
import { useParams } from 'next/navigation'
import { fetchUser } from "../../api/users";
 
const User = () => {

  const {id}=useParams();

  const {
    isLoading,
    isError,
    data: user,
    error,
  } = useQuery({
    queryKey: ["users", id],
    queryFn: () => fetchUser(id),
  });
 
  if (isLoading) return "loading...";
  if (isError) return `Error: ${error.message}`;
 
 
  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">{user.name}</h1>
        </div> 
        <div className="overflow-x-auto py-10">
          <h2>{user.email}</h2>
          <h3>{user.age}</h3>
        </div>
    </div>
  )
}
 
export default User
app\user\edit\[id]\page.tsx
//app\user\edit\[id]\page.tsx
"use client";

import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useParams, useRouter } from 'next/navigation'
import { fetchUser, updateUser } from "../../../api/users";
import UserForm from "../../../components/UserForm"

const EditUser = () => {
  const {id}=useParams();
  //console.log(id);

  const queryClient = useQueryClient();
  const router = useRouter();

  const {
    isLoading,
    isError,
    data: user,
    error,
  } = useQuery({
    queryKey: ["users", id],
    queryFn: () => fetchUser(id),
  });

  const updateUserMutation = useMutation({
    mutationFn: updateUser,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users']});
      router.push('/')
    }
  })

  if (isLoading) return "loading...";
  if (isError) return `Error: ${error.message}`;

  const handleSubmit = (updatedUser) => {
    updateUserMutation.mutate({id, ...updatedUser})
  }

  return (
    <div className="max-w-md mx-auto mt-5">
        <div className="flex items-center justify-between gap-1 mb-5">
            <h1 className="text-4xl font-bold">{user.name}</h1>
        </div> 
        <UserForm onSubmit={handleSubmit} initialValue={user} />
    </div>
  )
}
 
export default EditUser
app\api\page.tsx
//app\api\page.tsx
const ITEM_PER_PAGE = 3;
export async function getUserList(page) {
  const response = await fetch(`http://localhost:3001/pagination?page=${page}&limit=${ITEM_PER_PAGE}`);
  const totalData = response.headers.get('X-Total-Count');
  const data = await response.json();
  //console.log(totalData);
  return {
    userlist: data.users,
    totalData
  }
}

export async function fetchUsers() {
  const response = await fetch('http://localhost:3001/users');
  return response.json()
}

export async function createUser(newUser) {
  const response = await fetch(`http://localhost:3001/users/create`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify(newUser)
  });
  return response.json()
}

export async function fetchUser(id) {
  const response = await fetch(`http://localhost:3001/users/get/${id}`);
  return response.json()
}

export async function updateUser(updatedUser) {
  const response = await fetch(`http://localhost:3001/users/update/${updatedUser.id}`, {
    method: "PUT",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify(updatedUser)
  });
  return response.json()
}

export async function deleteUser(id) {
  const response = await fetch(`http://localhost:3001/users/delete/${id}`, {
    method: "DELETE",
  });
  return response.json()
}
run C:\nextjs>npm run dev

Thursday, January 16, 2025

ReactJS 18 Node Express CRUD | React Query | MongoDB Atlas | TailwindCSS (Create Read Update and Delete)

ReactJS 18 Node Express CRUD | React Query | MongoDB Atlas | TailwindCSS (Create Read Update and Delete)
https://expressjs_com/
Express JS
Fast, unopinionated, minimalist web framework for Node.js

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

mongoose
mongoosejs/docs/
npm install mongoose --save

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

run PS C:\nodeproject> node index.js
index.js
//index.js
const express = require('express')
const mongoose = require('mongoose')
const cors = require('cors')
const UserModel = require('./User')
  
const app = express()
const port = 3001
 
app.use(cors())
app.use(express.json())
  
main().catch(err => console.log(err));
 
async function main() {
    try {                     //"mongodb+srv://"username":"password"@cluster0.x45tgvn.mongodb.net/"databasename"?retryWrites=true&w=majority&appName=Cluster0"
        await mongoose.connect('mongodb+srv://cairocoders:123456@cluster0.x45tgvn.mongodb.net/expressdb?retryWrites=true&w=majority&appName=Cluster0', {});
        console.log("CONNECTED TO DATABASE SUCCESSFULLY");
    } catch (error) {
        console.error('COULD NOT CONNECT TO DATABASE:', error.message);
    }
}
 
app.get('/hello', (req, res) => {
  res.send('Hello World!')
})
 
app.get('/users', (req, res) => {
    UserModel.find()
    .then(users => res.json(users))
    .catch(err => res.json(err))
})
  
  
app.get('/users/get/:id', (req, res) => {
    const id = req.params.id
    UserModel.findById({_id: id})
    .then(post => res.json(post))
    .catch(err => console.log(err))
})
  
app.post('/users/create', (req, res) => {
    UserModel.create(req.body)
    .then(user => res.json(user))
    .catch(err => res.json(err))
})
  
app.put('/users/update/:id', (req, res) => {
    const id = req.params.id;
    UserModel.findByIdAndUpdate({_id: id}, {
        name: req.body.name,
        email: req.body.email,
        age: req.body.age
    }).then(user => res.json(user))
    .catch(err => res.json(err))
})
  
app.delete('/users/delete/:id', (req, res) => {
    const id = req.params.id;
    UserModel.findByIdAndDelete({_id: id})
    .then(response => res.json(response))
    .catch(err => res.json(err))
})
  
app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})
User.js
//User.js
const mongoose = require('mongoose')
  
const UserSchema = new mongoose.Schema({
    name: String,
    email: String,
    age: Number
})
  
const UserModel = mongoose.model("users", UserSchema)
  
module.exports = UserModel;
react.dev
Create react project
Run the following command in your terminal: vite_dev/guide/
npm create vite@latest
tanstack/react-query
Hooks for fetching, caching and updating asynchronous data in React, Solid, Svelte and Vue

npm i @tanstack/react-query

react-query-devtools
npm i @tanstack/react-query-devtools

Tailwind CSS with Vite
tailwindcss_com/docs/guides/vite

Install npm i uuid

C:\react-js\my-app\src\App.jsx
//src\App.jsx
import { Route, Routes } from "react-router-dom" 
import UserLists from "./pages/UserLists"
import User from "./pages/User"
import EditUser from "./pages/EditUser"

function App() { tailwindcss_com/docs/guides/vite
  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) | React Query MongoDB Atlas</h1>
        </div> 
        <Routes>
          <Route path="/" element={<UserLists />} />
          <Route path="/user/:id" element={<User />} />
          <Route path="/user/:id/edit" element={<EditUser />} />
        </Routes>
    </div>
  )
}

export default App
C:\react-js\my-app\src\main.jsx
//src\main.jsx
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from '@tanstack/react-query-devtools' //npm i @tanstack/react-query-devtools
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import { BrowserRouter } from "react-router-dom";
import App from './App.jsx'

// create a client
const queryClient = new QueryClient();

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <BrowserRouter>
      <QueryClientProvider client={queryClient}>
        <App />
        <ReactQueryDevtools initialIsOpen={false} />
      </QueryClientProvider>
    </BrowserRouter>
  </StrictMode>,
);
C:\react-js\my-app\src\pages\UserLists.jsx
//src\pages\UserLists.jsx
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; //npm i @tanstack/react-query 
import { useNavigate } from "react-router-dom";
import { deleteUser, fetchUsers } from "../api/users";
import AddUser from "../components/AddUser";

const UserLists = () => {
    const navigate = useNavigate();
    const queryClient = useQueryClient();

    const {
      isLoading,
      isError,
      data: users,  
      error,
    } = useQuery({
      queryKey: ["users"],
      queryFn: fetchUsers,
    });

    //console.log(users);

    const deleteUserMutation = useMutation({
      mutationFn: deleteUser,
      onSuccess: () => {
        queryClient.invalidateQueries({ queryKey: ['users']});
      }
    });

    const handleDelete = (id) => {
      deleteUserMutation.mutate(id)
    }

    if (isLoading) return "loading...";
    if (isError) return `Error: ${error.message}`;

  return (
    <div className="overflow-x-auto py-10">
        <AddUser />
        <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">Age</th>
                    <th className="py-3 px-6 text-center">Actions</th>
                </tr>
            </thead>
            <tbody>
                {users.map((user) => (
                    <tr key={user._id} 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.age}</td>
                        <td className="flex justify-center gap-1 py-3">
                            <button
                                onClick={() => navigate(`/user/${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
                            </button>
                            <button 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"
                                onClick={() => navigate(`/user/${user._id}/edit`)}
                            >
                                Edit
                            </button>
                            <button onClick={() => handleDelete(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>
    </div>
  );
};

export default UserLists;
C:\react-js\my-app\src\pages\User.jsx
//src\pages\User.jsx
import { useQuery } from "@tanstack/react-query"; //npm i @tanstack/react-query
import { useNavigate, useParams } from "react-router-dom";
import { fetchUser } from "../api/users";

const User = () => {
  const navigate = useNavigate();
  const { id } = useParams();
  const {
    isLoading,
    isError,
    data: user,
    error,
  } = useQuery({
    queryKey: ["users", id],
    queryFn: () => fetchUser(id),
  });

  if (isLoading) return "loading...";
  if (isError) return `Error: ${error.message}`;


  return (
    <div className="overflow-x-auto py-10">
      <button onClick={() => navigate("/")}>back to list users</button>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
      <p>{user.age}</p>
    </div>
  )
}

export default User
C:\react-js\my-app\src\pages\User.jsx
//src\pages\User.jsx
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useNavigate, useParams } from "react-router-dom";
import { fetchUser, updateUser } from "../api/users";
import UserForm from "../components/UserForm"

const EditUser = () => {
  const queryClient = useQueryClient();
  const navigate = useNavigate();
  const { id } = useParams();
  const {
    isLoading,
    isError,
    data: user,
    error,
  } = useQuery({
    queryKey: ["users", id],
    queryFn: () => fetchUser(id),
  });
  const updateUserMutation = useMutation({
    mutationFn: updateUser,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users']});
      navigate("/")
    }
  })

  if (isLoading) return "loading...";
  if (isError) return `Error: ${error.message}`;

  const handleSubmit = (updatedUser) => {
    updateUserMutation.mutate({id, ...updatedUser})
  }


  return (
    <div className="overflow-x-auto py-10">
      <UserForm onSubmit={handleSubmit} initialValue={user} />
    </div>
  )
}

export default EditUser
C:\react-js\my-app\src\components\AddUser.jsx
//src\components\AddUser.jsx
import { useMutation, useQueryClient } from "@tanstack/react-query"
import { createUser } from "../api/users"
import UserForm from "./UserForm"
import { v4 as uuidv4 } from 'uuid'; //npm i uuid 

const AddUser = () => {
  const queryClient = useQueryClient();

  const createUserMutation = useMutation({
    mutationFn: createUser,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users']});
      console.log("success!")
    }
  });

  const handleAddPost = (user) => {
    createUserMutation.mutate({
      id: uuidv4(),
      ...user
    })
  }

  return (
    <div className="max-w-md mx-auto mt-5">
      <h1 className="text-2xl text-center mb-2">Add New User</h1>
      <UserForm onSubmit={handleAddPost} initialValue={{}} />
    </div>
  )
}

export default AddUser
C:\react-js\my-app\src\components\UserForm.jsx
//src\components\UserForm.jsx
import { useState } from "react"

const UserForm = ({ onSubmit, initialValue }) => {
  const [user, setUser] = useState({
    name: initialValue.name || "",
    email: initialValue.email || "",
    age: initialValue.age || ""
  });

  const handleChangeInput = (e) => {
    setUser({
      ...user,
      [e.target.name]: e.target.value
    })
  }

  const renderField = (label) => (
    <div>
      <label className="block text-sm font-medium text-gray-900">{label}</label>
      <input onChange={handleChangeInput} type="text" name={label.toLowerCase()} value={user[label.toLowerCase()]} 
        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"
      />
    </div>
  );

  const handleSubmit = (e) => {
    e.preventDefault();
    onSubmit(user);
    setUser({
      name: "",
      email: "",
      age: ""
    })

  }

  return (
    <form onSubmit={handleSubmit}>
      <div className="mb-5">{renderField('Name')}</div>
      <div className="mb-5">{renderField('Email')}</div>
      <div className="mb-5">{renderField('Age')}</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"
      >
        Submit</button>
    </form>
  )
}

export default UserForm
C:\react-js\my-app\src\api\users.jsx
//src\api\users.jsx
export async function fetchUsers() {
  const response = await fetch('http://localhost:3001/users');
  return response.json()
}

export async function fetchUser(id) {
  const response = await fetch(`http://localhost:3001/users/get/${id}`);
  return response.json()
}

export async function createUser(newUser) {
  const response = await fetch(`http://localhost:3001/users/create`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify(newUser)
  });
  return response.json()
}

export async function updateUser(updatedUser) {
  const response = await fetch(`http://localhost:3001/users/update/${updatedUser.id}`, {
    method: "PUT",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify(updatedUser)
  });
  return response.json()
}

export async function deleteUser(id) {
  const response = await fetch(`http://localhost:3001/users/delete/${id}`, {
    method: "DELETE",
  });
  return response.json()
}
Run
C:\react-js\my-app> 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

Tuesday, April 9, 2024

Next.js 14 Node Express MongoDB Create with Upload File and List all data | TailwindCSS DaisyUI

Next.js 14 Node Express MongoDB Create with Upload File and List all data | TailwindCSS DaisyUI

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

https://mongoosejs.com/docs/
npm install mongoose --save
multer
npm install --save multer
http://expressjs.com/en/resources/middleware/multer.html

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

run PS C:\nodeproject> node index.js

index.js
//index.js
const express = require('express')
const mongoose = require('mongoose')
const cors = require('cors')
const multer = require('multer') //http://expressjs.com/en/resources/middleware/multer.html npm install --save multer
const UserModel = require('./User')

const app = express()
const port = 3000

const path = require('path') //https://expressjs.com/en/starter/static-files.html
app.use('/static', express.static(path.join(__dirname, 'public')))

app.use(cors())
app.use(express.json())
 
main().catch(err => console.log(err));

async function main() {
    try {
        await mongoose.connect('mongodb://127.0.0.1/nodeexpressdb', {});
        console.log("CONNECTED TO DATABASE SUCCESSFULLY");
    } catch (error) {
        console.error('COULD NOT CONNECT TO DATABASE:', error.message);
    }
}

const storage = multer.diskStorage({
  destination: function(req, file, cb) {
    return cb(null, "./public/images")
  },
  filename: function (req, file, cb) {
    return cb(null, `${Date.now()}_${file.originalname}`)
  }
})
 
const upload = multer({storage})

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

app.post('/upload', upload.single('file'), (req, res) => {
  console.log(req.body)
  console.log(req.file)
  return res.json({Status: "Success"});
})

app.post('/create',upload.single('file'), (req, res) => {
    UserModel.create({
        name: req.body.name,
        email: req.body.email,
        photo: req.file.filename
    }).then(user => res.json(user))
    .catch(err => res.json(err))
})

app.get('/', (req, res) => {
    UserModel.find()
    .then(users => res.json(users))
    .catch(err => res.json(err))
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})
User.js
//User.js
const mongoose = require('mongoose')
 
const UserSchema = new mongoose.Schema({
    name: String,
    email: String,
    photo: String
})
 
const UserModel = mongoose.model("users", UserSchema)
 
module.exports = UserModel;
Next.js
Install requirements
npm install axios
https://www.npmjs.com/package/axios

app\page.tsx
//app\page.tsx
import Link from "next/link";
import TableData from "@/components/tabledata";
import { Suspense } from "react";
import { Spinner } from "@/components/spinner";
 
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 MongoDB Create with Upload File and List all data  | TailwindCSS DaisyUI</h1>
      </div>    
        <div className="overflow-x-auto">
          <div className="mb-2 w-full text-right">
            <Link
              href="/user/create"
              className="btn btn-primary">
              Create
            </Link>
          </div>
          <Suspense fallback={<Spinner />}>
            <TableData/>
          </Suspense>
      </div>  
    </div>
  ); 
}
components\tabledata.tsx
//components\tabledata.tsx
"use client";
  
import Image from 'next/image'  
import React, { useEffect, useState } from "react";
import axios from 'axios' //npm install axios https://www.npmjs.com/package/axios
import Link from "next/link";

export default function Users() {
    const [userData, setUSerData] = useState([]);
    useEffect(() => {
        fetchData();
    }, [])
 
    const fetchData = async () => {
        try {
            const result = await axios("http://localhost:3000/");
            console.log(result.data);
            setUSerData(result.data)
        } catch (err) {
            console.log("somthing Wrong");
        }
    }
  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">Photo</th>
            <th className="py-3 px-6 text-center">Actions</th>
            </tr>
        </thead>
        <tbody>
            {userData.map((rs, index) => (
            <tr key={rs.id} className="bg-white border-b">
                <td className="py-3 px-6">{index + 1}</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">
                    <Image
                    //src="http://localhost:3000/static/images/cairocoders-ednalan.png"
                    src={`http://localhost:3000/static/images/${rs.photo}`}
                    width={100}
                    height={100}
                    alt="Photo"
                    />
                </td>
                <td className="flex justify-center gap-1 py-3">
                    <Link
                    href="#" 
                    className="btn btn-info">
                    View
                    </Link>
                    <Link
                    href="#"
                    className="btn btn-primary">
                    Edit
                    </Link>
                    <button className="btn btn-secondary">Delete</button>
                </td>
            </tr>
            ))}
        </tbody>
        </table>
  );
}
components\spinner.tsx
//components\spinner.tsx
export const Spinner = () => {
  return (
    <span className="loading loading-spinner loading-lg"></span>
  );
};
app\user\create\page.tsx
//app\user\create\page.tsx
"use client";

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

const CreateUserPage = () => {
    const [name, setName] = useState("");
    const [email, setEmail] = useState("");
    const [file, setFile] = useState()

    const onSubmitUpload = async (e) => {
        e.preventDefault();
       try {
          const formData = new FormData()
          formData.append("name", name);
          formData.append("email", email);
          formData.append('file', file)
          axios.post('http://localhost:3000/create',formData )
          .then((response) => {
              console.log(response);
              window.location.href = '/';
          })
          .catch(er => console.log(er))
        } catch (err) {
            console.log("Something Wrong");
        }
    }

    return (
    <div className="max-w-md mx-auto mt-5">
        <h1 className="text-2xl text-center mb-2">Add New User</h1>
        <div>
        <form>
        <div className="mb-5">
          <label htmlFor="name" className="block text-sm font-medium text-gray-900">
            Full Name
          </label>
          <input
            type="text"
            name="name"
            id="name"
            className="input input-bordered input-primary w-full max-w-xs"
            placeholder="Full Name..."
            onChange={(e) => setName(e.target.value)}
          />
        </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={(e) => setEmail(e.target.value)}
          />
        </div>
        <div className="mb-5">
          <label className="block text-sm font-medium text-gray-900">
            Upload File
          </label>
          <input type="file" onChange={(e) => setFile(e.target.files[0])} className="file-input file-input-bordered file-input-secondary w-full max-w-xs"/>
        </div>
         <button type="submit" className="btn btn-primary" onClick={e => onClick={e => onSubmitUpload(e)}>Add User</button> 
      </form>
    </div>
    </div>
  );
};
  
export default CreateUserPage;
next.config.mjs
//next.config.mjs
/** @type {import('next').NextConfig} */
//const nextConfig = {};
const nextConfig = {
  reactStrictMode: true,
  images : {
    domains : ['localhost', 'cairocoders.com'] // == Domain name
  }
}
export default nextConfig;
run C:\nextjs>npm run dev
Github - Next.js 14 Node Express MongoDB Create with Upload File and List all data | TailwindCSS DaisyUI

Related Post