How to create CRUD operation with CodeIgniter 4 and React JS

Choirul Ihwan
9 min readJan 13, 2022

In this tutorial you will learn how to create a Create-Read-Update-Delete (CRUD) application using CodeIgniter 4 on the backend and React JS on the frontend. Not only that, I will also share with you how to use Bulma CSS to style the User Interface (UI). Thus, the application that is built becomes more elegant, responsive, and user friendly.

Install CodeIgniter 4

CodeIgniter 4 can be installed in 2 ways, Manual installation and Installation using composer. In this tutorial, we will use composer. If you did not install composer yet, visit the following URL to download composer, then install it on your computer.

After composer is installed, test it by open a Command Prompt (CMD) and type the following command:

Once composer is properly installed, then you can create a CodeIgniter 4 project using composer. Create a folder, and give it name like for example “mycrud”. Then open the “mycrud” folder using the Code Editor. For my simplicity, I prefer use Visual Studio Code.

  • Open a new visual code terminal
  • Write the following command create a CodeIgniter 4 project:
composer create-project codeigniter4/appstarter backend

This command will create folder backend and install required packages to use CodeIgniter 4.

  • After the installation is complete, go to the “backend” folder.
  • Type the following command to run web server
cd backend
php spark serve
  • If everything runs well, it will display such following image

Create Database and Tables

For this tutorial, we need to create a new database in MySQL. You can use tools such as SQLyog, PHPMyAdmin, HeidiSQL or other tools. Here I create a database react_database.

  • Create mysql database react_database
  • Create a connection between database and CodeIgniter 4 project. Find the env file in project root, then rename it to .env and open the file.
  • Find and change the code as below
database.default.hostname = localhost
database.default.database = react_database
database.default.username = root
database.default.password = ****
database.default.DBDriver = MySQLi
  • Create a table for example products. In this tutorial, I will use the migration feature in CodeIgniter 4 to create tables. To do that, type the following command:
php spark make:migration Products
  • This command will create a file with the initials Products in the “app/Database/Migrations” folder.
  • Open those file, then type the following code.
<?phpnamespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class Products extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'auto_increment' => True
],
'title' => [
'type' => 'VARCHAR',
'constraint' => 200
],
'price' => [
'type' => 'INT',
'constraint' => 11,
]
]);
$this->forge->addKey('id', true);
$this->forge->createTable('products', true);
}
public function down()
{
$this->forge->dropTable('products');
}
}
  • There are 2 functions up() and down(). Function up() will be used to create a table in the database with the specified fields. In the above case, we create a table with the name “products” with fields id, title, and price along with the data type and other attributes.
  • To create products table, type the following command
php spark migrate

Create Models

  • Type the following command to create a model file
php spark make:model ProductModel
  • CodeIgniter will automatically create a model file named “ProductModel.php” in “app/Models” folder. Open the file and change the code on allowed field as below.
protected $allowedFields        = ['title','price'];

Create Controllers

  • Type the following command to create a controller file
php spark make:controller Products --restful
  • CodeIgniter will automatically create a controller file named “Products.php” in the “app/Controllers” folder. Open and change the code as below.
<?phpnamespace App\Controllers;use CodeIgniter\RESTful\ResourceController;
use CodeIgniter\API\ResponseTrait;
use App\Models\ProductModel;
class Products extends ResourceController
{
use ResponseTrait;
public function index()
{
$model = new ProductModel();
$data = $model->findAll();
return $this->respond($data);
}
public function show($id = null)
{
$model = new ProductModel();
$data = $model->find(['id' => $id]);
if (!$data) return $this->failNotFound('No Data Found');
return $this->respond($data[0]);
}
public function create() {
helper(['form']);
$rules = [
'title' => 'required',
'price' => 'required'
];
$data = [
'title' => $this->request->getVar('title'),
'price' => $this->request->getVar('price')
];

if(!$this->validate($rules)) return $this->fail($this->validator->getErrors());
$model = new ProductModel();
$model->save($data);
$response = [
'status' => 201,
'error' => null,
'messages' => [
'success' => 'Data Inserted'
]
];
return $this->respondCreated($response);
}
public function update($id=null) {
helper(['form']);
$rules = [
'title' => 'required',
'price' => 'required'
];
$data = [
'title' => $this->request->getVar('title'),
'price' => $this->request->getVar('price')
];

if(!$this->validate($rules)) return $this->fail($this->validator->getErrors());
$model = new ProductModel();
$find = $model->find(['id' => $id]);
if(!$find) return $this->failNotFound('No Data Found');
$model->update($id, $data);

$response = [
'status' => 200,
'error' => null,
'messages' => [
'success' => 'Data updated'
]
];
return $this->respond($response);
}
public function delete($id=null) {
$model = new ProductModel();
$find = $model->find(['id' => $id]);
if(!$find) return $this->failNotFound('No Data Found');
$model->delete($id);

$response = [
'status' => 200,
'error' => null,
'messages' => [
'success' => 'Data deleted'
]
];
return $this->respond($response);
}
}

Route Configuration

In order to work as restful framework, change configuration on the Routes.php file located in the “app/Config” folder. Find and change as below.

// $routes->get('/', 'Home::index');
$routes->resource('products');

CORS (Cross-Origin Resources Sharing) Configuration

It is important to create a filter file for CORS (Cross-Origin Resource Sharing) so that our API can be accessed from outside domain. To create a CORS filter, run the following command in a terminal:

php spark make:filter Cors

CodeIgniter will automatically create a filter file named “Cors.php” in the “app/Filters” folder. Then, open that file and change the code as below:

public function before(RequestInterface $request, $arguments = null)
{
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: X-API-KEY, Origin,X-Requested-With, Content-Type, Accept, Access-Control-Requested-Method, Authorization");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PATCH, PUT, DELETE");
$method = $_SERVER['REQUEST_METHOD'];
if($method == "OPTIONS"){
die();
}
}

Then, open the file “Filters.php” located in the folder “app/Config” and add the filter for cors as below:

public $aliases = [
'csrf' => CSRF::class,
'toolbar' => DebugToolbar::class,
'honeypot' => Honeypot::class,
'cors' => Cors::class,
];
public $globals = [
'before' => [
// 'honeypot',
// 'csrf',
'cors'
],
'after' => [
'toolbar',
// 'honeypot',
],
];

That’s it for backend application. Next, we will create frontend application using react.

Frontend Application with React JS

For frontend application, we will use React JS. Actually, there are many ways to create a React js project. However, the easiest way is to use “create react app”. In order to run the command “create-react-app”, we need to install node.js.

You can download node.js from https://nodejs.org/en/ and install it on your computer. After node.js installed, open Command Prompt or terminal then type the following command to make sure node.js is installed properly.

node -v
npm -v

If everything is good, it will show the version of node and npm as below :

Now open a new terminal and create a new React project by typing the following command.

npx create-react-app frontend

It will create folder “frontend” for our application. Next, go to the “frontend” folder using cd command.

Install react-router-dom, axios, and bulma by typing the following command.

npm install react-router-dom axios bulma

To make sure everything goes well, type the following command to run the project.

npm start

If everything run well, you will display this message below.

Open your browser and type the url http://localhost:3000, you will see react symbol that indicate it is running now.

Create React Components

To create components, you need to do the following tasks:

  • Create a folder called “components” inside “frontend/src” folder.
  • Create component files “ProductList.js”, “AddProduct.js”, and “EditProduct.js” in that folder.
  • Open “ProductList.js” file, and type the following code
import React, { useState, useEffect } from 'react'
import axios from 'axios'
import { Link } from 'react-router-dom'
const ListProduct = () => {
const [products, setProducts] = useState([])
useEffect(() => {
getProducts();
}, [])
const getProducts = async() => {
const products = await axios.get('http://localhost:8080/products')
setProducts(products.data)
}
const deleteProduct = async (id) => {
await axios.delete(`http://localhost:8080/products/${id}`)
getProducts()
}
return (
<div>
<Link to="/add" className="button is-primary mt-5">Add New Item</Link>
<table className="table is-striped is-fullwidth">
<thead>
<tr>
<th width="150">Action</th>
<th>No</th>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{ products.map((product, index) => (
<tr key={product.id}>
<td>
<Link to={`/edit/${product.id}`} className="button is-small is-info">Edit</Link>
&nbsp;
<button onClick={() => deleteProduct(product.id)} className="button is-small is-danger">Delete</button>
</td>
<td>{index + 1}</td>
<td>{product.title}</td>
<td>{product.price}</td>
</tr>
))
}
</tbody>
</table>
</div>
)
};
export default ListProduct

This code will create component ListProduct that consist of link and table element. Link can be used to add new data, while table will display the list of data along with button to edit and delete it.

  • Open “AddProduct.js” file, then type the following code.
import React, { useState } from 'react'
import { useNavigate } from 'react-router-dom'
import axios from 'axios'
const AddProduct = () => {
const [title, setTitle] = useState('')
const [price, setPrice] = useState(0)
const navigate = useNavigate()
const saveProduct = async (e) => {
e.preventDefault();
await axios.post('http://localhost:8080/products', {
title: title,
price: price
})
navigate("/")
}
return (
<div>
<form onSubmit={ saveProduct }>
<div className="field">
<label className="label">Name</label>
<input type="text" className="input" value={ title } onChange={ (e) => setTitle(e.target.value) } placeholder="nama" />
</div>
<div className="field">
<label className="label">Price</label>
<input type="text" className="input" value={ price } onChange={ (e) => setPrice(e.target.value) } placeholder="harga" />
</div>
<div className="field">
<button className="button is-primary">Save</button>&nbsp;
<button className="button is-warning" onClick={() => navigate("/")}>Cancel</button>
</div>
</form>
</div>
)
};
export default AddProduct;

This code will create AddProduct element that generate a form to add new data. There are 2 elements on it, Name and Price. Saveproduct function is a function to send data with post method to the backend application.

  • Open the “EditProduct.js” file, then type the following code.
import React, { useEffect, useState } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
import axios from 'axios'
const EditProduct = () => {
const [title, setTitle] = useState('')
const [price, setPrice] = useState(0)
const navigate = useNavigate()
const { id } = useParams()
const updateProduct = async (e) => {
e.preventDefault();
await axios.post(`http://localhost:8080/products/update/${id}`, {
title: title,
price: price
})
navigate("/")
}
useEffect(() => {
getProductById()
}, [])
const getProductById = async () => {
const response = await axios.get(`http://localhost:8080/products/${id}`);
setTitle(response.data.title)
setPrice(response.data.price)
}
return (
<div>
<form onSubmit={ updateProduct }>
<div className="field">
<label className="label">Nama Barang</label>
<input type="text" className="input" value={ title } onChange={ (e) => setTitle(e.target.value) } placeholder="nama" />
</div>
<div className="field">
<label className="label">Harga</label>
<input type="text" className="input" value={ price } onChange={ (e) => setPrice(e.target.value) } placeholder="harga" />
</div>
<div className="field">
<button className="button is-primary">Update</button>&nbsp;
<button className="button is-warning" onClick={() => navigate("/")}>Cancel</button>
</div>
</form>
</div>
)
};
export default EditProduct;

Resemble with AddProduct component, EditProduct component will generate a form to update data. Every element of this form has predefined value taken from backend application. updateProduct function is function to send data using post method to backend.

Import components to App.js

After create components, we need to register them in App.js. Open the “App.js” file in “frontend/src” folder, then change it as below.

import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import ListProduct from './components/ListProduct';
import AddProduct from './components/AddProduct';
import EditProduct from './components/EditProduct';
import './App.css';
function App() {
return (
<Router>
<div className="container">
<Routes>
<Route exact path="/" element={<ListProduct />} />
<Route path="/add" element={<AddProduct />} />
<Route path="/edit/:id" element={<EditProduct />} />
</Routes>
</div>
</Router>
);
}
export default App;

This code will route everypath to their components. It means that if we access path add for example, it will display AddProduct component. So that for edit url and default url.

Modify index.js

Open “index.js” file in the “frontend/src” folder, then change it to something as below.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import 'bulma/css/bulma.css';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
reportWebVitals();

This file will register App element to index.js as the home url for our application.

Conclusion

Here is the steps to create CRUD application with Codeigniter 4 and React Js. As a backend application codeigniter will serve the request sent by React as a frontend application and return it in JSON form. Thus, now you have an idea of how to create a modern web application that separates the backend and frontend.

--

--