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 | //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); } } //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() .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( '/' , (req, res) => { res.send( 'Hello World!' ) }) app.get( '/users' , (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}`) }) |
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 | //src\App.jsx import { Route, Routes } from "react-router-dom" //npm i react-router-dom import Users from "./pages/Users" function App() { 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 Pagination with Tanstack Query | Node Express MongoDB Atlas</h1> </div> <Routes> <Route path= "/" element={<Users />} /> </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 | //src\pages\Users.jsx import { useQuery } from "@tanstack/react-query" ; import { useState } from "react" ; import LoadingSpinner from "../components/LoadingSpinner" ; import Pagination from "../components/Pagination" ; import UserList from "../components/UserList" ; import { getUserList } from "../services/api" ; const Users = () => { const [currentPage, setCurrentPage] = useState(1); const { isLoading, data, isError, isFetching, isPreviousData, error } = useQuery({ queryKey: [ "posts" , currentPage], queryFn: () => getUserList(currentPage), keepPreviousData: true }); if (isLoading) return "Loading..." ; if (isError) return `Error: ${error.message}`; //console.log(data.totalData); return ( <div className= 'max-w-[1100px] mx-auto mt-10 pb-10 px-4' > <h1 className= "text-center text-2xl my-5 underline font-bold" > Pagination </h1> <UserList userlist={data.userlist} /> <div className= "flex items-center justify-between my-5" > <Pagination currentPage={currentPage} totalItems={data.totalData} onPageChange={(page) => setCurrentPage(page)} isPreviousData={isPreviousData} /> {isFetching ? <LoadingSpinner /> : null} </div> </div> ); }; export default Users; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //src\components\UserList.jsx const UserList = ({ userlist }) => { return ( <div className= "grid grid-cols-3 gap-x-3 gap-y-3" > {userlist.map(user => ( <div key={user._id} className= "bg-primary p-10 rounded-lg shadow-md border border-gray-200" > <p>#id : {user._id}</p> <span className= 'block mb-4 font-bold text-lg text-secondary capitalize' >{user.name}</span> <span className= 'text-secondary' >{user.email}</span> </div> ))} </div> ) } export default UserList |
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 | //src\components\Pagination.jsx const ITEM_PER_PAGE = 3; const Pagination = ({ currentPage, totalItems, onPageChange, isPreviousData, }) => { const pageCount = Math. ceil (totalItems / ITEM_PER_PAGE); const handlePrevClick = () => onPageChange(currentPage - 1); const handleNextClick = () => onPageChange(currentPage + 1); return ( <div className= "btn-group" > <button disabled={currentPage === 1 || isPreviousData} onClick={handlePrevClick} className= "btn" > « </button> <button className= "btn" >Page {currentPage}</button> <button disabled={currentPage === pageCount || isPreviousData} onClick={handleNextClick} className= "btn" > » </button> </div> ); }; export default Pagination; |
1 2 3 4 5 6 7 8 9 10 11 12 | //src\components\api.jsx const ITEM_PER_PAGE = 3; export const getUserList = async (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 } } |
C:\react-js\my-app> npm run dev