Sick Gaming
[Tut] React + Node + MySQL CRUD App Tutorial (Full-Stack API Integration) - Printable Version

+- Sick Gaming (https://www.sickgaming.net)
+-- Forum: Programming (https://www.sickgaming.net/forum-76.html)
+--- Forum: PHP Development (https://www.sickgaming.net/forum-82.html)
+--- Thread: [Tut] React + Node + MySQL CRUD App Tutorial (Full-Stack API Integration) (/thread-103615.html)



[Tut] React + Node + MySQL CRUD App Tutorial (Full-Stack API Integration) - xSicKxBot - 12-06-2025

[Tut] React + Node + MySQL CRUD App Tutorial (Full-Stack API Integration)

<div style="margin: 5px 5% 10px 5%;"><img src="https://www.sickgaming.net/blog/wp-content/uploads/2025/12/react-node-mysql-crud-app-tutorial-full-stack-api-integration.jpg" width="550" height="288" title="" alt="" /></div><div><div class="modified-on" readability="7.1304347826087"> by <a href="https://phppot.com/about/">Vincy</a>. Last modified on November 6th, 2025.</div>
<p>This tutorial has a React NodeJS example for building a full-stack CRUD application. It gives easy guidelines to design the frontend and backend. The frontend React connects the NodeJS API controllers to interact for creating, reading, updating, and deleting. This will provide persistent storage with a <a href="https://phppot.com/mysql/access-mysql-from-php/">MySQL database connected</a> via API DAOs.</p>
<p><img decoding="async" loading="lazy" class="alignnone size-large wp-image-24527" src="https://phppot.com/wp-content/uploads/2025/10/react-node-mysql-crud-full-stack-tutorial-550x288.jpeg" alt="React Node Mysql Crud Full Stack Tutorial" width="550" height="288" srcset="https://phppot.com/wp-content/uploads/2025/10/react-node-mysql-crud-full-stack-tutorial-550x288.jpeg 550w, https://phppot.com/wp-content/uploads/2025/10/react-node-mysql-crud-full-stack-tutorial-300x157.jpeg 300w, https://phppot.com/wp-content/uploads/2025/10/react-node-mysql-crud-full-stack-tutorial-768x402.jpeg 768w, https://phppot.com/wp-content/uploads/2025/10/react-node-mysql-crud-full-stack-tutorial.jpeg 1200w" sizes="auto, (max-width: 550px) 100vw, 550px"></p>
<h2>Add form design and user creation with NodeJS and MySQL</h2>
<p>The user-create form contains only two fields to simplify the example. So, the React frontend assets manages states and props to carry these inputs’ data to post.</p>
<p>The <code>addUser()</code> props triggers the add action request to post the form data to the server.</p>
<p>It executes the insert query on clicking the ‘Add User’ button. The response will have the auto-generated key created on successful insert action of the <a href="https://phppot.com/php/how-to-create-php-crud-using-oops-with-mysqli-in-mvc/">CRUD functionality</a>.</p>
<p class="code-heading"><code>src/components/form/AddUserForm.jsx</code></p>
<pre class="prettyprint"><code class="language-jsx">import { useState } from 'react'
const AddUserForm = props =&gt; { const initialFormState = { id: null, name: '', username: '' } const [ user, setUser ] = useState(initialFormState) const handleInputChange = event =&gt; { const { name, value } = event.target setUser({ ...user, [name]: value }) }
return ( &lt;form onSubmit={event =&gt; { event.preventDefault() if (!user.name || !user.username) return props.addUser(user) setUser(initialFormState) }}&gt; &lt;label&gt;Name&lt;/label&gt; &lt;input type="text" name="name" value={user.name} onChange={handleInputChange} /&gt; &lt;label&gt;Username&lt;/label&gt; &lt;input type="text" name="username" value={user.username} onChange={handleInputChange} /&gt; &lt;div className="action-buttons"&gt; &lt;button type="submit"&gt;Add User&lt;/button&gt; &lt;/div&gt; &lt;/form&gt;
)
}
export default AddUserForm
</code></pre>
<p class="code-heading"><code>part of userControllers.js</code></p>
<pre class="prettyprint"><code class="language-js">export const addUser = (req, res) =&gt; { const { name, username } = req.body; db.query("INSERT INTO users (name, username) VALUES (?, ?)", [name, username], (err, result) =&gt; { if (err) return res.status(500).json({ error: err.message }); res.json({ id: result.insertId, name, username }); });
};
</code></pre>
<p><img decoding="async" loading="lazy" class="alignnone size-large wp-image-24465" src="https://phppot.com/wp-content/uploads/2025/10/react-crud-add-user-550x252.jpg" alt="react crud add user" width="550" height="252" srcset="https://phppot.com/wp-content/uploads/2025/10/react-crud-add-user-550x252.jpg 550w, https://phppot.com/wp-content/uploads/2025/10/react-crud-add-user-300x137.jpg 300w, https://phppot.com/wp-content/uploads/2025/10/react-crud-add-user-768x352.jpg 768w, https://phppot.com/wp-content/uploads/2025/10/react-crud-add-user-1536x704.jpg 1536w, https://phppot.com/wp-content/uploads/2025/10/react-crud-add-user.jpg 1628w" sizes="auto, (max-width: 550px) 100vw, 550px"></p>
<h2>Displaying user data rows with edit and delete action controls</h2>
<p>The <code>UserTable</code> component displays the data table dynamically from the MySQL database. The <code>prop.users</code> tree map is used to form these rows to the UI.</p>
<p>Each row contains edit and delete controls that are bound to <code>editRow</code> and <code>deleteUser</code> functions respectively.</p>
<p class="code-heading"><code>src/components/table/UserTable.jsx</code></p>
<pre class="prettyprint"><code class="language-js">import React from 'react'
const UserTable = props =&gt; ( &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;Name&lt;/th&gt; &lt;th&gt;Username&lt;/th&gt; &lt;th&gt;Actions&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; {props.users.length &gt; 0 ? ( props.users.map(user =&gt; ( &lt;tr key={user.id}&gt; &lt;td&gt;{user.name}&lt;/td&gt; &lt;td&gt;{user.username}&lt;/td&gt; &lt;td&gt; &lt;button onClick={() =&gt; { props.editRow(user) }} className="button muted-button" &gt; Edit &lt;/button&gt; &lt;button onClick={() =&gt; props.deleteUser(user.id)} className="button muted-button" &gt; Delete &lt;/button&gt; &lt;/td&gt; &lt;/tr&gt; )) ) : ( &lt;tr&gt; &lt;td colSpan={3}&gt;No users&lt;/td&gt; &lt;/tr&gt; )} &lt;/tbody&gt; &lt;/table&gt;
)
export default UserTable
</code></pre>
<p>In NodeJS the <code>getUsers()</code> module reads all users into an JSON response array as built below.</p>
<p class="code-heading"><code>part of userControllers.js</code></p>
<pre class="prettyprint"><code class="language-js">export const getUsers = (req, res) =&gt; { db.query("SELECT * FROM users", (err, results) =&gt; { if (err) return res.status(500).json({ error: err.message }); res.json(results); });
};
</code></pre>
<h2>Updating current user by ID using update API in NodeJS</h2>
<p>The add and edit forms are exactly same except that this edit form populated the existing user data. The <code>currentUser</code> props contains the exiting user details read by id.</p>
<p>On clicking the ‘Edit’ button the user id is passed to <a href="https://phppot.com/php/mysqli_fetch_array-php/">send the read request</a> with the id.</p>
<p>Once the enduser edits the detail and submit, it request the NodeJS API to perform the update operation of the CRUD module.</p>
<p>Both add and edit button clicks prevents the default submit and requests the API via network call.</p>
<p>The edit form is used to view or edit the user details. When clicking cancel the editing directive is set to false to switch to the view mode.</p>
<p class="code-heading"><code>src/components/form/EditUserForm</code></p>
<pre class="prettyprint"><code class="language-jsx">import { useState, useEffect } from 'react'
const EditUserForm = props =&gt; { const [user, setUser] = useState(props.currentUser) useEffect( () =&gt; { setUser(props.currentUser) }, [props] ) const handleInputChange = event =&gt; { const { name, value } = event.target setUser({ ...user, [name]: value }) }
return ( &lt;form onSubmit={event =&gt; { event.preventDefault() props.updateUser(user.id, user) }} &gt; &lt;label&gt;Name&lt;/label&gt; &lt;input type="text" name="name" value={user.name} onChange={handleInputChange} /&gt; &lt;label&gt;Username&lt;/label&gt; &lt;input type="text" name="username" value={user.username} onChange={handleInputChange} /&gt; &lt;div className="action-buttons"&gt; &lt;button type="submit"&gt;Update User&lt;/button&gt; &lt;button onClick={() =&gt; props.setEditing(false)} className="button muted-button"&gt; Cancel &lt;/button&gt; &lt;/div&gt; &lt;/form&gt;
)
}
export default EditUserForm
</code></pre>
<p>In the NodeJS, the backend user controller has an exclusive handle to prepare the user update query.</p>
<p>It binds the request parameter to the update query to go with the update operation.</p>
<p class="code-heading"><code>part of userControllers.js</code></p>
<pre class="prettyprint"><code class="language-js">export const updateUser = (req, res) =&gt; { const { id } = req.params; const { name, username } = req.body; db.query("UPDATE users SET name=?, username=? WHERE id=?", [name, username, id], (err) =&gt; { if (err) return res.status(500).json({ error: err.message }); res.json({ id, name, username }); });
};
</code></pre>
<p><img decoding="async" loading="lazy" class="alignnone size-large wp-image-24467" src="https://phppot.com/wp-content/uploads/2025/10/react-crud-edit-user-550x317.jpg" alt="react crud edit user" width="550" height="317" srcset="https://phppot.com/wp-content/uploads/2025/10/react-crud-edit-user-550x317.jpg 550w, https://phppot.com/wp-content/uploads/2025/10/react-crud-edit-user-300x173.jpg 300w, https://phppot.com/wp-content/uploads/2025/10/react-crud-edit-user.jpg 763w" sizes="auto, (max-width: 550px) 100vw, 550px"></p>
<h2>Deleting a user row via NodeJS controller</h2>
<p>As like as an edit request, the delete action is also receives the user’s unique id in the request parameter.</p>
<p>The backend Node API receives the id and process the delete operation.</p>
<p>In the React frontend, it shows <a href="https://phppot.com/javascript/javascript-confirm/">confirmation message in a toast box</a> to avoid the mistakes.</p>
<p class="code-heading"><code>part of userControllers.js</code></p>
<pre class="prettyprint"><code class="language-js">export const deleteUser = (req, res) =&gt; { const { id } = req.params; db.query("DELETE FROM users WHERE id=?", [id], (err) =&gt; { if (err) return res.status(500).json({ error: err.message }); res.json({ message: "User deleted" }); });
};
</code></pre>
<p><img decoding="async" loading="lazy" class="alignnone size-large wp-image-24468" src="https://phppot.com/wp-content/uploads/2025/10/react-crud-delete-user-550x260.jpg" alt="react crud delete user" width="550" height="260" srcset="https://phppot.com/wp-content/uploads/2025/10/react-crud-delete-user-550x260.jpg 550w, https://phppot.com/wp-content/uploads/2025/10/react-crud-delete-user-300x142.jpg 300w, https://phppot.com/wp-content/uploads/2025/10/react-crud-delete-user-768x362.jpg 768w, https://phppot.com/wp-content/uploads/2025/10/react-crud-delete-user-1536x725.jpg 1536w, https://phppot.com/wp-content/uploads/2025/10/react-crud-delete-user.jpg 1615w" sizes="auto, (max-width: 550px) 100vw, 550px"></p>
<h2>How to run the React + NodeJS example?</h2>
<p>Set up the MySQL database:</p>
<p>First find the <code>/api/sql/users.sql</code> file from the downloadable source code given in this React + NodeJS CRUD example.</p>
<p>Create a database and import that SQL script into that. Then Configure the database details to the <code>db.js</code> file in the NodeJS API root.<br /><strong>Start the backend NodeJS server:</strong></p>
<p>Go to the NodeJS api path and start the server via npm. It will return the server running path <code>http://localhost:5000/</code></p>
<p>In this example this backend api URL is configured with a specific constant for convenience. It avoid the overhead of changing in multiple places based on the environment.</p>
<pre class="prettyprint"><code class="language-makefile">cd /path/api npm install npm start node server.js
</code></pre>
<p><strong>Start the frontend React dev:</strong></p>
<p>Go to the app location path and start the dev server. This will start the dev server <code>http://localhost:5173/</code></p>
<pre class="prettyprint"><code class="language-makefile">cd react-node-mysql-crud-full-stack-tutorial npm install npm run dev
</code></pre>
<h2>Conclusion</h2>
<p>This tutorial built a simple code for React + Node.js + MySQL CRUD. The code for designing frontend and connecting the NodeJS API together gives a full stack execution sample. I hope, you learned how to read and display dynamic data from backend to React components and also how to manipulate them. The server controller is with appropriate cases to handle the CRUD with MySQL to store, retrieve, update, and delete data. With this base, it is easy to add more features to this CRUD utility.</p>
<p><strong>References:</strong></p>
<ol>
<li><a href="https://www.npmjs.com/package/mysql2#using-promise-wrapper" target="_blank" rel="noopener">Installing MySQL client in NodeJS</a></li>
<li><a href="https://dev.mysql.com/doc/refman/8.0/en/sql-prepared-statements.html" target="_blank" rel="noopener">Secured database accessing with prepared statements.</a></li>
</ol>
<p><a class="download" href="https://phppot.com/downloads/react/react-node-mysql-crud-full-stack-tutorial.zip">Download</a></p>
<div class="written-by" readability="9.8427672955975">
<div class="author-photo"> <img loading="lazy" decoding="async" src="https://phppot.com/wp-content/themes/solandra/images/Vincy.jpg" alt="Vincy" width="100" height="100" title="Vincy"> </div>
<div class="written-by-desc" readability="14.764150943396"> Written by <a href="https://phppot.com/about/">Vincy</a>, a web developer with 15+ years of experience and a Masters degree in Computer Science. She specializes in building modern, lightweight websites using PHP, JavaScript, React, and related technologies. Phppot helps you in mastering web development through over a decade of publishing quality tutorials. </div>
</p></div>
<p> <!-- #comments --> </p>
<div class="related-articles">
<h2>Related Tutorials</h2>
</p></div>
<p> <a href="https://phppot.com/react/react-node-mysql-crud-full-stack-tutorial/#top" class="top">↑ Back to Top</a> </p>
</div>


https://www.sickgaming.net/blog/2025/10/31/react-node-mysql-crud-app-tutorial-full-stack-api-integration/