JSON Payload in RESTful API
» Important Links
» What are we building?
-
In this blog, we will build a RestFul API that takes in JSON as a payload.
-
To look at the demo, please visit the youtube video provided above and look at the Demo part in it.
-
This will be the Blog App which we will build using react for client and express for server.
» Initialise the project
- Let’s initialise the project. So we are going to have two folders in our main folder, named client and server, so let’s do that.
mkdir json-payload-in-restful-api
cd json-payload-in-restful-api
mkdir server
cd server
» Initialise Server
- Okay, now let’s intialise our server. Navigate to the server folder and run the command:
pnpm init
pnpm install body-parser cors express fs jsonfile nodemon
- Create files we need: index.js in server’s root directory and db.json in db folder.
» Initialise Client
- Next let’s work on client side, for it we will use react, tailwind, vite. So open this tailwind site and follow the steps for creating client.
NOTE: And if by any chance you get lost somewhere, remember you have a link for its GitHub repository.
» Create Server
- Let’s now start creating our server. Open the index.js file and paste the below code.
// server/index.js
const express = require("express");
const bodyparser = require("body-parser");
const jsonfile = require("jsonfile");
const db = require("./db/db.json");
const cors = require("cors");
const app = express();
app.use(cors());
const port = process.env.PORT || 3000;
app.use(bodyparser.urlencoded({ extended: true }));
app.use(bodyparser.json());
app.get("/posts", (req, res) => {
res.json(db);
});
app.get("/post-by-id/:id", function (req, res) {
const id = req.params.id;
let post = db.find((post) => post.id == id);
if (!post) {
res.json({ Message: "No Post Found With This ID" });
} else {
res.json(post);
}
});
app.get("/posts-by-title/:title", function (req, res) {
const title = req.params.title;
let post = db.find((post) => post.title == title);
if (!post) {
res.json({ Message: "Not Found Any Post Related to Your Title" });
} else {
res.json(post);
}
});
app.get("/posts-by-tags/:tag", function (req, res) {
const tag = req.params.tag;
let posts = db.filter((post) => post.tags.includes(tag));
if (!posts.length) {
res.json({ Message: `No Posts Found Against This Tag ${tag}` });
} else {
res.json(posts);
}
});
app.get("/posts-by-category/:category", function (req, res) {
const category = req.params.category;
let posts = db.filter((post) => post.category == category);
if (!posts.length) {
res.json({ Message: `No Posts Found In This Category ${category}` });
} else {
res.json(posts);
}
});
app.post("/newpost", (req, res) => {
const maxId = Math.max(...db.map((post) => post.id));
const newPost = {
id: maxId + 1,
title: req.body.title,
content: req.body.content,
category: req.body.category,
tags: req.body.tags.split(","),
};
db.push(newPost);
jsonfile.writeFile("./db/db.json", db, (err) => {
if (err) {
console.error(err);
res.json({ message: "Error writing to database" });
} else {
res.json({
message: `Post added successfully! Your Post Id is ${newPost.id}`,
});
}
});
});
app.put("/postupdate/:id", (req, res) => {
let id = req.params.id;
let post = db.find((post) => post.id == id);
if (!post) {
res.status(404).json({ message: "Not Found Any Post Related to Your ID" });
} else {
post.title = req.body.title;
post.content = req.body.content;
post.category = req.body.category;
post.tags = req.body.tags.split(",");
jsonfile.writeFile("./db/db.json", db, (err) => {
if (err) {
console.error(err);
res.status(500).json({ message: "Error writing to database" });
} else {
res.json({
message: `Post updated successfully! Your Post Id is ${id} `,
});
}
});
}
});
app.delete("/deletepost/:id", (req, res) => {
let id = req.params.id;
let post = db.find((post) => post.id == id);
if (!post) {
res.status(404).json({ message: "Not Found Any Post Related to Your ID" });
} else {
let index = db.indexOf(post);
db.splice(index, 1);
jsonfile.writeFile("./db/db.json", db, (err) => {
if (err) {
console.error(err);
res.status(500).json({ message: "Error writing to database" });
} else {
res.json({
message: `Post deleted successfully! Your Post Id was ${id} `,
});
}
});
}
});
app.listen(port, () => {
console.log(`Server is running on ${port}`);
});
-
There’s a lot going on. Don’t worry, I got you covered.
-
We start off by importing the modules:
- express is a web framework for Node.js.
- body-parser middleware is used to parse incoming request bodies.
- jsonfile is used to read and write JSON files.
- db is our JSON file that acts as a database.
- cors allows cross-origin resource sharing.
- Next, we initialize the Express app and set up middleware:
- app is an instance of Express.
- app.use(cors()) enables CORS.
- port defines the port number, defaulting to 3000 if not specified in environment variables.
- bodyparser middleware helps parse URL-encoded and JSON bodies.
» GET Routes
This route handles GET requests to the /posts
endpoint. So when a client will make a request to this endpoint, it will fetch all posts available in our db.json file ir we can say server.
app.get("/posts", (req, res) => {});
Next, we have a route for retrieving a specific post by its ID. Here, :id
is a route parameter that will capture the ID of the post from the URL. Our server will use this ID to find the corresponding post in the database.
app.get('/post-by-id/:id', function(req, res) {});
Next we will Get the Posts by their Title. Here, we use :title
as a parameter to find a post by its title. If the post is found, it’s returned; otherwise, a message is sent back indicating no post was found.
app.get('/posts-by-title/:title', function(req, res) {});
Next let’s Get Posts by their Tag. In this we use :tag
as parameter and filter the posts that include the said tag in their tags array.
app.get('/posts-by-tags/:tag', function(req, res) {});
Then our last Get route to get Posts by Category
app.get('/posts-by-category/:category', function(req, res) {});
Same as to what we did before, we use :category
as a parameter and filter the posts by the specified category. As before, if no posts are found, a message is returned No Posts Found In This Category ${category}; otherwise, we return the matching posts.
Now that’s all we have for GET routes. Now let’s use POST route, so that with the help of it we can create a new post in our APP.
» POST Route
To do that we will first Create a route for New Post.
app.post("/newpost", (req, res) => {});
So basically This route handles POST requests to create a new post and write it back to database with the new ID.
» PUT Route
This PUT Route will be used to update a POST.
So for updating an existing post, we have this:
app.put("/postupdate/:id", (req, res) => {}):
We use the :id
parameter to find the post to update. If the post is not found, we respond with a 404 status and a message: Not Found Any Post Related to Your ID.
If we do find the post, we update the post’s details with data from the request body. The updated database is then written back to the db.json
file.
» DELETE Route
Last but not the least our DELETE Route to delete the post.
app.delete("/deletepost/:id", (req, res) => {});
This route handles DELETE requests to remove a post. We use:id
as a parameter to find the post to delete. If the post is not found, we respond with a 404 status and a message: Not Found Any Post Related to Your ID.
If we find it, we remove the post from the database array and writes the updated database back to the db.json
file. And a success message is then sent if it was successful, or an error message: Error writing to database if something goes wrong
» Database
Now let’s write some fake data in the db.json file. Just copy and paste this data. If you want fake data, you can visit this typicode json placeholder website(link given above) and get any kind of data you want and then according to it update your routes.
[
{
"id": 1,
"title": "Fake Title 1",
"author": "Fake Author 1",
"content": "This is fake content 1.",
"category": "Fake Category 1",
"tags": [
"Fake Tag 1"
]
},
{
"id": 2,
"title": "Fake Title 2",
"author": "Fake Author 2",
"content": "This is fake content 2.",
"category": "Fake Category 2",
"tags": [
"Fake Tag 2"
]
},
{
"id": 3,
"title": "Fake Title 3",
"author": "Fake Author 3",
"content": "This is fake content 3.",
"category": "Fake Category 3",
"tags": [
"Fake Tag 3"
]
},
{
"id": 4,
"title": "Fake Title 4",
"author": "Fake Author 4",
"content": "This is fake content 4.",
"category": "Fake Category 4",
"tags": [
"Fake Tag 4"
]
},
{
"id": 5,
"title": "Fake Title 5",
"author": "Fake Author 5",
"content": "This is fake content 5.",
"category": "Fake Category 5",
"tags": [
"Fake Tag 5"
]
},
{
"id": 6,
"title": "Fake Title 6",
"author": "Fake Author 6",
"content": "This is fake content 6.",
"category": "Fake Category 6",
"tags": [
"Fake Tag 6"
]
},
{
"id": 7,
"title": "Fake 7",
"content": "Fake 7",
"category": "Fake 7",
"tags": [
"Fake 7"
]
},
{
"id": 8,
"title": "Fake 5",
"content": "Fake 5",
"category": "Fake 5",
"tags": [
"Fake 5"
]
}
]
» Create Frontend
Now that we are done with creating our server you can test them on localhost 3000. You can also test these routes on POSTMAN if you want to.
We now start off by creating all the files that we need for our react app in src directory.
CreatePost.jsx
DeletePost.jsx
UpdatePost.jsx
Navigation.jsx
PostById.jsx
PostByTitle.jsx
Posts.jsx
PostByCategory.jsx
PostByTag.jsx
» Navigation.jsx
Now that’s done, let’s create the main page or the Navigation page for our app that will have all the buttons that we need.
Navigation.jsx provides links to create, retrieve, update, and delete blog posts. It also allows us to search for posts by ID, title, tags, or category. The search parameter is entered in an input field and appended to the URL when a search button is clicked.
import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
function Navigation() {
const [parameter, setParameter] = useState('');
const navigate = useNavigate();
const openURL = (path) => {
navigate(`${path}/${parameter}`);
}
return (
<div className="container mx-auto px-4 mt-5 text-center">
<h3 className="text-2xl font-bold mb-2">Welcome to FEWV's Blog APP</h3>
<p className="mb-4">A web app for testing Blogs...</p>
<input
value={parameter}
onChange={(e) => setParameter(e.target.value)}
name="parameter"
placeholder="Enter Parameter..."
className="mt-1 block w-full py-2 px-3 border border-gray-300 rounded-md"
/>
<div className="btn-section flex space-x-4 mt-4">
<Link to="/postform" className="py-2 px-4 bg-blue-500
text-white text-sm rounded hover:bg-blue-700">Create Post</Link>
<Link to="/posts" className="py-2 px-4 bg-blue-500
text-white text-sm rounded hover:bg-blue-700">Get Posts</Link>
<Link to="/postupdate" className="py-2 px-4 bg-blue-500
text-white text-sm rounded hover:bg-blue-700">Update Post</Link>
<Link to="/deletepost" className="py-2 px-4 bg-blue-500
text-white text-sm rounded hover:bg-blue-700">Delete Post</Link>
</div>
<div className="btn-section flex space-x-4 mt-8 mb-5">
<button onClick={() => openURL('/post-by-id')} className="py-2 px-4 bg-green-500
text-white text-sm rounded hover:bg-green-700">Get Post By ID</button>
<button onClick={() => openURL('/posts-by-title')} className="py-2 px-4 bg-green-500
text-white text-sm rounded hover:bg-green-700">Get Post By Title</button>
<button onClick={() => openURL('/posts-by-tags')} className="py-2 px-4 bg-green-500
text-white text-sm rounded hover:bg-green-700">Get Posts By tags</button>
<button onClick={() => openURL('/posts-by-category')} className="py-2 px-4 bg-green-500
text-white text-sm rounded hover:bg-green-700">Get Posts By Category</button>
</div>
</div>
);
}
export default Navigation;
» APP.jsx
Here, we’re setting up our main application component, App, which serves as the entry point of our React application. We’re using BrowserRouter from react-router-dom to enable routing in our application.
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Navigation from './Navigation';
import CreatePost from './CreatePost';
import UpdatePost from './UpdatePost';
import DeletePost from './DeletePost';
import Posts from './Posts';
import PostByTitle from './PostByTitle';
import PostByCategory from './PostByCategory'
import PostById from './PostById';
import PostByTag from './PostByTag';
function App() {
return (
<Router>
<div className="min-h-screen bg-gray-100 py-6 flex flex-col
justify-center sm:py-12">
<div className="relative py-3 sm:max-w-xl sm:mx-auto">
<div className="relative px-4 py-10 bg-white mx-8 md:mx-0
shadow rounded-3xl sm:p-10">
<Routes>
<Route path="/" element={<Navigation />} />
<Route path="/postform" element={<CreatePost />} />
<Route path="/posts" element={<Posts />} />
<Route path="/postupdate" element={<UpdatePost />} />
<Route path="/deletepost" element={<DeletePost />} />
<Route path="/posts-by-title/:title" element={<PostByTitle />} />
<Route path="/posts-by-category/:category" element={<PostByCategory />} />
<Route path="/post-by-id/:id" element={<PostById />} />
<Route path="/posts-by-tags/:tag" element={<PostByTag />} />
</Routes>
</div>
</div>
</div>
</Router>
);
}
export default App;
» CreatePost.jsx
The CreatePost
component in React allows users to create new blog posts by filling out a form with fields for the title, content, category, and tags. It uses React’s useState
to manage input states and handles form submission by sending a POST request to http://localhost:3000/newpost
with the collected data. Upon submission, it alerts the user with the server’s response or logs an error if the request fails.
import React, { useState } from 'react';
function CreatePost() {
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const [category, setCategory] = useState('');
const [tags, setTags] = useState('');
const createPost = (event) => {
event.preventDefault();
const postData = { title, content, category, tags };
fetch('http://localhost:3000/newpost', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(postData),
})
.then(response => response.json())
.then(data => alert(data.message))
.catch(error => {
console.error('Error:', error);
});
};
return (
<div className="container mx-auto px-4 mt-5">
<h3 className="text-2xl font-bold mb-2">Create A Post</h3>
<p className="mb-4">Create a Nice Post with the fakest content ever...hehe</p>
<form onSubmit={createPost} className="space-y-4">
<div>
<label htmlFor="title" className="block text-sm font-medium
text-gray-700">Write Post Title</label>
<input
type="text"
name="title"
id="title"
placeholder="Enter Post title here..."
value={title}
onChange={(e) => setTitle(e.target.value)}
className="mt-1 block w-full py-2 px-3 border border-gray-300 rounded-md"
/>
</div>
<div>
<label htmlFor="post-content" className="block text-sm font-medium
text-gray-700">Post Content</label>
<textarea
name="content"
id="content"
cols="30"
rows="10"
placeholder="Enter Content Here..."
value={content}
onChange={(e) => setContent(e.target.value)}
className="mt-1 block w-full py-2 px-3 border border-gray-300 rounded-md"
/>
</div>
<div>
<label htmlFor="category" className="block text-sm font-medium
text-gray-700">Category</label>
<input
type="text"
id="category"
name="category"
placeholder="Enter Category"
value={category}
onChange={(e) => setCategory(e.target.value)}
className="mt-1 block w-full py-2 px-3 border border-gray-300 rounded-md"
/>
</div>
<div>
<label htmlFor="tags" className="block text-sm font-medium
text-gray-700">Tags</label>
<input
type="text"
id="tags"
name="tags"
placeholder="Enter Tags Here"
value={tags}
onChange={(e) => setTags(e.target.value)}
className="mt-1 block w-full py-2 px-3 border border-gray-300 rounded-md"
/>
</div>
<button className="w-full py-2 px-4 border border-transparent rounded-md
shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700
focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
type="submit">Post Now</button>
</form>
</div>
);
}
export default CreatePost;
» Post.jsx
The Posts
component fetches a list of blog posts from http://localhost:3000/posts
and displays them. It uses the useEffect
hook to perform the fetch operation when the component mounts, storing the retrieved posts in the posts
state with useState
. The posts are then displayed in a JSON format inside a <pre>
tag.
import React, { useEffect, useState } from 'react';
function Posts() {
const [posts, setPosts] = useState([]);
useEffect(() => {
fetch('http://localhost:3000/posts')
.then(response => response.json())
.then(data => setPosts(data));
}, []);
return (
<div>
<pre>{JSON.stringify(posts, null, 2)}</pre>
</div>
);
}
export default Posts;
» UpdatePost.jsx
The UpdatePost
component allows users to update an existing blog post by entering the post ID along with the new title, content, category, and tags. It uses useState
to manage the input fields' states and sends a PUT request to http://localhost:3000/postupdate/${id}
with the updated post data upon form submission. The component displays a success message if the update is successful, otherwise it shows an error message.
import React, { useState } from 'react';
function UpdatePost() {
const [id, setId] = useState('');
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const [category, setCategory] = useState('');
const [tags, setTags] = useState('');
const updatePost = (event) => {
event.preventDefault();
const postData = { id, title, content, category, tags };
fetch(`http://localhost:3000/postupdate/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(postData),
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert(`Post with id ${id} updated successfully`);
} else {
alert(data.message);
}
})
}
return (
<div className="container mx-auto px-4 mt-5">
<h3 className="text-2xl font-bold mb-2">Update Post</h3>
<p className="mb-4">You can update your post with your post ID.</p>
<form onSubmit={updatePost} className="space-y-4">
<div>
<label htmlFor="ID" className="block text-sm font-medium
text-gray-700">ID</label>
<input
type="number"
name="id"
id="id"
placeholder="Enter Post ID here..."
value={id}
onChange={(e) => setId(e.target.value)}
className="mt-1 block w-full py-2 px-3 border border-gray-300 rounded-md"
/>
</div>
<div>
<label htmlFor="title" className="block text-sm font-medium
text-gray-700">Write Post Title</label>
<input
type="text"
name="title"
id="title"
placeholder="Enter Post title here..."
value={title}
onChange={(e) => setTitle(e.target.value)}
className="mt-1 block w-full py-2 px-3 border border-gray-300 rounded-md"
/>
</div>
<div>
<label htmlFor="post-content"
className="block text-sm font-medium text-gray-700">
Post Content</label>
<textarea
name="content"
id="content"
cols="30"
rows="10"
placeholder="Enter Content Here..."
value={content}
onChange={(e) => setContent(e.target.value)}
className="mt-1 block w-full py-2 px-3 border border-gray-300 rounded-md"
/>
</div>
<div>
<label htmlFor="category" className="block text-sm font-medium
text-gray-700">Category</label>
<input
type="text"
id="category"
name="category"
placeholder="Enter Category"
value={category}
onChange={(e) => setCategory(e.target.value)}
className="mt-1 block w-full py-2 px-3 border border-gray-300 rounded-md"
/>
</div>
<div>
<label htmlFor="tags" className="block text-sm font-medium
text-gray-700">Tags</label>
<input
type="text"
id="tags"
name="tags"
placeholder="Enter Tags Here"
value={tags}
onChange={(e) => setTags(e.target.value)}
className="mt-1 block w-full py-2 px-3 border border-gray-300 rounded-md"
/>
</div>
<button className="w-full py-2 px-4 border border-transparent rounded-md
shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700
focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
type="submit">Update Now</button>
</form>
</div>
);
}
export default UpdatePost;
» DeletePost.jsx
The DeletePost
component allows users to delete a specific blog post by providing its ID. It consists of a form where users can input the ID of the post they want to delete. Upon form submission, a DELETE request is sent to http://localhost:3000/deletepost/${id}
. If the deletion is successful, a message is displayed; otherwise, an error is logged.
import React, { useState } from 'react';
function DeletePost() {
const [id, setId] = useState('');
const deletePost = () => {
fetch(`http://localhost:3000/deletepost/${id}`, {
method: 'DELETE',
})
.then(response => response.json())
.then(data => alert(data.message))
.catch((error) => {
console.error('Error:', error);
});
}
return (
<div className="container mx-auto px-4 mt-5">
<h3 className="text-2xl font-bold mb-2">Delete Post</h3>
<p className="mb-4">Enter the ID of the post you want to delete.</p>
<form onSubmit={(e) => {e.preventDefault(); deletePost();}}
className="space-y-4">
<div>
<label htmlFor="ID" className="block text-sm font-medium
text-gray-700">ID</label>
<input
type="number"
name="id"
id="id"
placeholder="Enter Post ID here..."
value={id}
onChange={(e) => setId(e.target.value)}
className="mt-1 block w-full py-2 px-3 border border-gray-300 rounded-md"
/>
</div>
<button className="w-full py-2 px-4 border border-transparent rounded-md
shadow-sm text-sm font-medium text-white bg-red-600 hover:bg-red-700
focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
type="submit">Delete Now</button>
</form>
</div>
);
}
export default DeletePost;
» Post by ID Component
Let’s now move forward and create a component that will fetches and displays a post by its ID using React Router and the Fetch API.
The PostById
component retrieves and displays a single blog post based on its ID. It utilizes the useParams
hook from react-router-dom
to extract the ID parameter from the URL. Upon mounting, it sends a GET request to http://localhost:3000/post-by-id/${id}
to fetch the corresponding post data. The retrieved post is stored in the component’s state using useState
. If the post data is not yet available, it displays a loading message. Once the data is fetched, it renders the post details in a JSON format within a <pre>
tag.
import { useParams } from 'react-router-dom';
import { useState, useEffect } from 'react';
function PostById() {
let { id } = useParams();
const [post, setPost] = useState(null);
useEffect(() => {
fetch(`http://localhost:3000/post-by-id/${id}`)
.then(response => response.json())
.then(data => setPost(data))
.catch(error => console.error('Error:', error));
}, [id]);
if (!post) {
return <div>Loading...</div>;
}
return (
<div>
<pre>{JSON.stringify(post, null, 2)}</pre>
</div>
);
}
export default PostById;
» Post by Title Component
The PostByTitle
component fetches and displays a blog post based on its title. It utilizes the useParams
hook from react-router-dom
to extract the title parameter from the URL. Upon rendering, it sends a GET request to http://localhost:3000/posts-by-title/${title}
to retrieve the corresponding post data. The retrieved post is stored in the component’s state using useState
. If the post data is not yet available, it displays a loading message. Once the data is fetched, it renders the post details in a JSON format within a <pre>
tag.
import { useParams } from 'react-router-dom';
import { useState, useEffect } from 'react';
function PostByTitle() {
let { title } = useParams();
const [post, setPost] = useState(null);
useEffect(() => {
fetch(`http://localhost:3000/posts-by-title/${title}`)
.then(response => response.json())
.then(data => setPost(data))
.catch(error => console.error('Error:', error));
}, [title]);
if (!post) {
return <div>Loading...</div>;
}
return (
<div>
<pre>{JSON.stringify(post, null, 2)}</pre>
</div>
);
}
export default PostByTitle;
» Post by Tag Component
The PostsByTag
component fetches and displays blog posts based on a specific tag. It uses the useParams
hook from react-router-dom
to extract the tag parameter from the URL. Upon rendering, it sends a GET request to http://localhost:3000/posts-by-tags/${tag}
to retrieve the posts associated with the specified tag. The fetched posts are stored in the component’s state using useState
. If the posts data is not yet available, it displays a loading message. Once the data is fetched, it renders the post details in a JSON format within a <pre>
tag.
import { useParams } from 'react-router-dom';
import { useState, useEffect } from 'react';
function PostsByTag() {
let { tag } = useParams();
const [posts, setPosts] = useState(null);
useEffect(() => {
fetch(`http://localhost:3000/posts-by-tags/${tag}`)
.then(response => response.json())
.then(data => setPosts(data))
.catch(error => console.error('Error:', error));
}, [tag]);
if (!posts) {
return <div>Loading...</div>;
}
return (
<div>
<pre>{JSON.stringify(posts, null, 2)}</pre>
</div>
);
}
export default PostsByTag;
» Post by Category Component
The PostsByCategory
component fetches and displays blog posts based on a specific category. It utilizes the useParams
hook from react-router-dom
to extract the category parameter from the URL. Upon rendering, it sends a GET request to http://localhost:3000/posts-by-category/${category}
to retrieve the posts associated with the specified category. The fetched posts are stored in the component’s state using useState
. If the posts data is not yet available, it displays a loading message. Once the data is fetched, it renders the post details in a JSON format within a <pre>
tag.
import { useParams } from 'react-router-dom';
import { useState, useEffect } from 'react';
function PostsByCategory() {
let { category } = useParams();
const [posts, setPosts] = useState(null);
useEffect(() => {
fetch(`http://localhost:3000/posts-by-category/${category}`)
.then(response => response.json())
.then(data => setPosts(data))
.catch(error => console.error('Error:', error));
}, [category]);
if (!posts) {
return <div>Loading...</div>;
}
return (
<div>
<pre>{JSON.stringify(posts, null, 2)}</pre>
</div>
);
}
export default PostsByCategory;
» Test the app
Now all that is done, let’s test our to see if it works perfectly. Make sure that the server and client are running. Then let’s head to the localhost:5173 where our client is and let’s test the app.
# Start server on 3000
cd server
pnpm start
# Start Client on 5173
cd client
pnpm run dev
And that’s all for this blog. I hope you enjoyed it and found it useful. Also if you have any doubts please drop a message on our discord server. The link can be found below.
👉 And I’ll see you in next one, till then bye-bye and take care.