Blog Contant
Blog Contant
Blog Contant
Blogging has become an essential tool for individuals and organizations alike to share their
thoughts, opinions, and knowledge with the world. With the advancement of technology, creating
and managing a blog has become easier than ever. In this post, we will discuss how to build a
blog post app using React, Node.js and MySQL. React is a popular front-end JavaScript library
for building user interfaces, while Node.js is a powerful back-end JavaScript runtime environment
that can be used with MySQL, a popular open-source relational database management system to
By combining these technologies, we can create a powerful and dynamic blog post app that will
allow users to create, view, and interact with blog posts. We’ll be creating a database to store
posts and user information using MySQL, we’ll use Node.js and Express to create a server that
interacts with the database. Next, we’ll build a front-end user interface using React, allowing
users to create and edit blog posts. By the end of this tutorial, you will have gained a solid
understanding of how to integrate these technologies to create a fully functional web application.
So let’s get started and learn how to build a blog post app with React, Node.js, and MySQL.
On the Home page of our blog post application, we will employ a query to retrieve all posts stored
in the MySQL database. Upon clicking a specific post, the application will execute a query to
retrieve the details of the post, including the user who wrote it. This functionality will require a
User registration and login features will be implemented and only authorized users, who are the
creators of posts, will be granted editing and deleting permissions. Additionally, the application
will offer to the users the ability to write posts using a rich text editor, allowing the application to
This project will help developers in understanding the basic concepts of React and MySQL,
database relationships, user authentication, JSON, web tokens for security, cookies manipulation,
React is a popular open-source JavaScript library used for building user interfaces. It was
developed by Facebook and released in 2013. React uses a virtual DOM (Document Object
Model) which is a lightweight copy of the actual DOM that allows React to efficiently update
only the parts of the UI that need to be changed. This approach provides better performance
React’s component-based architecture is another key feature. Components are reusable pieces of
code that define the structure and behavior of a part of the user interface. Components can be
nested within each other, allowing developers to create complex UIs by combining smaller and
simpler components. This approach promotes code reusability, maintainability, and testability.
write server-side JavaScript code. This enables the use of a single language (JavaScript) for both
client-side and server-side development. Node.js provides non-blocking I/O operations, which
means that I/O operations do not block the event loop and can be executed asynchronously. This
makes Node.js a great choice for building scalable, high-performance web applications.
Node.js also has a large and active community with a wide range of modules and packages
available through the npm registry. These modules and packages provide functionality that can be
easily integrated into Node.js applications, which speeds up development and improves the
Overall, React and Node.js are powerful tools that can be used to build modern web applications.
Their popularity is driven by their ease of use, performance, and active communities.
let’s get started
We will initiate our project by creating a root directory and subsequently adding two
subdirectories named ‘client’ (where React app will be located) and ‘api’ (will be the back-end of
To avoid creating unnecessary files when setting up a React application and to save time on
cleaning up the project, please refer to the react-mini branch in my GitHub repository, where you
With that said, go to the ‘client’ folder and run the following command:
git clone --single-branch -b "react-mini" https://github.com/santiagobedoa/tutorial-
blog_post_app.git .
Note that the ‘pages’ folder found inside ‘src’ contains some .jsx files that will be the pages that
our application will have, but how we’re going to reach all those pages? The answer is react-
React Router DOM is a library that provides routing capabilities to a React application. It is built
on top of the React Router library and provides a higher-level API for building client-side
applications with routing. With React Router DOM, you can easily manage the application state
and change the content displayed on the screen based on the URL path. It provides a set of
components and methods that allow you to define routes and navigate between them using links
or programmatic navigation. It also supports server-side rendering and integration with popular
Example
Here is a simple example of how you can use react-router-dom to create a navigation bar
function Home() {
return <h2>Welcome to the Home page</h2>;
}
function About() {
return <h2>Learn more about us on the About page</h2>;
}
function Contact() {
return <h2>Contact us through the Contact page</h2>;
}
function App() {
return (
<Router>
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/contact">Contact</Link>
</li>
</ul>
</nav>
different pages. We're also defining three different page components (Home, About and
Contact) that are rendered when the user navigates to the corresponding URL path.
Note
The Router component is the root component of the routing system. It is responsible for
keeping the current URL in sync with the current set of Route components that are
being rendered.
The Route component is used to declare which components should be rendered for a
specific URL path. It can take a path prop to specify the URL path and
It’s important to have all Route components inside the Router component so that the
router can match the current URL with the path props of the Route components and
render the corresponding components. If the Route components were not inside
the Router component, the router would not be able to match the current URL with
the path props of the Route components and no components would be rendered.
React-Router-Dom Implementation
Now you know the basics of react-router-dom let’s install it by running npm
Before continuing we’re going to create the navbar and the footer that will be common
components in our pages excluding the login and register page. Let’s create a folder
// App.js
// Import the necessary components from the react-router-dom
package and other custom components
import { createBrowserRouter, RouterProvider, Outlet } from "react-
router-dom";
import Home from "./pages/Home";
import Login from "./pages/Login";
import Register from "./pages/Register";
import Single from "./pages/Single";
import Write from "./pages/Write";
import Navbar from "./components/Navbar";
import Footer from "./components/Footer";
The code defines a Layout component that provides the basic structure of the web page,
with a navigation bar, content, and a footer. It then defines the application routes using
the createBrowserRouter function from react-router-dom, which takes an array
of objects with path and element properties. The path property specifies the URL path,
while the element property specifies the component to be rendered when the path is
matched. The Layout component is used as a container for the routes, with
the Outlet component used to render the nested routes inside the Layout.
Finally, the App function provides the routing context to the entire app using
The router object contains all the defined routes. The App function returns a div
class container.
Now, if you run your app using npm start and visit http://localhost:3000/ you should
Navbar
Home
Footer
With this, I hope you have a clear understanding of how routes and the Outlet
component work.
Basic Front-end
As the primary objective of this tutorial is to illustrate the integration of React, Node.js,
and MySQL to develop a functional application, I will omit the fundamental front-end
creation phase, which is more aligned with HTML and CSS writing practices than React
development.
Make sure
e.jsx, Write.jsx
npm install sass: will be used to style our front-end. But, what
functions, and more. Sass allows developers to write more maintainable and
scalable stylesheets with less repetition and boilerplate code, and it can be
frameworks like React, Vue, and Angular. Sass files can be compiled into
users to format and edit text using a toolbar with a variety of options. It is
built on top of the Quill.js library, and provides a flexible and customizable
interface for creating rich text editors in React applications. With React Quill,
developers can easily add rich text editing capabilities to their applications,
and allow users to create and format text content with ease.
If you start the application ( npm start ) it should look like this:
http://localhost:3000/login
basic-frontend Login. Jsx
http://localhost:3000/register
basic-frontend Register. Jsx
http://localhost:3000/
basic-frontend Home. Jsx
http://localhost:3000/post/1
Before you go any further, I recommend that you stop and review each of the
components that were created and make sure you understand the code, as we will be
We will proceed to navigate to the API folder, and initialize a Node application through
the npm in it -y command. Prior to continuing, let's install the necessary libraries
that we will be working with. First, we will install express via npm install
express which will provide us with the ability to create our server. We will also be
changes.
We will modify the script “test” to “start” in the package.json file and set it to
we will add "types": "modules" in the same file to allow importing libraries in the ES6
module format. package.json should looks like:
{
"name": "api",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"start": "nodemon index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.2",
"mysql2": "^3.1.2",
"nodemon": "^2.0.20"
}
}
Now let’s create index.js file, where we will create our server:
// index.js
import express from "express";
This code sets up an Express application with a JSON middleware and starts a server on
port 8800. The app constant is an instance of the Express application, which is used to
define the routes and middleware for the server. The express.json() middleware is
In order to establish the connection to the MySQL server, the mysql2 library is used
practicality, the root user is being used, which doesn't have a password. However, it's
recommended to create a new user with a password and store this information in
an .env file. This file can be accessed using the dotenv library, which loads the
environment variables stored in the .env file into process.env so that they can be
You are probably wondering why the name of the database is “blog_app” if we have never
-- setup_mysql.sql
-- create database
CREATE DATABASE IF NOT EXISTS blog_app;
-- create table users
CREATE TABLE IF NOT EXISTS `blog_app`.`users` (
`id` INT NOT NULL AUTO_INCREMENT,
`username` VARCHAR(255) NOT NULL,
`email` VARCHAR(255) NOT NULL,
`password` VARCHAR(255) NOT NULL,
`img` VARCHAR(255) NULL,
PRIMARY KEY (`id`)
);
-- create table posts
CREATE TABLE IF NOT EXISTS `blog_app`.`posts` (
`id` INT NOT NULL AUTO_INCREMENT,
`title` VARCHAR(255) NOT NULL,
`desc` VARCHAR(1000) NOT NULL,
`img` VARCHAR(255) NOT NULL,
`cat` VARCHAR(255) NOT NULL,
`date` DATETIME NOT NULL,
`uid` INT NOT NULL,
PRIMARY KEY (`id`),
INDEX `uid_idx` (`uid` ASC) VISIBLE,
CONSTRAINT `uid`
FOREIGN KEY (`uid`)
REFERENCES `blog_app`.`users` (`id`)
ON DELETE CASCADE
ON UPDATE CASCADE
);
The above code is a set of SQL commands that creates a new database named “blog_app”
The “users” table has columns named “id”, “username”, “email”, “password”, and “img”.
The “id” column is set as the primary key for the table.
The “posts” table has columns named “id”, “title”, “desc”, “img”, “cat”, “date”, and “uid”.
The “id” column is set as the primary key and the “uid” column has a foreign key
constraint that references the “id” column in the “users” table. The “INDEX” command
creates an index on the “uid” column for faster querying. The “ON DELETE CASCADE”
and “ON UPDATE CASCADE” commands ensure that any changes to the “users” table
MySQL server. If you're using root user without password, you can run cat
password for the root user, you can add the -p flag at the end of the command, which
To modularize our code and improve maintainability, we’ll move away from placing all
routes and functions in index.js. Instead, we’ll create a new directory called “routes”
within the API directory. Inside this directory, we’ll have files for each endpoint of our
application, including auth.js, posts.js, and users.js. Each of these files will
define the routes for the endpoint and the corresponding function to be executed when a
request is made to that route. This approach helps us create specific functions that
perform well-defined tasks, making our code more modular and easier to manage.
Example:
// api/routes/posts.js
import express from "express";
// Defining a new route on this router for the GET HTTP method for
the '/test' endpoint
// When the '/test' endpoint is reached, the provided callback
function will execute
router.get("/test", (req, res) => {
res.json("this is post");
});
// api/index.js
import express from "express";
// Import router as postRoutes
import postRoutes from "./routes/posts.js";
// Routes
app.use("/api/posts", postRoutes);
as we said before, is important to modularize our code. So we are going to create another
folder inside api named controller that will contain the following
files: auth.js, posts.js, and users.js. Within these files we will have all the
Example:
Let’s add the following code to controller/posts.js:
// controller/posts.js
// Exporting a function called testPost that takes two parameters,
req and res, which represent the request and response objects,
respectively.
export const testPost = (req, res) => {
// Sending a JSON response with a string message "this is post
from controller" when this function is called.
res.json("this is post from controller");
};
Modify routes/posts.js:
// routes/posts.js
import express from "express";
import { testPost } from "../controller/posts.js";
// Defining a new route on this router for the GET HTTP method for
the '/test' endpoint
// When the '/test' endpoint is reached, testPost function will
execute
router.get("/test", testPost);
Up to this point, the API folder structure should look like this:
api
├── controller
│ ├── auth.js
│ ├── posts.js
│ └── users.js
├── routes
│ ├── auth.js
│ ├── posts.js
│ └── users.js
├── db.js
├── index.js
├── package.json
└── setup_mysql.sql
Now that we have the basic structure of our project. Let’s create our authentication
The library provides methods for generating, signing, and verifying JSON
algorithm to securely hash passwords and protect them from attacks such as
brute-force and rainbow table attacks. Crypts generates a salt for each
password and then hashes the salted password multiple times, making it
more difficult for an attacker to crack the password even if they have access to
input to a one-way function, such as a cryptographic hash function. The purpose of the
salt is to make it more difficult to perform attacks such as precomputed hash attacks and
dictionary attacks, which try to guess the input of the hash function. By adding a salt to
the input, the resulting hash will be different, even if the input is the same. This makes it
much more difficult for an attacker to crack the hashed password. The salt value is
Register BACK-END
The first thing we need to do is create a function that allows us to register new users , let’s
add the following function to controller/auth.js file:
// controller/auth.js
import { db } from "../db.js";
import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";
This code exports a function named “register” that handles a POST request for
registering a new user. The function first checks if the user already exists in the database
by executing a SELECT query with the user’s email and username. If the query returns
any data, it means the user already exists, so the function returns a response with a 409
If the user does not exist, the function hashes the user’s password using bcrypt library
and creates a new user by executing an INSERT query with the user’s username, email,
and hashed password. If the query is successful, the function returns a response with a
The function uses the db object to execute the SQL queries, which should be established
in a separate file (db.js) to establish a connection with the database.
// routes/auth.js
// importing express and the necessary controller functions
import express from "express";
import { login, logout, register } from "../controller/auth.js";
Note that login and logout functions haven’t been implemented yet.
Finally, let’s implement authentication routes to our application. index.js file should
look like:
// index.js
// Import the Express library
import express from "express";
// Import routes
import authRoutes from "./routes/auth.js";
import postRoutes from "./routes/posts.js";
// Routes
app.use("/api/auth", authRoutes);
app.use("/api/posts", postRoutes);
The benefits of modularized code become apparent when considering the specific
application with all necessary middleware, a db.js file to establish the database
In order to test the registration of a user, we must make some changes in the front-end.
First we need to install axios (npm install axios) to be able to communicate with
the back-end.
Axios is a popular JavaScript library used to make HTTP requests from a web browser or
Node.js. It provides a simple and easy-to-use API for making requests to RESTful web
services, and supports a wide range of features such as sending form data, setting
headers, handling request and response interceptors, and canceling requests. Axios can
be used in both client-side and server-side applications, and is widely used in modern
web development for communicating with APIs and fetching data from servers.
Let’s make the changes to registration page (Register.jsx):
// Register.jsx
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import axios from "axios";
return (
<div className="auth">
{/* containing the authentication form */}
<h1>Register</h1>
<form>
<input
required
type="text"
placeholder="username"
name="username"
onChange={handleChange} // triggering handleChange
function on input change
/>
<input
required
type="email"
placeholder="email"
name="email"
onChange={handleChange} // triggering handleChange
function on input change
/>
<input
required
type="password"
placeholder="password"
name="password"
onChange={handleChange} // triggering handleChange
function on input change
/>
<button onClick={handleSubmit}>Register</button>{" "}
{/* triggering handleSubmit function on form submit */}
{err && <p>{err}</p>} {/* displaying error message if
there's any */}
<span>
Do you have an account? <Link to="/login">Login</Link>{"
"}
{/* providing link to login page */}
</span>
</form>
</div>
);
};
When a user enters data into the input fields, the handleChange function updates the
state accordingly.
When the user clicks the “Register” button, the handleSubmit function is triggered. This
function makes an HTTP POST request to the server with the user’s registration
information, and if successful, navigates the user to the login page using the useNavigate
hook. If there’s an error during registration, the error message is displayed on the page.
Finally, we need to add "proxy":http//localhost:8800/api at the end
of client/package.json file. Why? It allows the server to proxy requests to the API
This is useful when the React project is hosted on a different server than the API server,
as it allows the frontend code to make requests to the backend API without worrying
By specifying the proxy in package.json, requests made to paths beginning with “/api”
case, http://localhost:8800).
Now, if you fill the registration form, you must be redirected to the login page and the
let’s apply the same logic for the login and logout functions. Add this two functions to the
bottom of the controller/auth.js file:
// This function handles user login
export const login = (req, res) => {
// SQL query to check if the user exists in the DB
const query = "SELECT * FROM users WHERE username = ?";
The first step is to construct a SQL query to retrieve the user data from the database
using the provided username, stored in req.body.username. The query variable
callback function provides two arguments: err and data. If there is an error, the
If the query returns no data, which means that there is no user with the provided
username, the function returns a 404 status with an error message as a JSON response.
If the login credentials are correct, the function generates a JSON web token with the
user’s id as payload, using the jwt.sign() method.
Then, the password is removed from the data object using destructuring assignment,
and the user data, excluding the password, is stored in the other variable.
Finally, the generated token is set as an HTTP-only cookie, and the user data is sent as a
is used to create a new JWT. A JWT is a compact and self-contained token that contains
information (i.e., the payload) that can be used to verify the identity of the user.
The jwt.sign() method takes two parameters: the payload and a secret key. The
payload is the data that will be stored in the JWT, and it can be any JavaScript object that
can be serialized as JSON. The secret key is a string that is used to sign the token,
ensuring that the token has not been tampered with.
When the jwt.sign() method is called, it creates a new JWT that includes the payload
and a signature that is generated using the secret key. This JWT is then returned as a
string, which can be sent to the client and stored in a cookie or local storage. When the
client sends a request to the server, the server can verify the identity of the user by
decoding the JWT and checking the payload. If the signature is valid and the payload is
trusted, the server can use the information in the payload to authenticate the user and
respond accordingly.
removes the access_token cookie from the user's browser. It takes two arguments: the
name of the cookie to clear ("access_token"), and an object containing additional options
for the cookie. In this case, the options sameSite: "none" and secure: true are
set, which ensure that the cookie is only sent over HTTPS and is not limited to same-site
requests.
cookie-parser
As you may have noticed, we are working with cookies. They are important in web
websites. When a user logs in, the website sets a cookie containing a unique
session ID, which is then sent with each subsequent request from the user.
This allows the server to associate subsequent requests with the same session,
cart items.
which pages they visit or which products they view. This data can be used to
marketing purposes.
Overall, cookies are a powerful tool for web developers to create more personalized and
dynamic web experiences for users, while also providing important functionality such as
session management. They are not as bad as they are painted. As developers we have to
be careful with the third point, because this is when the line of ethics becomes blurred.
In order to work with cookies in our back-end we need to install cookie-parser (npm
middleware:
//index.js
import express from "express";
import authRoutes from "./routes/auth.js";
import postRoutes from "./routes/posts.js";
import cookieParser from "cookie-parser";
// Create an instance of the Express application
const app = express();
// Routes
app.use("/api/auth", authRoutes);
app.use("/api/posts", postRoutes);
Login FRONT-END
// Render the login form with input fields for username and
password and a button to submit the form
return (
<div className="auth">
<h1>Login</h1>
<form>
<input
type="text"
placeholder="username"
name="username"
onChange={handleChange}
/>
<input
type="password"
placeholder="password"
name="password"
onChange={handleChange}
/>
<button onClick={handleSubmit}>Login</button>
{err && <p>{err}</p>}
<span>
Don't you have an account? <Link
to="/register">Register</Link>
</span>
</form>
</div>
);
};
It is not worth explaining what was done since the exact same logic was applied as in the
Register page. Now, if you login with the user previously registered, you must be
across different React components to present data and enable specific actions. As such,
user data should be stored in local storage to ensure availability and accessibility across
components.
For this purpose, we’re going to use react-context. React context is a feature of React
that allows data to be passed down the component tree without the need to pass props
manually at every level. It provides a way to share data between components, even if they
are not directly related in the component hierarchy. Context is often used for global state
management, such as user authentication and theme settings. It consists of two parts: a
Provider component that provides the data to its children, and a Consumer component
that consumes the data. The useContext hook can also be used to consume context data
in functional components.
To begin, we will create a directory named ‘context’, within our client source code, where
we will store the file ‘authContext.js’. The full path to this file should
be client/src/context/authContext.js. This file will serve as the context for our
application:
// src/context/authContext.js
import axios from "axios";
import { createContext, useEffect, useState } from "react";
This code defines a context for managing authentication in a React application. It exports
an AuthContext object created with the createContext function, which can be used
the child components that provides the authentication state and methods for logging in
and logging out. It uses the useState hook to maintain the current user's information
in the component's state. The user's information is initially set by retrieving the stored
user object from the browser's local storage using localStorage.getItem("user").
The login function is defined to perform a login operation by sending a POST request to
the /auth/login endpoint with the user's input data. If the request is successful, the
The useEffect hook is used to store the current user's information to the browser's
the currentUser, login, and logout values as the context value to its child
Last, we need to ensure that our application is wrapped with the Provider component so
that the components can access the state data and actions provided by the context.
Specifically, the App component will be the child component of the Provider component.
// Navbar.jsx
import React, { useContext } from "react";
import { Link, useNavigate } from "react-router-dom";
import { AuthContext } from "../context/authContext";
import Logo from "../images/logo.png";
return (
<div className="navbar">
<div className="container">
<div className="logo">
<a href="/">
<img src={Logo} alt="logo" />
</a>
</div>
<div className="links">
<Link className="link" to="/?cat=art">
<h6>ART</h6>
</Link>
<Link className="link" to="/?cat=science">
<h6>SCIENCE</h6>
</Link>
<Link className="link" to="/?cat=technology">
<h6>TECHNOLOGY</h6>
</Link>
<Link className="link" to="/?cat=cinema">
<h6>CINEMA</h6>
</Link>
<Link className="link" to="/?cat=design">
<h6>DESIGN</h6>
</Link>
<Link className="link" to="/?cat=food">
<h6>FOOD</h6>
</Link>
<span>{currentUser?.username}</span>
{currentUser ? (
<span onClick={logoutNavbar}>Logout</span>
) : (
<Link className="link" to="/login">
Login
</Link>
)}
<span className="write">
<Link className="link" to="/write">
Write
</Link>
</span>
</div>
</div>
</div>
);
};
Now if you login, you should see the username in the navbar, and if you logout you must
Now we will retrieve information about the posts. However, before proceeding, I would
like to provide you with an SQL script to insert data into the database. I recommend to
delete any pre-existing data to avoid conflicts. Note that the password in the script is
already encrypted. To login use the password “test” which was used for the two users.
-- data_mysql.sql
-- add users
INSERT INTO `blog_app`.`users` (`id`, `username`, `email`,
`password`, `img`) VALUES ('1', 'Santiago', '[email protected]',
'$2a$10$VqZJYEYnauC8qrVrVpWZX.E0u95i40pBmAEOc.Vs178nUJNyenzaa',
'https://images.unsplash.com/photo-1633332755192-727a05c4013d?
ixlib=rb-
4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format
&fit=crop&w=1760&q=80');
INSERT INTO `blog_app`.`users` (`id`, `username`, `email`,
`password`, `img`) VALUES ('2', 'Pablo', '[email protected]',
'$2a$10$VqZJYEYnauC8qrVrVpWZX.E0u95i40pBmAEOc.Vs178nUJNyenzaa',
'https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?
ixlib=rb-
4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format
&fit=crop&w=1760&q=80');
-- insert posts
insert into `blog_app`.`posts` (`id`, `title`, `desc`, `img`,
`uid`, `cat`, `date`) VALUES ('1', 'Lorem ipsum dolor sit amet
consectetur adipisicing elit', 'Lorem, ipsum dolor sit amet
consectetur adipisicing elit. A possimus excepturi aliquid nihil
cumque ipsam facere aperiam at! Ea dolorem ratione sit debitis
deserunt repellendus numquam ab vel perspiciatis corporis!',
'https://images.unsplash.com/photo-1513364776144-60967b0f800f?
ixlib=rb-
4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format
&fit=crop&w=2671&q=80', '1', 'art', '2023-02-08');
insert into `blog_app`.`posts` (`id`, `title`, `desc`, `img`,
`uid`, `cat`, `date`) VALUES ('2', 'Lorem ipsum dolor sit amet
consectetur adipisicing elit', 'Lorem, ipsum dolor sits amet
consectetur adipisicing elit. A possimus excepturi aliquid nihil
cumque ipsam facere aperiam at! Ea dolorem ratione sit debitis
deserunt repellendus numquam ab vel perspiciatis corporis!',
'https://images.unsplash.com/photo-1536924940846-227afb31e2a5?
ixlib=rb-
4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format
&fit=crop&w=1766&q=80', '2', 'food', '2023-02-08');
insert into `blog_app`.`posts` (`id`, `title`, `desc`, `img`,
`uid`, `cat`, `date`) VALUES ('3', 'Lorem ipsum dolor sit amet
consectetur adipisicing elit', 'Lorem, ipsum dolor sit amet
consectetur adipisicing elit. A possimus excepturi aliquid nihil
cumque ipsam facere aperiam at! Ea dolorem ratione sit debitis
deserunt repellendus numquam ab vel perspiciatis corporis!',
'https://images.unsplash.com/photo-1546069901-ba9599a7e63c?
ixlib=rb-
4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format
&fit=crop&w=1180&q=80', '2', 'art', '2023-02-08');
insert into `blog_app`. `posts` (`id`, `title`, `desc`, `img`,
`uid`, `cat`, `date`) VALUES ('4', 'Lorem ipsum dolor sit amet
consectetur adipisicing elit', 'Lorem, ipsum dolor sits amet
consectetur adipisicing elit. A possimus excepturi aliquid nihil
cumque ipsam facere aperiam at! Ea dolorem ratione sit debitis
deserunt repellendus numquam ab vel perspiciatis corporis!',
'https://images.unsplash.com/photo-1493770348161-369560ae357d?
ixlib=rb-
4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format
&fit=crop&w=1770&q=80', '1', 'food', '2023-02-08');
Now we have two users and some posts created in the database we can proceed. First let’s
// api/routes/posts.js
import express from "express";
import {
add Post,
delete Post,
get Post,
get Posts,
update Post,
} from "../controller/posts.js";
// Use the database object to query the database for the post
with
// the given ID, and any necessary parameters.
db.query(q, [req.params.id], (err, data) => {
// If there's an error, send a 500 status code and the error
message
if (err) return res.status(500).json(err);
// Otherwise, send a 200 status code and the first item in the
data array as JSON
return res.status(200).json(data[0]);
});
};
// Update a post
export const updatePost = (req, res) => {
// Get the access token from the request cookies.
const token = req.cookies.access_token;
// Verify the token using the "jwtkey" secret key. If the token
is not valid, return an error response.
jwt. Verify (token, "jwtkey", (err, user Info) => {
if (err) return res. status (403). json ("Token is not
valid!");
// Execute the query using the values and post ID. If there's
an error, return an error response. Otherwise, return a success
response.
db.query(q, [...values, postId, userInfo.id], (err, data) => {
if (err) return res.status(500).json(err);
return res.json("Post has been updated.");
});
});
};
authenticated user is the one who created the post. Like add Post and delete Post,
Given that we have established endpoints within our backend to fetch post data, the next
step is to incorporate these endpoints into the Home page component in order to retrieve
// Home. Jsx
import axios from "axios";
import React, { useEffect, useState } from "react";
import { Link, use Location } from "react-router-dom";
// Getting the current URL query string (if any) using the use
Location hook from react-router-dom
const cat = use Location().search;
This is a React component that displays a list of blog posts. The component retrieves data
from a server based on the current URL query string and uses state to store and render
the retrieved data. It also defines a helper function to extract text content from HTML
strings. The component maps over the retrieved data and renders a Post component for
each post, which includes the post image, title, description, and a link to read more.
If you visit the “art” section you should see only the posts that contain the “art” category,
the same should happen for the “food” category:
Art:
Food:
The useState hook initializes a state variable called post to an empty object and provides
a function called setPost to update the state. The useLocation hook returns the current
location object, which contains information about the current URL. The useNavigate
hook returns a navigate function that can be used to navigate to a new location. The
useContext hook is used to get the current user from the AuthContext. The useEffect
hook is used to fetch the blog post data from the server when the component mounts.
The handleDelete function is used to delete a blog post. The getText function is a helper
function to extract plain text from an HTML string. Finally, the component returns a JSX
element that renders the blog post content, including the post image, author and date
information, edit and delete buttons, and post title and description.
The moment library (npm install moment) was used to get the current date to insert
into the DB. Note: the description found in the database is in quotation marks and italics.
The paragraphs with Lorem Ipsum were left to fill out the information page (aesthetic
purposes only).
Recommended posts
Let’s modify Menu.jsx:
// Menu.jsx
import axios from "axios";
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
return (
<div className="menu">
<h1>Other posts you may like</h1>
{posts.map((post) => (
<div className="post" key={post.id}>
<img src={`${post.img}`} alt="post cover" />
<h2>{post.title}</h2>
{/* Using Link component to navigate to the post */}
<Link className="link" to={`/post/${post.id}`}>
<button>Read More</button>
</Link>
</div>
))}
</div>
);
};
As you can see, the logic applied to this component is quite similar to the one we have
used for the others. We utilize the useEffect hook to invoke axios for data fetching and
employ the useState hook (posts, setPosts) to retain the response. Subsequently, we
execute the rendering of posts, pertaining to a specific category, from the “posts”
variable.”
Write post
Before we dive into implementing the article writing feature, let’s first decide how we
want to save the images that users attach to their posts. There are several options we
could use, like cloud storage services, but for this tutorial, we’re going to keep it simple
and show you how to upload your images directly to your server.
Let’s install multer (npm install multer) which is a popular middleware for
handling file uploads in Node.js. Multer is used in conjunction with Express.js and allows
through HTML forms. With Multer, we can easily specify where to save uploaded files,
limit the size of files that can be uploaded, and perform various validations on the files
being uploaded.
Let’s add multer to index.js in the api server:
// ape/index.js
information such as the post’s title and description. By utilizing this information, we can
Single and Menu components to properly render the images that because we are using
multer.
What’s next?
This tutorial provides a comprehensive guide on integrating React, Node.js, and MySQL
middleware, routes, and controllers using the Express framework. You have learned how
to establish a connection from the server to the database and perform CRUD (Create,
Great job on completing the tutorial! Now, if you want to take your app to the next level,
here’s a fun challenge for you: add a feature that allows users to upload a profile image
when they register. You could also create a user panel where users can edit their profile
information such as their name, email, password, and profile picture. Don’t worry if
you’re not sure where to start, the route and user controller are ready for you to get
This project was created by “Lama Dev,” a passionate developer who shares valuable
insights and tutorials on their YouTube channel. You can watch the full tutorial series
It’s essential to understand that this blog and the associated code were developed with a
primary focus on learning and educational purposes. I believe that one of the best ways
to truly grasp and internalize knowledge is by explaining it to others. In that spirit, this
blog serves as a comprehensive guide to integrating React, Node.js, and MySQL to build
a full-stack application.