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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | //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}`) }) |
1 2 3 4 5 6 7 8 9 10 11 12 | //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; |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //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>, ); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | //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; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | //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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | //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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | //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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | //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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | //src\api\users.jsx export async function fetchUsers() { 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() } |
C:\react-js\my-app> npm run dev