How To Create A Simple REST API in PHP? Step By Step Guide!

Previously, we learned the basics of JavaScript from our JavaScript tutorial for beginners.

Today, we will explore the creation of a simple REST API using the PHP programming language. We will delve into the implementation of CRUD (Create, Read, Update, Delete) functionality and examine the implementation of search and pagination functionality within the context of a REST API.

This knowledge will be crucial as we utilize this API for data retrieval within our JavaScript project. Join us as we embark on this step-by-step guide to building a robust and efficient REST API.

[adinserter block=”33″]

Project Overview

What is REST API?

To define “REST API”, we have to know what is “REST” is and what “API” first. I’ll do my best to explain it in simple terms because REST has a lot of concepts inside of it that could mean a lot of things.

REST stands for “REpresentational State Transfer”. It is a concept or architecture for managing information over the internet. REST concepts are referred to as resources. A representation of a resource must be stateless. It is usually represented by JSON. This post is worth reading: How I Explained REST to My Wife?

API stands for “Application Programming Interface.” It is a set of rules that allows one software application to talk to another. Those “rules” can include the create, read, update and delete operations.

REST API enables your application to cooperate with one or several different applications using REST concepts. If you want to learn more, watch the video below.

Why do we need REST API?

REST API is needed in many applications because this is the lightest way to create, read, update or delete information between different applications over the internet or HTTP protocol. This information is presented to the user instantly, primarily if you use JavaScript to render the data on a webpage.

Where is REST API used?

REST API can be used by any application connecting to the internet. If data from an application can be created, read, updated, or deleted using another, it usually means a REST API is used.

REST API in our tutorials

A REST API is needed for our JavaScript CRUD tutorial. But don’t mind it for now. We will do it one step at a time. You don’t need to learn all of it as well. Just choose what you need to learn.

But one thing is for sure, this source code is good enough and works for our JavaScript tutorials.

Let’s start coding!

Setup the database

Using PhpMyAdmin, create a new api_db database. Yes, api_db is the database name. After that, run the following SQL queries to create new tables with sample data.

Create categories table

CREATE TABLE IF NOT EXISTS `categories` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(256) NOT NULL,
  `description` text NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=19 ;

Dump data for categories table

INSERT INTO `categories` (`id`, `name`, `description`, `created`, `modified`) VALUES
(1, 'Fashion', 'Category for anything related to fashion.', '2014-06-01 00:35:07', '2014-05-30 17:34:33'),
(2, 'Electronics', 'Gadgets, drones and more.', '2014-06-01 00:35:07', '2014-05-30 17:34:33'),
(3, 'Motors', 'Motor sports and more', '2014-06-01 00:35:07', '2014-05-30 17:34:54'),
(5, 'Movies', 'Movie products.', '0000-00-00 00:00:00', '2016-01-08 13:27:26'),
(6, 'Books', 'Kindle books, audio books and more.', '0000-00-00 00:00:00', '2016-01-08 13:27:47'),
(13, 'Sports', 'Drop into new winter gear.', '2016-01-09 02:24:24', '2016-01-09 01:24:24');

Products table

CREATE TABLE IF NOT EXISTS `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL,
  `description` text NOT NULL,
  `price` decimal(10,0) NOT NULL,
  `category_id` int(11) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=65 ;

Dump data for products table

INSERT INTO `products` (`id`, `name`, `description`, `price`, `category_id`, `created`, `modified`) VALUES
(1, 'LG P880 4X HD', 'My first awesome phone!', '336', 3, '2014-06-01 01:12:26', '2014-05-31 17:12:26'),
(2, 'Google Nexus 4', 'The most awesome phone of 2013!', '299', 2, '2014-06-01 01:12:26', '2014-05-31 17:12:26'),
(3, 'Samsung Galaxy S4', 'How about no?', '600', 3, '2014-06-01 01:12:26', '2014-05-31 17:12:26'),
(6, 'Bench Shirt', 'The best shirt!', '29', 1, '2014-06-01 01:12:26', '2014-05-31 02:12:21'),
(7, 'Lenovo Laptop', 'My business partner.', '399', 2, '2014-06-01 01:13:45', '2014-05-31 02:13:39'),
(8, 'Samsung Galaxy Tab 10.1', 'Good tablet.', '259', 2, '2014-06-01 01:14:13', '2014-05-31 02:14:08'),
(9, 'Spalding Watch', 'My sports watch.', '199', 1, '2014-06-01 01:18:36', '2014-05-31 02:18:31'),
(10, 'Sony Smart Watch', 'The coolest smart watch!', '300', 2, '2014-06-06 17:10:01', '2014-06-05 18:09:51'),
(11, 'Huawei Y300', 'For testing purposes.', '100', 2, '2014-06-06 17:11:04', '2014-06-05 18:10:54'),
(12, 'Abercrombie Lake Arnold Shirt', 'Perfect as gift!', '60', 1, '2014-06-06 17:12:21', '2014-06-05 18:12:11'),
(13, 'Abercrombie Allen Brook Shirt', 'Cool red shirt!', '70', 1, '2014-06-06 17:12:59', '2014-06-05 18:12:49'),
(26, 'Another product', 'Awesome product!', '555', 2, '2014-11-22 19:07:34', '2014-11-21 20:07:34'),
(28, 'Wallet', 'You can absolutely use this one!', '799', 6, '2014-12-04 21:12:03', '2014-12-03 22:12:03'),
(31, 'Amanda Waller Shirt', 'New awesome shirt!', '333', 1, '2014-12-13 00:52:54', '2014-12-12 01:52:54'),
(42, 'Nike Shoes for Men', 'Nike Shoes', '12999', 3, '2015-12-12 06:47:08', '2015-12-12 05:47:08'),
(48, 'Bristol Shoes', 'Awesome shoes.', '999', 5, '2016-01-08 06:36:37', '2016-01-08 05:36:37'),
(60, 'Rolex Watch', 'Luxury watch.', '25000', 1, '2016-01-11 15:46:02', '2016-01-11 14:46:02');

Connect to database

The code below shows the database credentials and a method to get a database connection using PDO. If you’re not yet familiar with PDO, please learn from our PHP OOP CRUD Tutorial first.

  • Create api folder. Open api folder.
  • Create config folder. Open config folder.
  • Create a database.php file. Place the following code inside it.
<?php
class Database{

    // specify your own database credentials
    private $host = "localhost";
    private $db_name = "api_db";
    private $username = "root";
    private $password = "";
    public $conn;

    // get the database connection
    public function getConnection(){

        $this->conn = null;

        try{
            $this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password);
            $this->conn->exec("set names utf8");
        }catch(PDOException $exception){
            echo "Connection error: " . $exception->getMessage();
        }

        return $this->conn;
    }
}
?>

Read products

Product object

The code below shows a class named Product with several of its properties. It also shows a constructor method that will accept the database connection.

We will use this class to read data from the database.

  • Open api folder.
  • Create objects folder.
  • Open objects folder.
  • Create product.php file.
  • Place the following code inside it.
<?php
class Product{

    // database connection and table name
    private $conn;
    private $table_name = "products";

    // object properties
    public $id;
    public $name;
    public $description;
    public $price;
    public $category_id;
    public $category_name;
    public $created;

    // constructor with $db as database connection
    public function __construct($db){
        $this->conn = $db;
    }
}
?>

Create file to read products

The code below shows headers about who can read this file and which type of content it will return.

In this case, our read.php the file can be read by anyone (asterisk * means all) and will return a data in JSON format.

  • Open api folder.
  • Create product folder.
  • Open product folder.
  • Create read.php file.
  • Place the following code inside it.
<?php
// required headers
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");

// database connection will be here

Connect to database and products table

In the code below, we included the database.php and product.php files. These are the files we created earlier.

We need to use the getConnection() method of the Database class to get a database connection. We pass this connection to the Product class.

Replace of // database connection will be here comment of read.php file with the following code.

// include database and object files
include_once '../config/database.php';
include_once '../objects/product.php';

// instantiate database and product object
$database = new Database();
$db = $database->getConnection();

// initialize object
$product = new Product($db);

// read products will be here

Read products from the database

In the code below, we used the read() method of Product class to read data from the database. Through the $num variable, we check if there are records found.

If there are records found, we loop through it using the while loop, add each record to the $products_arr array, set a 200 OK response code and show it to the user in JSON format.

Replace of // read products will be here comment of read.php file with the following code.

// query products
$stmt = $product->read();
$num = $stmt->rowCount();

// check if more than 0 record found
if($num>0){

    // products array
    $products_arr=array();
    $products_arr["records"]=array();

    // retrieve our table contents
    // fetch() is faster than fetchAll()
    // http://stackoverflow.com/questions/2770630/pdofetchall-vs-pdofetch-in-a-loop
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
        // extract row
        // this will make $row['name'] to
        // just $name only
        extract($row);

        $product_item=array(
            "id" => $id,
            "name" => $name,
            "description" => html_entity_decode($description),
            "price" => $price,
            "category_id" => $category_id,
            "category_name" => $category_name
        );

        array_push($products_arr["records"], $product_item);
    }

    // set response code - 200 OK
    http_response_code(200);

    // show products data in json format
    echo json_encode($products_arr);
}

// no products found will be here

Add product “read()” method

We used the read() method in the previous section but it does not exist yet in the Product class. We need to add this read() method. The code below shows the query to get records from the database.

  • Open objects folder.
  • Open product.php file.
  • Place the following code inside the Product class.
  • To make sure you added it correctly, place the code before the last closing curly brace.
// read products
function read(){

    // select all query
    $query = "SELECT
                c.name as category_name, p.id, p.name, p.description, p.price, p.category_id, p.created
            FROM
                " . $this->table_name . " p
                LEFT JOIN
                    categories c
                        ON p.category_id = c.id
            ORDER BY
                p.created DESC";

    // prepare query statement
    $stmt = $this->conn->prepare($query);

    // execute query
    $stmt->execute();

    return $stmt;
}

Tell the user no products found

If the $num variable has a value of zero or negative, it means there are no records returned from the database. We need to tell the user about this.

On the code below, we set the response code to 404 - Not found and a message that says No products found.

Replace of // no products found will be here comment of read.php file with the following code.

else{

    // set response code - 404 Not found
    http_response_code(404);

    // tell the user no products found
    echo json_encode(
        array("message" => "No products found.")
    );
}

Output

You need to use POSTMAN to test our API. Download your version of POSTMAN here.

Launch POSTMAN. Enter the following as the request URL.

http://localhost/api/product/read.php

Click the blue “Send” button.

  • Output if there are product data.
  • Output if there are no product data.

Create Product

Create create.php file

  • Open product folder.
  • Create a new create.php file.
  • Open that file and put the following code inside it.
<?php
// required headers
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

// get database connection
include_once '../config/database.php';

// instantiate product object
include_once '../objects/product.php';

$database = new Database();
$db = $database->getConnection();

$product = new Product($db);

// get posted data
$data = json_decode(file_get_contents("php://input"));

// make sure data is not empty
if(
    !empty($data->name) &&
    !empty($data->price) &&
    !empty($data->description) &&
    !empty($data->category_id)
){

    // set product property values
    $product->name = $data->name;
    $product->price = $data->price;
    $product->description = $data->description;
    $product->category_id = $data->category_id;
    $product->created = date('Y-m-d H:i:s');

    // create the product
    if($product->create()){

        // set response code - 201 created
        http_response_code(201);

        // tell the user
        echo json_encode(array("message" => "Product was created."));
    }

    // if unable to create the product, tell the user
    else{

        // set response code - 503 service unavailable
        http_response_code(503);

        // tell the user
        echo json_encode(array("message" => "Unable to create product."));
    }
}

// tell the user data is incomplete
else{

    // set response code - 400 bad request
    http_response_code(400);

    // tell the user
    echo json_encode(array("message" => "Unable to create product. Data is incomplete."));
}
?>

Product create() method

  • Open objects folder.
  • Open product.php file.
  • The previous section will not work without the following code inside the Product (objects/product.php) class.
// create product
function create(){

    // query to insert record
    $query = "INSERT INTO
                " . $this->table_name . "
            SET
                name=:name, price=:price, description=:description, category_id=:category_id, created=:created";

    // prepare query
    $stmt = $this->conn->prepare($query);

    // sanitize
    $this->name=htmlspecialchars(strip_tags($this->name));
    $this->price=htmlspecialchars(strip_tags($this->price));
    $this->description=htmlspecialchars(strip_tags($this->description));
    $this->category_id=htmlspecialchars(strip_tags($this->category_id));
    $this->created=htmlspecialchars(strip_tags($this->created));

    // bind values
    $stmt->bindParam(":name", $this->name);
    $stmt->bindParam(":price", $this->price);
    $stmt->bindParam(":description", $this->description);
    $stmt->bindParam(":category_id", $this->category_id);
    $stmt->bindParam(":created", $this->created);

    // execute query
    if($stmt->execute()){
        return true;
    }

    return false;

}

Output

To test for the successful creation of a product, open POSTMAN. Enter the following as the request URL

http://localhost/api/product/create.php
  • Click the “Body” tab.
  • Click “raw”.
  • Enter this JSON value:
{
    "name" : "Amazing Pillow 2.0",
    "price" : "199",
    "description" : "The best pillow for amazing programmers.",
    "category_id" : 2,
    "created" : "2018-06-01 00:35:07"
}
  • It should look like this:
  • If the system is unable to create the product, it should look like this:
  • If the sent data is incomplete, for example, it is missing the price data, the output should look like this:

Read One Product

Create read_one.php file

  • Open product folder.
  • Create a w read_one.php file.
  • Open that file and put the following code.
<?php
// required headers
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: access");
header("Access-Control-Allow-Methods: GET");
header("Access-Control-Allow-Credentials: true");
header('Content-Type: application/json');

// include database and object files
include_once '../config/database.php';
include_once '../objects/product.php';

// get database connection
$database = new Database();
$db = $database->getConnection();

// prepare product object
$product = new Product($db);

// set ID property of record to read
$product->id = isset($_GET['id']) ? $_GET['id'] : die();

// read the details of product to be edited
$product->readOne();

if($product->name!=null){
    // create array
    $product_arr = array(
        "id" =>  $product->id,
        "name" => $product->name,
        "description" => $product->description,
        "price" => $product->price,
        "category_id" => $product->category_id,
        "category_name" => $product->category_name

    );

    // set response code - 200 OK
    http_response_code(200);

    // make it json format
    echo json_encode($product_arr);
}

else{
    // set response code - 404 Not found
    http_response_code(404);

    // tell the user product does not exist
    echo json_encode(array("message" => "Product does not exist."));
}
?>

Product readOne() method

  • Open objects folder.
  • Open product.php file.
  • The previous section will not work without the following code inside the Product class.
// used when filling up the update product form
function readOne(){

    // query to read single record
    $query = "SELECT
                c.name as category_name, p.id, p.name, p.description, p.price, p.category_id, p.created
            FROM
                " . $this->table_name . " p
                LEFT JOIN
                    categories c
                        ON p.category_id = c.id
            WHERE
                p.id = ?
            LIMIT
                0,1";

    // prepare query statement
    $stmt = $this->conn->prepare( $query );

    // bind id of product to be updated
    $stmt->bindParam(1, $this->id);

    // execute query
    $stmt->execute();

    // get retrieved row
    $row = $stmt->fetch(PDO::FETCH_ASSOC);

    // set values to object properties
    $this->name = $row['name'];
    $this->price = $row['price'];
    $this->description = $row['description'];
    $this->category_id = $row['category_id'];
    $this->category_name = $row['category_name'];
}

Output

  • First, we will test for a product that exists. Open POSTMAN. Enter the following as the request URL. Click the blue “Send” button.
http://localhost/api/product/read_one.php?id=60
  • Next, we will test for a product that does not exist. Enter the following as the request URL. Click the blue “Send” button.
http://localhost/api/product/read_one.php?id=999

Update product

Create “update.php” file

  • Open product folder.
  • Create a new update.php file.
  • Open that file and put the following code inside it.
<?php
// required headers
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

// include database and object files
include_once '../config/database.php';
include_once '../objects/product.php';

// get database connection
$database = new Database();
$db = $database->getConnection();

// prepare product object
$product = new Product($db);

// get id of product to be edited
$data = json_decode(file_get_contents("php://input"));

// set ID property of product to be edited
$product->id = $data->id;

// set product property values
$product->name = $data->name;
$product->price = $data->price;
$product->description = $data->description;
$product->category_id = $data->category_id;

// update the product
if($product->update()){

    // set response code - 200 ok
    http_response_code(200);

    // tell the user
    echo json_encode(array("message" => "Product was updated."));
}

// if unable to update the product, tell the user
else{

    // set response code - 503 service unavailable
    http_response_code(503);

    // tell the user
    echo json_encode(array("message" => "Unable to update product."));
}
?>

Product update() method

  • Open objects folder.
  • Open product.php file.
  • The previous section will not work without the following code inside the Product class.
// update the product
function update(){

    // update query
    $query = "UPDATE
                " . $this->table_name . "
            SET
                name = :name,
                price = :price,
                description = :description,
                category_id = :category_id
            WHERE
                id = :id";

    // prepare query statement
    $stmt = $this->conn->prepare($query);

    // sanitize
    $this->name=htmlspecialchars(strip_tags($this->name));
    $this->price=htmlspecialchars(strip_tags($this->price));
    $this->description=htmlspecialchars(strip_tags($this->description));
    $this->category_id=htmlspecialchars(strip_tags($this->category_id));
    $this->id=htmlspecialchars(strip_tags($this->id));

    // bind new values
    $stmt->bindParam(':name', $this->name);
    $stmt->bindParam(':price', $this->price);
    $stmt->bindParam(':description', $this->description);
    $stmt->bindParam(':category_id', $this->category_id);
    $stmt->bindParam(':id', $this->id);

    // execute the query
    if($stmt->execute()){
        return true;
    }

    return false;
}

Output

Open POSTMAN. Enter the following as the request URL.

http://localhost/api/product/update.php
  • Click the “Body” tab.
  • Click “raw”.
  • Enter the following JSON value.
  • Make sure the ID exists in your database.
  • Click the blue “Send” button.
{
    "id" : "106",
    "name" : "Amazing Pillow 3.0",
    "price" : "255",
    "description" : "The best pillow for amazing programmers.",
    "category_id" : 2,
    "created" : "2018-08-01 00:35:07"
}

The product ID 106, is just an example. You need to specify a product ID that exists in your database.

If you specify an ID that does not exist in the database, it might still say that the product was updated. It does not update anything on the database but the query was executed successfully without any syntax errors.

To prevent this, you need an extra validation where you check if an ID exists in the database. This feature is not yet part of our tutorial.

  • If updating a product is successful, it should look like this:
  • If the system fails to update the product, the output will look like this:

Delete Product

Create “delete.php” file

  • Open product folder.
  • Create new delete.php file.
  • Open that file and put the following code inside it.
<?php
// required headers
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

// include database and object file
include_once '../config/database.php';
include_once '../objects/product.php';

// get database connection
$database = new Database();
$db = $database->getConnection();

// prepare product object
$product = new Product($db);

// get product id
$data = json_decode(file_get_contents("php://input"));

// set product id to be deleted
$product->id = $data->id;

// delete the product
if($product->delete()){

    // set response code - 200 ok
    http_response_code(200);

    // tell the user
    echo json_encode(array("message" => "Product was deleted."));
}

// if unable to delete the product
else{

    // set response code - 503 service unavailable
    http_response_code(503);

    // tell the user
    echo json_encode(array("message" => "Unable to delete product."));
}
?>

Product delete() method

  • Open objects folder.
  • Open product.php file.
  • The previous section will not work without the following code inside the Product class.
// delete the product
function delete(){

    // delete query
    $query = "DELETE FROM " . $this->table_name . " WHERE id = ?";

    // prepare query
    $stmt = $this->conn->prepare($query);

    // sanitize
    $this->id=htmlspecialchars(strip_tags($this->id));

    // bind id of record to delete
    $stmt->bindParam(1, $this->id);

    // execute query
    if($stmt->execute()){
        return true;
    }

    return false;
}

Output

Open POSTMAN. Enter the following as the request URL.

http://localhost/api/product/delete.php
  • Click the “Body” tab.
  • Click “raw”.
  • Enter the following JSON value.
  • Make sure the ID exists in your database.
  • Click the blue “Send” button.
{
    "id" : "106"
}
  • If a product was successfully deleted, it should look like this:
  • If the system fails to delete the product, the output will look like this:

Search Products

Create “search.php” file

  • Open product folder.
  • Create a search.php file.
  • Open that file and place the following code.
<?php
// required headers
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");

// include database and object files
include_once '../config/core.php';
include_once '../config/database.php';
include_once '../objects/product.php';

// instantiate database and product object
$database = new Database();
$db = $database->getConnection();

// initialize object
$product = new Product($db);

// get keywords
$keywords=isset($_GET["s"]) ? $_GET["s"] : "";

// query products
$stmt = $product->search($keywords);
$num = $stmt->rowCount();

// check if more than 0 record found
if($num>0){

    // products array
    $products_arr=array();
    $products_arr["records"]=array();

    // retrieve our table contents
    // fetch() is faster than fetchAll()
    // http://stackoverflow.com/questions/2770630/pdofetchall-vs-pdofetch-in-a-loop
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
        // extract row
        // this will make $row['name'] to
        // just $name only
        extract($row);

        $product_item=array(
            "id" => $id,
            "name" => $name,
            "description" => html_entity_decode($description),
            "price" => $price,
            "category_id" => $category_id,
            "category_name" => $category_name
        );

        array_push($products_arr["records"], $product_item);
    }

    // set response code - 200 OK
    http_response_code(200);

    // show products data
    echo json_encode($products_arr);
}

else{
    // set response code - 404 Not found
    http_response_code(404);

    // tell the user no products found
    echo json_encode(
        array("message" => "No products found.")
    );
}
?>

Create search() method

  • Open objects folder.
  • Open product.php file.
  • Add the following search() method.
// search products
function search($keywords){

    // select all query
    $query = "SELECT
                c.name as category_name, p.id, p.name, p.description, p.price, p.category_id, p.created
            FROM
                " . $this->table_name . " p
                LEFT JOIN
                    categories c
                        ON p.category_id = c.id
            WHERE
                p.name LIKE ? OR p.description LIKE ? OR c.name LIKE ?
            ORDER BY
                p.created DESC";

    // prepare query statement
    $stmt = $this->conn->prepare($query);

    // sanitize
    $keywords=htmlspecialchars(strip_tags($keywords));
    $keywords = "%{$keywords}%";

    // bind
    $stmt->bindParam(1, $keywords);
    $stmt->bindParam(2, $keywords);
    $stmt->bindParam(3, $keywords);

    // execute query
    $stmt->execute();

    return $stmt;
}

Output

Open POSTMAN. Enter the following as the request URL.

http://localhost/api/product/search.php?s=shirt

Click the blue “Send” button.

  • If there was a product found, it should look like this:
  • If there are no products found, the output will look like this:

Paginate Products

Create “read_paging.php” file

  • Open product folder.
  • Create read_paging.php file.
<?php
// required headers
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");

// include database and object files
include_once '../config/core.php';
include_once '../shared/utilities.php';
include_once '../config/database.php';
include_once '../objects/product.php';

// utilities
$utilities = new Utilities();

// instantiate database and product object
$database = new Database();
$db = $database->getConnection();

// initialize object
$product = new Product($db);

// query products
$stmt = $product->readPaging($from_record_num, $records_per_page);
$num = $stmt->rowCount();

// check if more than 0 record found
if($num>0){

    // products array
    $products_arr=array();
    $products_arr["records"]=array();
    $products_arr["paging"]=array();

    // retrieve our table contents
    // fetch() is faster than fetchAll()
    // http://stackoverflow.com/questions/2770630/pdofetchall-vs-pdofetch-in-a-loop
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
        // extract row
        // this will make $row['name'] to
        // just $name only
        extract($row);

        $product_item=array(
            "id" => $id,
            "name" => $name,
            "description" => html_entity_decode($description),
            "price" => $price,
            "category_id" => $category_id,
            "category_name" => $category_name
        );

        array_push($products_arr["records"], $product_item);
    }


    // include paging
    $total_rows=$product->count();
    $page_url="{$home_url}product/read_paging.php?";
    $paging=$utilities->getPaging($page, $total_rows, $records_per_page, $page_url);
    $products_arr["paging"]=$paging;

    // set response code - 200 OK
    http_response_code(200);

    // make it json format
    echo json_encode($products_arr);
}

else{

    // set response code - 404 Not found
    http_response_code(404);

    // tell the user products does not exist
    echo json_encode(
        array("message" => "No products found.")
    );
}
?>

Create “core.php” file

This file holds our core configuration like the home URL and pagination variables.

  • Open the config folder.
  • Create core.php file.
  • Open core.php file.
  • Place the following code.
<?php
// show error reporting
ini_set('display_errors', 1);
error_reporting(E_ALL);

// home page url
$home_url="http://localhost/api/";

// page given in URL parameter, default page is one
$page = isset($_GET['page']) ? $_GET['page'] : 1;

// set number of records per page
$records_per_page = 5;

// calculate for the query LIMIT clause
$from_record_num = ($records_per_page * $page) - $records_per_page;
?>

Create “readPaging()” method

  • Open objects folder.
  • Open product.php file.
  • Add the following method inside the product class.
  • This method will return a list of records limited to what we set in $records_per_page of the previous section.
// read products with pagination
public function readPaging($from_record_num, $records_per_page){

    // select query
    $query = "SELECT
                c.name as category_name, p.id, p.name, p.description, p.price, p.category_id, p.created
            FROM
                " . $this->table_name . " p
                LEFT JOIN
                    categories c
                        ON p.category_id = c.id
            ORDER BY p.created DESC
            LIMIT ?, ?";

    // prepare query statement
    $stmt = $this->conn->prepare( $query );

    // bind variable values
    $stmt->bindParam(1, $from_record_num, PDO::PARAM_INT);
    $stmt->bindParam(2, $records_per_page, PDO::PARAM_INT);

    // execute query
    $stmt->execute();

    // return values from database
    return $stmt;
}

Create “count()” method

Still in the product class (product.php file), add the following method. The total rows are needed to build the pagination array. It is included in the ‘paging’ computation.

// used for paging products
public function count(){
    $query = "SELECT COUNT(*) as total_rows FROM " . $this->table_name . "";

    $stmt = $this->conn->prepare( $query );
    $stmt->execute();
    $row = $stmt->fetch(PDO::FETCH_ASSOC);

    return $row['total_rows'];
}

Get “paging” array

  • Create shared folder.
  • Open shared folder.
  • Create utilities.php file.
  • Open utilities.php file and place the following code.
<?php
class Utilities{

    public function getPaging($page, $total_rows, $records_per_page, $page_url){

        // paging array
        $paging_arr=array();

        // button for first page
        $paging_arr["first"] = $page>1 ? "{$page_url}page=1" : "";

        // count all products in the database to calculate total pages
        $total_pages = ceil($total_rows / $records_per_page);

        // range of links to show
        $range = 2;

        // display links to 'range of pages' around 'current page'
        $initial_num = $page - $range;
        $condition_limit_num = ($page + $range)  + 1;

        $paging_arr['pages']=array();
        $page_count=0;

        for($x=$initial_num; $x<$condition_limit_num; $x++){
            // be sure '$x is greater than 0' AND 'less than or equal to the $total_pages'
            if(($x > 0) && ($x <= $total_pages)){
                $paging_arr['pages'][$page_count]["page"]=$x;
                $paging_arr['pages'][$page_count]["url"]="{$page_url}page={$x}";
                $paging_arr['pages'][$page_count]["current_page"] = $x==$page ? "yes" : "no";

                $page_count++;
            }
        }

        // button for last page
        $paging_arr["last"] = $page<$total_pages ? "{$page_url}page={$total_pages}" : "";

        // json format
        return $paging_arr;
    }

}
?>

Output

Open POSTMAN. Enter the following as the request URL.

http://localhost/api/product/read_paging.php

Click the blue “Send” button.

  • If there are products found, scroll down to see the paging node. It should look like this:
  • If there are no products found, the output will look like this:

Read Categories

Create “category.php” file

  • Open objects folder.
  • Create new category.php file.
  • Place the following code inside the category.php file.
<?php
class Category{

    // database connection and table name
    private $conn;
    private $table_name = "categories";

    // object properties
    public $id;
    public $name;
    public $description;
    public $created;

    public function __construct($db){
        $this->conn = $db;
    }

    // used by select drop-down list
    public function readAll(){
        //select all data
        $query = "SELECT
                    id, name, description
                FROM
                    " . $this->table_name . "
                ORDER BY
                    name";

        $stmt = $this->conn->prepare( $query );
        $stmt->execute();

        return $stmt;
    }
}
?>

Create “read.php” file

  • Create new category folder.
  • Open that folder and create new read.php file inside it.
  • Open read.php file and place the following code.
<?php
// required header
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");

// include database and object files
include_once '../config/database.php';
include_once '../objects/category.php';

// instantiate database and category object
$database = new Database();
$db = $database->getConnection();

// initialize object
$category = new Category($db);

// query categorys
$stmt = $category->read();
$num = $stmt->rowCount();

// check if more than 0 record found
if($num>0){

    // products array
    $categories_arr=array();
    $categories_arr["records"]=array();

    // retrieve our table contents
    // fetch() is faster than fetchAll()
    // http://stackoverflow.com/questions/2770630/pdofetchall-vs-pdofetch-in-a-loop
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
        // extract row
        // this will make $row['name'] to
        // just $name only
        extract($row);

        $category_item=array(
            "id" => $id,
            "name" => $name,
            "description" => html_entity_decode($description)
        );

        array_push($categories_arr["records"], $category_item);
    }

    // set response code - 200 OK
    http_response_code(200);

    // show categories data in json format
    echo json_encode($categories_arr);
}

else{

    // set response code - 404 Not found
    http_response_code(404);

    // tell the user no categories found
    echo json_encode(
        array("message" => "No categories found.")
    );
}
?>

Add Category “read()” method

  • Open objects folder.
  • Open category.php file.
  • The previous section’s code will not work without the following code inside the category.php file.
  • Add the following method inside the Category class.
// used by select drop-down list
public function read(){

    //select all data
    $query = "SELECT
                id, name, description
            FROM
                " . $this->table_name . "
            ORDER BY
                name";

    $stmt = $this->conn->prepare( $query );
    $stmt->execute();

    return $stmt;
}

Output

Open POSTMAN. Enter the following as the request URL.

http://localhost/api/category/read.php

Click the blue “Send” button.

  • If there are categories found, it should look like this:
  • If there are no categories found, the output will look like this:

Download Source Codes

Choose your download

FEATURESBASICPRO
Create product
Read products
Read one product
Update product
Delete product
Search products
Paginate products
Read categories
Delete selected product
Export product CSV
Read products by category
Search products with pagination
Create category
Read categories
Read one category
Update category
Delete category
Search categories
Paginate categories
Delete selected categories
Export categories CSV
Search categories with pagination
Use the buttons below to download. ↓BASICPRO

What’s Next?

Next, we will learn how to use this API with a user interface made with JavaScript. Let’s learn our JavaScript CRUD tutorial.

[adinserter block=”3″]


Comments

187 responses to “How To Create A Simple REST API in PHP? Step By Step Guide!”

  1. cebuanoninoy Avatar
    cebuanoninoy

    Hello Mike. Thank you for this wonderful Tutorial. But where could you find the core.php that you mentioned or ‘include’ in read.php? It’s not part of the code above under config folder.

    1. Hello @cebuanoninoy , you’re welcome!

      Thanks for bringing this to my attention. I just updated the post and added the core.php code in section 10.2 above.

      1. Saurabh Upadhyay Avatar
        Saurabh Upadhyay

        http://localhost/api/product/read.php
        undefined
        unexpected end of data at line 1 column 1
        please help anyone

        1. Hi @disqus_jHq6n81WbA, it looks like you missed a closing bracket. Would you show us the full error message?

        2. Abdelaal Atif Avatar
          Abdelaal Atif

          got same issue kinda
          SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
          Solved it thanks for the extra-ordinary tuts!

          1. Hi @abdelaalatif, I’m unable to replicate the issue. Would you tell us how did you solved it?

  2. Ashish Maharjan Avatar
    Ashish Maharjan

    Hi, Mike. Thank you so much a very easy and well formatted tutorial. I think there’s a function “count()” missing in “Product” class which is calling from “read_paging.php” class. Could you please add that function? Thanks.

    1. Hi @ashish_maharjan , thanks for the kind words!

      I just added the “count()” method in section 10.4 above.

  3. Vito Zgonik Avatar
    Vito Zgonik

    Hi, great tutorial. I am getting Fatal error: Call to undefined method Product::count() in read_paging.php – any ideas? Thanks

    1. Hello @vitozgonik , this error can be solved by adding the “count()” method in section 10.4 above.

      1. Vito Zgonik Avatar
        Vito Zgonik

        Thanks so much, I must have missed this out by accident 🙂

  4. Zuda Madhara Avatar
    Zuda Madhara

    Hi Mike, Great stuff. However, output for read_one.php populates only the id as shown below:
    {
    id: “2”,
    name: null,
    description: null,
    price: null,
    category_id: null,
    category_name: null
    }

    Could there be something wrong?

    ====Ignore me … it was due to bad data in my database!!===

    1. Hello @zudamadhara , I see, thanks for your comment and sharing your solution!

  5. Mauro Rojas Avatar
    Mauro Rojas

    thanks, excelent tutorial!

    1. You’re welcome @disqus_MD8hPEDvks ! Thanks for the kind words as well.

      Please share our site to one of your friends or subscribe for more excellent tutorials, use: https://www.codeofaninja.com/subscribe

  6. Nikhil Khandave Avatar
    Nikhil Khandave

    Hello Mike, great tutorial, learned so much from your tutorials, i hav just started PHP and angular js, i am getting error from this line

    $product->id = isset($_GET[‘id’]) ? $_GET[‘id’] : die(“no record found”);

    it displays “no record found”,
    could there be something wrong, becuase when i hard code the
    product->id = 1,
    it shows the result. and
    one more thing how $_GET[‘id’] works ?

    Thanks in advace

    1. Hi @nikhilkhandave , glad to hear you’re learning so much from our tutorials!

      About your question, $_GET[‘id’] will work if you pass “id” as parameter in your URL. For example:

      http://localhost/product/read_one.php?id=10

      1. Nikhil Khandave Avatar
        Nikhil Khandave

        Thanks , it worked

      2. Masresha Tsegaye A.K.A Mati Avatar
        Masresha Tsegaye A.K.A Mati

        Hey Mike, What if i want to read only one product only by passing this “id”:”the id value”. Like when we delete a product we pass the id and its value. I dont want to pass the question mark sign and write it on my URL. https://uploads.disquscdn.com/images/8e947c978957cf13ca11585f0fc8b689bdd8811dcdad0c126b1c8cee2846ddd9.png

        1. Hi @masreshatsegayeakamati, you can use your JavaScript application to do a POST request and then your PHP code must accept a POST request as well. You can access the “id” or any parameter this way.

  7. Nonsono Statoio Avatar
    Nonsono Statoio

    Hi Mike, very interesting tut!
    My console give me a problem here:

    Parse error: syntax error, unexpected ‘public’ (T_PUBLIC) in C:xampphtdocsAngular-restconfigcore.php on line 19

    maybe is beacause the public function must be declared inside a class?

    1. Hi @nonsono_statoio , yes, all functions must be inside a class.

  8. Laurence Hudson Montgomery Avatar
    Laurence Hudson Montgomery

    Great tutorial, love the use of ‘output’ checks. I stumbled a bit at 6.3 because of hard to read details in the provided graphic not found in the text instructions:

    “If you develop on localhost and will run the read.php file using this URL: http://localhost/api/product/read_one.php
    You will see an output like this…”

    You might want to indicate in your text that an id value has to be passed :

    “If you develop on localhost and will run the read.php file using this URL: http://localhost/api/product/read_one.php?id=60
    You will see an output like this…”

    1. Hi @laurencehudsonmontgomery , thanks for letting me know about the graphic being hard to read at your end.

      I just replaced it with a bigger image. We added more description as well. Let us know if it is more readable now.

  9. welly boang Avatar
    welly boang

    Hi Mike, great tutorial…

    how execute POST method ?
    such as create () method.. thanks

    1. Hi @wellyboang , as I said at the end of section 5.2, I highly recommend completing this whole tutorial first. But if you want to test the code above, you have to use our JavaScript code.

      The reason is our JavaScript code is designed to work with this REST API. Please complete one of our JavaScript programming tutorials. See https://www.codeofaninja.com/javascript-programming-tutorials

      This same concept applies to our “update” and “delete” code.

  10. Anshul Mahipal Avatar
    Anshul Mahipal

    Great Tutorial Mike. But there is issue I just want to use that code with postman talking about create() it create the product but with empty field will you please tell me reason.

    1. You can only use our JavaScript code to test this API. Please read my notes after section 5.2 code above.

  11. Anshul Mahipal Avatar
    Anshul Mahipal

    Helle Mike, great tutorial
    But I stuck on single think that I want to post data using postman it create a record with empty field so please help me

    1. Hi @anshulmahipal , please read my notes after section 5.2 code.

      1. Anshul Mahipal Avatar
        Anshul Mahipal

        after 5.2 you put these lines
        Please complete one of our JavaScript programming tutorials. This same concept applies to our “update” and “delete” code.
        But I am Stuck on what javascript I Use because i don’t use HTML forms

        1. If you can’t learn one of our JavaScript tutorials, our tutorial above is not right for you. You have to learn somewhere else.

  12. Nii Quartey Avatar
    Nii Quartey

    how do i access this api via url? I am new to using API’s and REST especially. Or how do i access it on other platforms. I need help please. It’s a very good tutorial and i liked the step by step process

    1. Hi @niiquartey , if you want your API to be accessible in other platforms, you should upload it on your web server and access it via your domain or temporary URL.

  13. For the readers curiosity
    About 4.4 the JSON viewer is an embedded feature in Firefox. https://uploads.disquscdn.com/images/099775e74f8ca92fb2197d63b3d5581c34ac029893ea291be5a4ed47db03960a.png

    1. Thanks for sharing @Corsari! This will help Firefox users. On the screenshot, I saw that you bookmarked our site, thank you for that as well!

      1. 🙂
        I’d be surprised that someone that would land on Code Of A Ninja website would not bookmark it…

    2. Petarr Nonkovic Avatar
      Petarr Nonkovic

      Or just pass second argument to json_enconde($products_arr, JSON_PRETTY_PRINT) , no extension needed. Or whatever you like, CHEERS ALL!
      Great tutorial by the way!!

      1. I didn’t know this, thanks for this tip @petarrnonkovic ! Glad you liked our tutorial!

      2. hanifeoglu Avatar
        hanifeoglu

        @petarrnonkovic thank for trick i m more like just your words mistake “json_enconde” -> “json_encode” result here : echo json_encode($products_arr, JSON_PRETTY_PRINT);

        1. Thank you guys for the tips! This helps other people using this site. 🙂

  14. small fixes to avoid confusion in newbies like me 🙂
    10.1 Create “read_paging.php” file, no indications on where to create it (we know, in the products folder, but is worth to indicate)
    10.2 states “create config folder”, but it has been already created for the database.php file
    10.3 Create “readPaging()” method, no indications on where to create it (we know, in the product.php file, but is worth to indicate)
    10.4 Create “count()” method, same as 10.3
    11.3 Add Category “read()” method – Open “objects” folder. Create new “category.php” file. But this file already exists

    1. Hi @Corsari , thanks for your detailed corrections! I’ve made the changes on the post above.

      1. Good ! I’m glad you have fixed them.

  15. Hi @Corsari , thanks for visiting our site!

    To answer your question, we used LEFT JOIN to pull the “category name”, without it, we can only pull the “category ID” from the products table and so we have to do another query for the “category name”.

    In other words, using the LEFT JOIN is more efficient because we don’t have to do multiple queries just to get the category name.

    By the way, I love your first comment here. Thanks you for the kind words! I wish you can republish it because I want to answer all your questions from there as well.

    1. Hi Mike
      As you may have noticed, yesterday I was following your tutorials
      (REST API + following one, the “Pure AJAX CRUD”)

      So I have written some comments around the website

      Can you point me to the comment you mean?

      Thank you

  16. Hi @Corsari , your comment above is the one I’m talking about. I don’t know why but I found it in the spam section of Disqus, so I thought you deleted it.

    Anyway, I added section 1.1 to 1.4 on the post. Those are based on your suggestions. Thank you! I hope those explanation and supplementary videos will help you and the others.

  17. Hi Mike
    one kind “issue” with all of this related with the AJAX CRUD js php tutorial that uses this API

    All of this cannot be used in production right? 🙂

    Since e.g. the API is without athentication and all of the js files are easy readable so tehy will unveil the API path and commands

    Do you think to add some security? Or this tutorial is intended for the sole purpose of teach methods? Anyway the security is part of the learn stage 😉

    1. Hi @Corsari , this tutorial is intended for the sole purpose of teaching methods. You are right about the security being part of the learn stage. But that’s another topic and deserves another post. We’ll try to release that kind of tutorial soon.

  18. Roberto Avatar
    Roberto

    Hi Mike.
    I had made the first level but when I run the read.php I get this: {“message”:”No products found.”}.
    I have the tables well done. I had made the change in the table name “products” to “productos” and I don’t receive any warning. Thanks in advance for your comments

    1. Did you change your table name in product.php?

  19. Hi @disqus_8D8jRDxac2, unfortunately we don’t have a tutorial for this yet.

  20. Guido Leijten Avatar
    Guido Leijten

    Hi! It appears you’re missing an else on 8.2 Product delete() method after the if statement under execute query.

    1. Hi @guidoleijten, thanks for your comment! We don’t miss an ‘else’ on section 8.2, the code will work as is. If ‘$stmt->execute()’ is true, the method will return true. It won’t execute the ‘return false’ under it.

      1. Guido Leijten Avatar
        Guido Leijten

        Fair enough! It just looked like it as all the other methods do have it set up with an if/else. I must say that this is an excellent tutorial, I am following a PHP course and this week we had to find out how to build our own API, I searched the net for information on it and I got a vague idea, but after following your tutorial it’s all so clear and it’s amazing how simple it can be! I’ve recommended it to everyone here and my teacher will link to it on his blog as well, because not only is it good for people who want to learn how to create a RESTful API, but it’s also excellent to get a better grasp on OOP as I hear from a lot of people here that they don’t really know when to use classes.

        One other thing, I am using the Chrome extension Advanced REST Client and when trying to PUT/POST I had to utf8_encode the input before json_decode, it appears that others in the comment section did not experience this issue. Easily fixable, but it made me wonder why my code didn’t work without it.

        Anyway, again – great tutorial! Definitely something I’ll keep recommending when I hear someone struggling trying to create their own API.

        1. You’re awesome @guidoleijten! Thank you for your kind words and recommending our site to your friends. Thanks for the tip about utf8_encode as well!

  21. Techbyte Solutions Avatar
    Techbyte Solutions

    header(“Access-Control-Allow-Origin: *”); is not suitable. It allows anyone to allow access to our resources.
    You must provide some access token or Oauth for secure connection. bdw Your tutorial is nice.

    1. Thanks for sharing your thoughts @techbytesolutions! But this tutorial is not for private APIs. As said on the tutorial, this is not yet in its final form as well. You can always extend it to your project’s requirements.

      1. Canh Phong Avatar
        Canh Phong

        thanks

        1. You’re welcome @canhphong! Please share our site to your friends!

  22. Gary Thompson Avatar
    Gary Thompson

    Based on this work I’ve created endpoints in codeigniter for products – just as a learning exercise. https://bitbucket.org/sushiguru/api

    1. Thanks for sharing your work @sushiguru!

  23. Hi @@ninjazhai thanks alot for this applicable tutorial.
    I have a problem in receiving data In the product/create.php file, “php://input” is empty when I send http post request.
    I checked $data = json_decode(file_get_contents(“php://input”)) and also $_POST through these line of codes :
    var_dump($data);
    var_dump($_POST);

    both are empty!
    I tested other enctypes also … http request response is always 200OK but the controller php file (create.php) shows nothing data received.
    can you help me to debug this problem ?

    1. Hi @amin_r, you’re welcome!

      About your question, did you use our JavaScript code with your create.php file? Our script above was only tested with our JavaScript code.

      Try to follow one of our JavaScript tutorials, see https://www.codeofaninja.com/javascript-programming-tutorials

      1. I have the same problem, but the code is the same. Can you help me?

        1. Hi @maxufer, did you try to follow one of our JavaScript tutorials?

      2. UPDATE: We are now using POSTMAN (https://www.getpostman.com/) to test the output of our code above. You don’t have to use any JavaScript code to test it.

        We provided instructions and screenshots on each “Output” section so that the testing will be easier to follow.

    2. atomicnation Avatar
      atomicnation

      Hi, I use Postman for the requests. I was experiencing the same issue: product created, but empty.

      But, if you configure headers to “Content-Type : application/json” it works. I inroduced json raw data for test. Here the example:
      https://uploads.disquscdn.com/images/5fb53c5473d3a8240880bb03ed576a4d338f022357c094d0df9c7d4f96a39301.png https://uploads.disquscdn.com/images/4ee674bbd8f7db323a9940ad20b14072134912ab98b00a9ccafc134cfea5ece6.png https://uploads.disquscdn.com/images/84991f56fffe19b73b721993071546339b5c2ce3e73633b5ad10fbb0fcea0bcb.png

      As you can see previous tests failed till I configured the headers this way!

      1. Hi @atomicnation, thanks for sharing this tip! This one is very useful for Postman users. 🙂

        1. atomicnation Avatar
          atomicnation

          Thanks to you for this brilliant tutorial. By the way, I wonder if you know some place where there is any explanation of the headers and when & how they should be used, referred to REST services. Thanks!

    3. Tarandeep Singh Avatar
      Tarandeep Singh

      use
      $data = (object) $_POST;
      instead of
      $data = json_decode(file_get_contents(“php://input”));

      1. Amazing! Thanks for the tip @disqus_McgLMZMyJN!

    4. UPDATE: We are now using POSTMAN to test the output of our code above. You don’t have to use any JavaScript code to test it.

  24. Utilities class and getPaging method can be static, so you don’t have to instanciate it

    1. Hi @paulo, it can be. But it’s a good practice to make it reusable for other objects.

  25. Besart Emine Cana Avatar
    Besart Emine Cana

    Great tutorial! Thanks

    Have some questions. how can I paginate search results?

    Regards
    Besart

    1. You’re welcome @besarteminecana! You can use the same code pattern we used in section “10.0 Paginate Products”. Or you can download the LEVEL 2 source code above.

      1. Besart Emine Cana Avatar
        Besart Emine Cana

        I made it already, thanks!

  26. Kalana Wijekoon Avatar
    Kalana Wijekoon

    Thank you very much.. this really helped a lot.

    1. You’re welcome @kalanawijekoon! Please share our site to your friends, or subscribe https://www.codeofaninja.com/subscribe Thank you!

  27. Masresha Tsegaye A.K.A Mati Avatar
    Masresha Tsegaye A.K.A Mati

    What a great tutorial. This is what i’ve been looking for days. Thanks!!!!

    1. You’re welcome @masreshatsegayeakamati! I’m glad to save you some time. Thanks for the kind words about our tutorial!

  28. Masresha Tsegaye A.K.A Mati Avatar
    Masresha Tsegaye A.K.A Mati

    Hey Mike,
    Thanks for the tutorial. U just saved me from being dependable from the Back-End guys.
    I have a question. I want to read only one product by sending the id and get a response “the product full info” like we send requests to delete a product. I don’t want to pass the value on the URL. https://uploads.disquscdn.com/images/8e947c978957cf13ca11585f0fc8b689bdd8811dcdad0c126b1c8cee2846ddd9.png

    1. Hi @masreshatsegayeakamati , you can use your JavaScript application to do a POST request and then your PHP code must accept a POST request as well. You can access the “id” or any parameter using this method.

  29. Adriano Giovannini Avatar
    Adriano Giovannini

    Hello,
    first and foremost great tutorial (and great job) but i have a little question:

    In your screen i see the json output formatted, and your browser is chrome. Do you get a formatting that way? I have run both on linux and windows with chrome and firefox but nothing output is always on a line like this.

    {“records”:[{“id”:”4″,”name”:”Books”,”description”:”Kindle books”},{“id”:”2″,”name”:”Electronics”,”description”:”Gadgets, drones and more.”},{“id”:”1″,”name”:”Fashion”,”description”:”Category for anything related to fashion.”},{“id”:”3″,”name”:”Motor”,”description”:”Motor sports and more”},{“id”:”5″,”name”:”Movies”,”description”:”Movie products”},{“id”:”6″,”name”:”Sports”,”description”:”Drop into new winter gear.”}]}

    1. Hi @androidmegasources, thanks for the kind words about our tutorial! As I’ve said above, I’m using a Chrome extension called JSONView to make the JSON data readable in the browser. See https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc

  30. Hi @disqus_jd9M9R6P27, thanks for the kind words about our tutorial! I’m sorry I’m not familiar with sqlsrv. You can check if the $num variable value is correct or greater than 0. Make sure you sample data is sufficient as well.

  31. Hi @disqus_jd9M9R6P27, thanks for sharing your fix, I appreciate it!

  32. Hi @zelkaeddy, thanks for the catch! Sorry for the late reply, I just read your comment. It went to the spam folder. Those type has been fixed now, thanks to you again!

  33. Mohamed Mohamed Avatar
    Mohamed Mohamed

    Hi. May I ask, how do you automatically delete a product as you won’t need to refresh the page?

    1. Hi @disqus_tGEtJxEHOZ, the JavaScript application sends a post request with the “product ID” to the “delete” API. Follow our example here: https://www.codeofaninja.com/2015/06/php-crud-with-ajax-and-oop.html

  34. First, I’m very excited about starting this, because it looks comprehensive enough without getting too far “into the weeds” for someone who needs to work a day job (stuck in a legacy env.) and learn new technologies. Question about the file structure though: Are all the directories to be placed off the docroot, or are the subsequent directories off the ‘api’ directory? Like this:
    ├─ api/
    ├─── config/
    ├────── core.php – file used for core configuration
    ├────── database.php – file used for connecting to the database.
    ├─── objects/
    ├────── product.php – contains properties and methods for “product” database queries.
    ├────── category.php – contains properties and methods for “category” database
    queries.
    ├─── product/
    ├────── create.php – file that will accept posted product data to be saved to database.
    ├────── delete.php – file that will accept a product ID to delete a database record.
    ├────── read.php – file that will output JSON data based from “products” database
    records.
    ├────── read_paging.php – file that will output “products” JSON data with pagination.
    ├────── read_one.php – file that will accept product ID to read a record from the
    database.
    ├────── update.php – file that will accept a product ID to update a database record.
    ├────── search.php – file that will accept keywords parameter to search “products”
    database.
    ├─── category/
    ├────── read.php – file that will output JSON data based from “categories” database
    records.
    ├─── shared/
    ├────── utilities.php – file that will return pagination array.

    1. Hi @HCB, thanks for the kind words! Yes, all the files are inside the “api” directory. You can place your “api” directory in your document root.

  35. Meltito Vagallon Avatar
    Meltito Vagallon

    hi I got an error how to i solve this Fatal error: Uncaught Error: Class ‘Database’ not found in C:xampphtdocsAPI_practiceproductread.php:9

    1. Hi @meltitovagallon, make sure you followed section 3.0 properly.

  36. You’re welcome @disqus_ymPF68U0LP! Thanks for your positive feedback! Please share our site to your friends and subscribe here https://www.codeofaninja.com/subscribe

  37. Rookie Dev Avatar
    Rookie Dev

    Curious as to why you end each file with the php closing tag? Isn’t this generally considered poor practice? http://php.net/manual/en/language.basic-syntax.phptags.php

    1. Hi @Rookie Dev, thanks for visiting our site and sharing your thoughts! The closing PHP tag is not a “bad practice”. The link you sent says it is something that is “preferable” if a file is a pure PHP code.

      I’ve been coding PHP for many years and the closing PHP tag was never a problem. It is just a personal preference. If you think an accidental whitespace is a big deal for you app, then I suggest you don’t use the closing PHP tag.

      1. Rookie Dev Avatar
        Rookie Dev

        Thanks for clarifying! I want to make sure I follow best practices as I learn so that I learn the “correct” way.

  38. Gary Harris Avatar
    Gary Harris

    This is a great guide for developing a REST API! Would it be possible for you guys to put together a simple guide on how to secure the API with OAuth? Or, could you point me to a resource that describes securing an API with OAuth from beginning to end? Thanks!

    1. Thanks for the kind words about our tutorial @Gary Harris! We’ll post our REST API authentication tutorial soon. You can get a lot ideas how to build it here: https://stackoverflow.com/questions/7999295/rest-api-authentication

  39. Rather not Say Avatar
    Rather not Say

    Thanks.

    1. You’re welcome @Rather not Say!

  40. waleed rehan Avatar
    waleed rehan

    Hello
    I am facing this error.

    Fatal error: Using $this when not in object context in E:xampphtdocsAPIview.php on line 6

    Kindly give me solution for this.

    1. Hi @waleedrehan, we don’t have view.php on our tutorial above. Please follow the exact steps on our tutorial above.

  41. waleed rehan Avatar
    waleed rehan

    I am facing this problem

    Fatal error: Call to undefined method Product::read() in E:xampphtdocsAPIselect.php

    1. @waleedrehan, please read my response on your first comment.

  42. So Fabulous Avatar
    So Fabulous

    Me and my classmate are making this api on different laptops. His and mine weren’t able to communicate for some reason. While testing the create method, the only thing that’s being inputted in my database are brackets. Like so https://uploads.disquscdn.com/images/79d4ca780abe3a158ef684be084c6951ca57be16d2a0779e5b437495d62476c3.png
    I did mess with the code a lot, since I was using it for something else, but I wonder if you know why this occurs?

    1. Hi @disqus_7hJqGh3eKk, thank you for using our tutorial! Unfortunately, there’s no way for me to debug your customizations. I highly recommend following the exact tutorial above first. After that, you can do your own customizations on the code. It will be easier since you understand the basics.

  43. maneesha padamata Avatar
    maneesha padamata

    thanks alot 🙂

    1. You’re welcome @maneeshapadamata! Please share our site to your friends and subscribe for free here https://www.codeofaninja.com/subscribe

  44. Lorna Macdonald Avatar
    Lorna Macdonald

    Hi. I’m using this tutorial to help me build my own REST api. But within the search part of my api, I need to be able to search by two parameters. Is this an easy fix in this code? I’m really struggling to figure out how to implement it.

    1. Hi @disqus_fpCGRZwrkb, if you need a feature where you can pass two parameters, you can format the search like this:


      search.php?s=searchkey&s2=searchkey2

      and get the value of s2 like this


      $keywords2=isset($_GET["s2"]) ? $_GET["s2"] : "";

  45. how to remove .php from url in core php?

    1. Hi @sudhir600, if you want the clean URLs, you can make use of .htaccess, here’s a helpful tutorial https://www.codeofaninja.com/2013/04/mod-rewrite-url.html

  46. Tarandeep Singh Avatar
    Tarandeep Singh

    Thanks Man!!
    It helped me lot in learning and making api in php for my project. Thanks again

    1. You’re welcome @disqus_McgLMZMyJN! Please share our site to your friends and subscribe here https://www.codeofaninja.com/subscribe

  47. Am trying to connect to the mysql using PDO but am getting could not find driver error. what do i do?

    1. Hi @Valex, it looks like your development environment is not properly set up. You can try to use XAMPP, here’s a tutorial https://www.codeofaninja.com/2013/06/how-to-run-a-php-script.html

      If you’re working on a remote server, you should contact your web hosting provider to fix it for you.

  48. Hi @tobiasparent, thanks for the kind words and sorry for the late reply, it’s been a crazy couple of monhts for me. Anyway, yes, this is only a good starting point and the article covers only the basic concepts.

    Let me know if I understood your concern correctly. Do you mean you need the code to run with different type of data sources and not rely with PDO? You have a point, we can do that with PHP because it can connect even to an Oracle database.

    Unfortunately, I am not familiar with that situation and my knowledge is currently limited to using PDO. It works with all my projects and my clients are happy with it.

    It sounds like you’re a very knowledgable person and I’d love to learn more from you! Can you point us to better references or resources so that we can improve our tutorial above?

  49. Hi @androidcoban, unfortunately, that’s not part of our tutorial above. We will release a related login tutorial soon, please subscribe so we can let you know. Use this form: https://www.codeofaninja.com/subscribe

  50. You’re welcome @nbatioco! You’re right, you can use the technology to connect lot of applications.

  51. Hi @meindertjorna, I’m glad it works now and thanks for sharing your solution! But do you mean you placed the read() function inside the Product class?

  52. What you did is correct, you said “outside the class Product” in your previous comment where it should be “inside”.

  53. Vivek Gupta Avatar
    Vivek Gupta

    not able to get output of search() function
    i m getting this
    Fatal error: Uncaught Error: Call to undefined method Product::search() in E:xampphtdocsrootfolderapiproductsearch.php:21
    line no. 21// query products
    $stmt = $product->search($keywords);
    $num = $stmt->rowCount();
    plz help

    1. Hi @disqus_VMblFhhH2J, please make sure you added the search() method inside your product class. See the search method in section 9.2 above.

  54. keith Avatar
    keith

    This doesn’t work for a react fetch call – I’ve followed the code exactly, I’m receiving a Json response back when I visit in a browser but (I think) something is wrong with the header where the react code:

    fetch(‘fetch(‘http://localhost/api/product/read_one.php?id=60’)
    .then((response) => response.json())
    .then(json => this.setState({series:json}))

    won’t retrieve the data back

    why is that?
    thanks

    K

    1. Hi @disqus_ywxK8FvPED, I’m unable to replicate the issue, it works on my end. Did you follow our React tutorial? See https://www.codeofaninja.com/2016/07/react-crud-tutorial.html

  55. Hi @gabiluva, why do you need an ID to be returned from the create method?

    1. Gabi Luva Avatar
      Gabi Luva

      Normally I would return the hole json object (or an id) in a spa app or mobile app you need that for any other operations following, show details or link some other object to this new one or delete or I don’t know whatever etc.

      1. Unfortunately, we do not have this feature on our tutorial above. But you can play around with PDO’s lastInsertId(), see https://stackoverflow.com/questions/10680943/pdo-get-the-last-id-inserted

  56. Hi @dave_geez, thanks for your comment. Would you tell us what’s wrong with using MyISAM in the example above?

  57. Hi @dave_geez, first, I’m not forcing anyone to use MyISAM. A person like you who has a lot of time to update or comment on a tutorial can use whatever you want.

    I’m not forcing you to use it. In fact, I updated the tutorial with InnoDB as well.

    I don’t believe my question makes no sense. Some users have had to rely on MyISAM for its full text search capabilities, and in some cases, better performance in applications that didn’t require transactional capabilities.

  58. You’re welcome @savarisagayadevans! Please subscribe for more awesomeness, see: https://www.codeofaninja.com/subscribe

  59. Hi @amit, you can always use the green download button on section 12.0 above.

  60. Thank you for the detailed reply @tobiasparent! We’ll definitely improve the tutorial above with your suggestions in mind. I’m looking forward to the link of your work. I’m glad your appreciate our resource even though it is not perfect. Thank you!

  61. Hi @marceloclopes, thanks for the suggestion! We will definitely release a new tutorial with JSON Web Tokens soon. Please subscribe: https://www.codeofaninja.com/subscribe

  62. Hi @disqus_ky7Wa1JuzZ, thanks for the kind words and suggestion! Postman is very useful, thanks for the tip about it and testing the API with Android as well.

  63. Hi @Husam, would you show use the code of your update.php so we can see what was wrong? How are you testing this?

  64. Hi @pradeep_shekhawat, how are you testing this API? Would you show your code in create.php file?

  65. Rani Somu Avatar
    Rani Somu

    Hi,
    This article is clearly explained each and every step. It is very useful. Thank you for this article.
    I need your guidance to achieve the following requirements.
    1. “SELECT * FROM” . $this->table_name . ” WHERE created between ? and ?;”
    2. “SELECT * FROM” . $this->table_name . ” WHERE created >=? and created<=?;"
    Can you please guide me.
    Thanks in advance.

    1. You’re welcome @ranisomu! What exactly is the guidance you are seeking? Those queries look correct to me.

      If you are asking how to bind values on those queries, I suggest you study the “search” feature on our tutorial here: https://www.codeofaninja.com/2014/06/php-object-oriented-crud-example-oop.html

          1. Rani Somu Avatar
            Rani Somu

            That section deals with LIKE and bind parameter surrounded by %{ }% as such as in mysql. It’s working fine for LIKE.

            In my case, I need to do it for BETWEEN and >=, <=. The step which is used for LIKE doesn't work for my requirement.

            I'm not getting an idea, how to pass the bind parameter for BETWEEN. So only I had attached the screenshots.
            Kindly please refer it and guide me to achieve this.

          2. @ranisomu, sorry, I misunderstood your question. If you need to use the between/and clause, it should look like this:

            SELECT * FROM `table` WHERE `start_date` BETWEEN :start_date AND :end_date

  66. Hi @disqus_rjV2B2Ktib. Unfortunately, our tutorial above is using PDO only. We do not have tutorial for MySQLi.

  67. Steve Janssen Avatar
    Steve Janssen

    how would i update read.php in the API to show only a certain category? e.g. only Electronics

    1. Hi @disqus_jOu97V5UXn, you need to add a “WHERE” clause on your query. It looks like: …WHERE category_id=3 something like that.

  68. Would you paste your whole query? Also, make sure you got records with the category ID you specified.

  69. Hi @disqus_TR7OR7SoPY, if it works on localhost, I think there’s a problem with your database connection. Would you try to show an error message using this inside the “else” statement.

    print_r($this->conn->errorInfo());

  70. Hi @ranisomu, did your query worked on PhpMyAdmin? Based on your query, it should be:


    $start_date="2014-06-01";
    $end_date"2014-06-30";

    I’m not sure why this is not working on your end. Which PHP and MySQL version are you using? Are you debugging locally?

    Yes, the isset method is needed. The code on your searchdate.php is in comments, please remove the double slash.

  71. You can pass the value the same way we pass the data in the “create” operation on our tutorial above.

  72. Hi @maziksolutionsmzk, you can try our Angular tutorial linked on the post above.

  73. Hi @dannchristiangavino, make sure the read() method exists inside your product class.

    1. DANN CHRISTIAN Avatar
      DANN CHRISTIAN

      conn = $db;
      }
      }

      // read products
      function read() {

      // select all query
      $query = “SELECT
      c.name as category_name, p.id, p.name, p.description, p.price, p.category_id, p.created
      FROM
      ” . $this->table_name . ” p
      LEFT JOIN
      categories c
      ON p.category_id = c.id
      ORDER BY
      p.created DESC”;

      // prepare query statement
      $stmt = $this->conn->prepare($query);

      // execute query
      $stmt->execute();

      return $stmt;
      }

      // search products
      function search($keywords){

      // select all query
      $query = “SELECT
      c.name as category_name, p.id, p.name, p.description, p.price, p.category_id, p.created
      FROM
      ” . $this->table_name . ” p
      LEFT JOIN
      categories c
      ON p.category_id = c.id
      WHERE
      p.name LIKE ? OR p.description LIKE ? OR c.name LIKE ?
      ORDER BY
      p.created DESC”;

      // prepare query statement
      $stmt = $this->conn->prepare($query);

      // sanitize
      $keywords=htmlspecialchars(strip_tags($keywords));
      $keywords = “%{$keywords}%”;

      // bind
      $stmt->bindParam(1, $keywords);
      $stmt->bindParam(2, $keywords);
      $stmt->bindParam(3, $keywords);

      // execute query
      $stmt->execute();

      return $stmt;
      }
      ?>

      the only script in my product.php

      1. Hi @dannchristian, the code looks wrong, please follow our tutorial carefully.

        1. DANN CHRISTIAN Avatar
          DANN CHRISTIAN

          i Mike

          How this become wrong it seems i copy paste it to the editor

          1. Hi @dannchristian, please do not just copy and paste the codes from tutorial above. Please understand how it works so you can make it work.

          1. This code looks correct.

  74. Hi @dannchristian make sure the read() method exists inside your product class. Make sure you did not misplace the closing bracket of your product class.

    1. Rani Somu Avatar
      Rani Somu

      Thanks for your suggestion.
      What’s wrong in the following?
      http://localhost/php_mysql/php_api/product/searchdate.php?start_date=2014-06-01&end_date=2014-06-30

      Please correct it.

      1. I can’t correct it because I cannot debug it for you. If you’re using the URL with values, you must use the GET request to get it processed by PHP.

  75. DANN CHRISTIAN Avatar
    DANN CHRISTIAN

    Is there anyway to put token for authentication for this REST API tutorial

  76. Hi @khalid, thanks for the suggestion. We do not have one yet, but we will make it in the future. It will be posted on androidcode.ninja website.

  77. Hi @eugeniesoulange, I’m unable to replicate the issue. Would you show us your read.php code? Please wrap your code in pre and code tag, see an example here: https://help.disqus.com/commenting/what-html-tags-are-allowed-within-comments

  78. Great! Thank you for sharing your solution. I’m sure this is helpful for other people who face the same issue.

  79. Thanks for brining this to my attention. That part is now fixed. I was testing the code for a negative result and I forget to bring it back.

  80. Hi @disqus_qYLv79Vi6b, it looks like your product.php file is not in the correct location. Please take a look at the section 2.0 (file structure) above.

  81. Hi @disqus_UHxBXCcyty, sorry, I did not understand your question. What exactly are you trying to accomplish?

  82. I suggest you test both the options.

  83. Gabriele Avatar
    Gabriele

    Hi, everybody,

    i’m trying this code but I don’t know why I can’t create a product. it return me 503 error code. Any idea?

    1. Hi @disqus_33q8lBEIJf, we are unable to replicate the issue. 503 means service is unavailable, make sure your server is running correctly.

  84. Hi @vincentjonathan, you’re welcome! Yes, I believe it is safe from SQL Injection because we are using prepared statements. About authentication, I suggest you study our tutorial here: https://www.codeofaninja.com/2018/09/rest-api-authentication-example-php-jwt-tutorial.html

  85. Hi @disqus_LGtoUa2quM, would you send a screenshot of your update.php code around line 26-32? Make sure your /objects/product.php was properly set up as well.

  86. Hi @disqus_WgiJsBfCzg, it will work with PHP 5.5+, the code is not tested with postgresql.

  87. Hi @disqus_OT9R0KMI6s, did you try to run your code on localhost?

  88. Hi @iqbalprashiasuhardi, would you show us a screenshot of your read.php code?

  89. This is too complex.
    I want to look at how to implement endpoints with PHP versus Express or Rails or Djanngo.
    Most of this is about database and retrieving data. It’s a lot of stuff that skips an overview of essential.
    I give it an F.

  90. 0000magda0000 Avatar
    0000magda0000

    I am new to PHP and didn’t know that I had to initialize the vars in a constructor function. here is my initial question:

    HI 🙂 I built a rest API with this tutorial, I enjoyed it a lot. Thank you! Now I am trying to deploy the API on Heroku. But I am having trouble with the database.php configurations. Bc this code examples depends on the class database which is used to set up the connection to the Database and PHP doesn’t want me to put my environment variables to access the Heroku DB within the class. Any hints on how I could solve that?

  91. Mirosław Całka Avatar
    Mirosław Całka

    https://uploads.disquscdn.com/images/0bd04a0468e05aa050da2e5a531b79a0710357f4d2f7e9a1f303044d2baf6d05.png Hi
    While testing point 6.3 in Postman I see warnings “Trying to access array offset on value of type bool”. Is this correct?

    1. Hi @mirosawcaka, I cannot reproduce the issue. Would you show us your code in objects/product.php file in line around 110?

  92. Great tutorial, with real life examples, thanks!

    1. Hi Rudolf, you’re welcome! Glad you liked our tutorial, thank you!

Leave a Reply

Your email address will not be published. Required fields are marked *