JSON Payload in RESTful API

  1. GitHub Repository Link

  2. Tailwind + Vite + React app

  3. JSON Placeholder Website

» What are we building?

  1. In this blog, we will build a RestFul API that takes in JSON as a payload.

  2. To look at the demo, please visit the youtube video provided above and look at the Demo part in it.

  3. This will be the Blog App which we will build using react for client and express for server.

» Initialise the project

  1. 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

  1. 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
  1. Create files we need: index.js in server’s root directory and db.json in db folder.

» Initialise Client

  1. 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

  1. 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}`);
});
  1. There’s a lot going on. Don’t worry, I got you covered.

  2. 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.
  1. 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.