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
