In this tutorial, I'll teach you how to build a simple REST API with PHP and MySQL.
REST has become the de facto standard when it comes to exposing data via APIs and building web services. In fact, most web applications these days access and expose data via REST APIs. With the popularity of front-end frameworks that can consume REST APIs effortlessly, it’s always going to be a plus for you if your web application exposes REST APIs.
In this article, we’ll build a simple demo application, which allows you to fetch a list of users from the MySQL database via a REST endpoint.
Setting up the skeleton
In this section, we’ll briefly go through the project structure.
Let’s have a look at the following structure.
├── Controller
│ └── Api
│ ├── BaseController.php
│ └── UserController.php
├── inc
│ ├── bootstrap.php
│ └── config.php
├── index.php
└── Model
├── Database.php
└── UserModel.php
Let’s try to understand the project structure.
- index.php: the entry-point of our application. It will act as a front-controller of our application.
- inc/config.php: holds the configuration information of our application. Mainly, it will hold the database credentials.
- inc/bootstrap.php: used to bootstrap our application by including the necessary files.
- Model/Database.php: the database access layer which will be used to interact with the underlying MySQL database.
- Model/UserModel.php: the User model file which implements the necessary methods to interact with the users table in the MySQL database.
- Controller/Api/BaseController.php: a base controller file which holds common utility methods.
- Controller/Api/UserController.php: the User controller file which holds the necessary application code to entertain REST API calls.
So that’s the basic setup that we are going to implement in the rest of the post.
Create a database and model classes
In this section, we’ll create a database and the users table. We’ll also create the necessary model classes that will be used to fetch users from a database.
Create a Database and the Users Table
Create the rest_api_demo database by executing the following command in your MySQL terminal. (Access this with the command mysql from the command line)
$CREATE DATABASE rest_api_demo;
You could also use a tool like phpMyAdmin if you prefer working with your databases that way.
Once the rest_api_demo database is created, go ahead and create the users table by running the following statements.
$use rest_api_demo;
$CREATE TABLE `users` (
`user_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`user_email` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`user_status` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
That should create the users table in the rest_api_demo database. You will also want to populate this table with a few dummy records for testing purposes. Insert a few records, and you’re good to go!
Create model classes
In this section, we’ll create the necessary model classes.
Create the Model/Database.php file with the following contents.
<?php
class Database
{
protected $connection = null;
public function __construct()
{
try {
$this->connection = new mysqli(DB_HOST, DB_USERNAME, DB_PASSWORD, DB_DATABASE_NAME);
if (mysqli_connect_errno()) {
throw new Exception("Could not connect to database.");
}
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
public function select($query = "" , $params = [])
{
try {
$stmt = $this->executeStatement( $query , $params );
$result = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
$stmt->close();
return $result;
} catch(Exception $e) {
throw New Exception( $e->getMessage() );
}
return false;
}
private function executeStatement($query = "" , $params = [])
{
try {
$stmt = $this->connection->prepare( $query );
if ($stmt === false) {
throw New Exception("Unable to do prepared statement: " . $query);
}
if ($params ) {
$stmt->bind_param($params[0], $params[1]);
}
$stmt->execute();
return $stmt;
} catch(Exception $e) {
throw New Exception( $e->getMessage() );
}
}
}
?>
This is a database access layer class, which allows us to set up a connection to the MySQL database. Apart from the connection setup, it contains generic methods like select and executeStatement that allow us to select records from a database. We won’t use the Database class directly; instead, we will create corresponding model classes that extend the Database class in order to access the underlying MySQL database.
Next, let’s create the Model/UserModel.php class with the following contents.
<?php
require_once PROJECT_ROOT_PATH . "/Model/Database.php";
class UserModel extends Database
{
public function getUsers($limit)
{
return $this->select("SELECT * FROM users ORDER BY user_id ASC LIMIT ?", ["i", $limit]);
}
}
?>
It’s important to note that the UserModel class extends the Database class.
Apart from that, it contains the getUsers method, which allows us to select users from the MySQL database. It’s mandatory to pass the $limit parameter, which makes sure that it won’t select all records at once.
Of course, you could define more methods in the UserModel class as per your requirements. We’ll keep things simple in the context of this tutorial.
So now we have our database and model classes set up. In the next section, we’ll see how to create controllers and the remaining files in our demo application.
Create application layer components
In this section, we’ll create the remaining files that are required for our demo application to work.
The inc directory
For starters, we’ll create the necessary configuration files.
Create the inc/config.php file with the following contents.
<?php
define("DB_HOST", "localhost");
define("DB_USERNAME", "demo");
define("DB_PASSWORD", "demo");
define("DB_DATABASE_NAME", "rest_api_demo");
?>
Make sure to update all the values with the actual ones that you’re using in your installation.
Next, go ahead and create the inc/bootstrap.php file with the following contents.
<?php
define("PROJECT_ROOT_PATH", __DIR__ . "/../");
// include main configuration file
require_once PROJECT_ROOT_PATH . "/inc/config.php";
// include the base controller file
require_once PROJECT_ROOT_PATH . "/Controller/Api/BaseController.php";
// include the use model file
require_once PROJECT_ROOT_PATH . "/Model/UserModel.php";
?>
Firstly, we’ve initialized the PROJECT_ROOT_PATH constant with the directory root of our application. In this way, we could use the PROJECT_ROOT_PATH constant to prepare absolute paths in our application. Next, we’ve included the config.php file, which holds the database connection information. Finally, we've included controller and model files.
So that’s it for setting up the common files in our application.
The controller directory
In this section, we’ll implement controllers that hold the majority of our application logic.
The BaseController.php File
Create the Controller/Api/BaseController.php file with the following contents. The BaseController class contains the utility methods that are used by other controllers.
<?php
class BaseController
{
/**
* __call magic method.
*/
public function __call($name, $arguments)
{
$this->sendOutput('', array('HTTP/1.1 404 Not Found'));
}
/**
* Get URI elements.
*
* @return array
*/
protected function getUriSegments()
{
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$uri = explode( '/', $uri );
return $uri;
}
/**
* Get querystring params.
*
* @return array
*/
protected function getQueryStringParams()
{
return parse_str($_SERVER['QUERY_STRING'], $query);
}
/**
* Send API output.
*
* @param mixed $data
* @param string $httpHeader
*/
protected function sendOutput($data, $httpHeaders=array())
{
header_remove('Set-Cookie');
if (is_array($httpHeaders) && count($httpHeaders)) {
foreach ($httpHeaders as $httpHeader) {
header($httpHeader);
}
}
echo $data;
exit;
}
}
?>
Let’s go through all the BaseController class methods quickly.
The __call method is a magic method, and it’s called when you try to call a method that doesn't exist. We’re using this opportunity to throw the HTTP/1.1 404 Not Found error when someone tries to call a method which we haven’t implemented. If this sounds confusing to you, don't worry—it will make more sense when we test our application in the next section.
Next, there’s the getUriSegments method, which returns an array of URI segments. It’s useful when we try to validate the REST endpoint called by the user. Following that, there’s the getQueryStringParams method, which returns an array of query string variables that are passed along with the incoming request.
Finally, there’s the sendOutput method, which is used to send the API response. We’ll call this method when we want to send the API response to the user.
The UserController.php File
Next, create the Controller/Api/UserController.php file with the following contents.
<?php
class UserController extends BaseController
{
/**
* "/user/list" Endpoint - Get list of users
*/
public function listAction()
{
$strErrorDesc = '';
$requestMethod = $_SERVER["REQUEST_METHOD"];
$arrQueryStringParams = $this->getQueryStringParams();
if (strtoupper($requestMethod) == 'GET') {
try {
$userModel = new UserModel();
$intLimit = 10;
if (isset($arrQueryStringParams['limit']) && $arrQueryStringParams['limit']) {
$intLimit = $arrQueryStringParams['limit'];
}
$arrUsers = $userModel->getUsers($intLimit);
$responseData = json_encode($arrUsers);
} catch (Error $e) {
$strErrorDesc = $e->getMessage().'Something went wrong! Please contact support.';
$strErrorHeader = 'HTTP/1.1 500 Internal Server Error';
}
} else {
$strErrorDesc = 'Method not supported';
$strErrorHeader = 'HTTP/1.1 422 Unprocessable Entity';
}
// send output
if (!$strErrorDesc) {
$this->sendOutput(
$responseData,
array('Content-Type: application/json', 'HTTP/1.1 200 OK')
);
} else {
$this->sendOutput(json_encode(array('error' => $strErrorDesc)),
array('Content-Type: application/json', $strErrorHeader)
);
}
}
}
?>
It’s important to note that the UserController class extends the BaseController class. Ideally, this class would contain the action methods that are associated with the REST endpoints that are defined for the user entity. In our case, for example, the /user/list REST endpoint corresponds to the listAction method. In this way, you can also define other methods for other REST endpoints.
The listAction method is used to get a list of users from the MySQL database. It contains the whole logic of the /user/list REST endpoint.
In the listAction method, we’ve initialized a couple of variables like $requestMethod and $arrQueryStringParams in the first place. Next, we check if the user has called the user/list endpoint with the GET method; otherwise, we won’t process further. Finally, we create the UserModel object and call the getUsers method to fetch a list of users from a database. We’ve also used the json_encode function to convert an array into a JSON object before it’s sent to the user.
Lastly, we’ve used the sendOutput method to send the JSON response to the user. It’s important to note that the response content-type header value is set to application/json since we’re sending the JSON response.
Similarly, you could define other methods for other endpoints as well.
The index.php file
The index.php file is the entry-point of our application. Let’s see how it looks.
<?php
require __DIR__ . "/inc/bootstrap.php";
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$uri = explode( '/', $uri );
if ((isset($uri[2]) && $uri[2] != 'user') || !isset($uri[3])) {
header("HTTP/1.1 404 Not Found");
exit();
}
require PROJECT_ROOT_PATH . "/Controller/Api/UserController.php";
$objFeedController = new UserController();
$strMethodName = $uri[3] . 'Action';
$objFeedController->{$strMethodName}();
?>
Firstly, we’ve used parse_url and explode functions to initialize URI segments into the $uri array variable. Next, we’re validating the URI segments. Finally, we’ve initialized the UserController controller and called the corresponding action method.
With that, we’ve created all the necessary files in our demo REST application. In the next section, we’ll see how to call it from the end-user perspective.
How to call our REST API
In this section, we’ll see how to call our demo application. In our application, we’ve built a REST endpoint to get a list of users.
Let’s see how the URL of our endpoint looks:
// https://localhost/index.php/{MODULE_NAME}/{METHOD_NAME}?limit={LIMIT_VALUE}
http://localhost/index.php/user/list?limit=20
If you recall the index.php file, we checked if the $uri[2] variable is set to user. Also, the $uri[3] variable value would act as a method name. In the above case, the $uri[3] variable is set to list. Thus, it would end up calling the listAction method of the UserController class.
The output should look like this:
[
{
"user_id":1,
"username":"Bob",
"user_email":"bob@gmail.com",
"user_status":0
},
{
"user_id":2,
"username":"John",
"user_email":"john@gmail.com",
"user_status":1
},
{
"user_id":3,
"username":"Mark",
"user_email":"mark@gmail.com",
"user_status":1
},
{
"user_id":4,
"username":"Ville",
"user_email":"ville@gmail.com",
"user_status":0
}
]
As you can see, it returns a list of users as a JSON object. Apart from that, if there’s any application error, it would be returned as a JSON object as well for debugging purposes.
Conclusion
Today, we discussed how you can build a REST application with PHP and MySQL. For demonstration purposes, we created a demo application which allows you to fetch a list of users from a MySQL database via a REST API.