How to Create a RESTful API in PHP With SlimPHP 4 and MySQL

December 14, 2021
Written by
Ijeoma Nelson
Contributor
Opinions expressed by Twilio contributors are their own
Reviewed by

For those times when all you need is a simple solution that enables you to write quick, yet powerful applications, Slim is a great contender amongst the myriad of available PHP frameworks.

At its core, Slim is a microframework designed to receive HTTP requests, route the requests to the relevant controllers, and return the corresponding HTTP responses.

Slim’s minimalist environment makes it ideal for the development of both microservices and APIs that consume, repurpose, or publish data.

In this tutorial, you’re going to learn how to create a RESTful API using Slim 4, PHP, and MySQL.

Prerequisites

To complete this tutorial you will need the following:

Let's create the API!

To begin, you need to create the project’s root directory, named customers-api. You can do this in your editor/IDE or by running the following commands on a mac.

mkdir customers-api
cd customers-api

Now, let’s create two more directories named src and public inside the project's root directory. src will contain another directory, named Models, and inside of that, there will be a file named Db.php. Then public will have an index.php file, which will contain the application's middleware and routes for the API. To achieve all of this, run the following command in the terminal in the project's root directory.

mkdir -p public src/Models && touch src/Models/Db.php public/index.php

Install the dependencies

Having created the project directory structure and switched to it, we now need to install the required dependencies. To do this, run the following command in the project's root directory.

composer require --with-all-dependencies \
    slim/slim:"4.*" \
    slim/psr7 \
    selective/basepath

Provision the database

Next, using your preferred database tool (such as MySQL's command-line tool or the database tool in PhpStorm) run the SQL below to provision the database schema with the required tables, and two dummy records containing name, email, and phone fields for each person.

To begin, create a new database named customers_api by running the command below.

CREATE DATABASE customers_api;

Now, use the MySQL command below to confirm that the database has been created.

SHOW DATABASES;

You’ll need to create a table inside the customers_api database and to do this, type the command below into the terminal.

USE customers_api;

Once inside the customers_api database, create a table named customers, with the following code.

CREATE TABLE customers
(
  id INT unsigned NOT NULL AUTO_INCREMENT,  
  name VARCHAR(150) NOT NULL,
  email VARCHAR(150) NOT NULL,
  phone VARCHAR(150) NOT NULL,
  PRIMARY KEY(id)
);

This creates a table with four columns, the customer’s id (which auto-increments), name, email and phone. Let’s test it has worked by typing the following SQL command in the terminal.

DESCRIBE customers;

You should have the following result.

Describing the customers table

So, let’s add some sample records to the customers’ table. Copy and paste the following code into the terminal.

INSERT INTO customers ( name, email, phone) VALUES
  ( 'jane', 'jane_doe@mail.com', '123456789' ),
  ( 'john', 'john_doe@mail.com', '234567890' );

Now test the records have been added by typing out the following commands in the terminal.

SELECT * FROM customers;

You should have an output similar to the one below.

Selecting all records from the customers table

Create and run the Slim application

To accomplish this, copy the code below and paste it into public/index.php.

<?php

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Selective\BasePath\BasePathMiddleware;
use Slim\Factory\AppFactory;

require_once __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

$app->addRoutingMiddleware();
$app->add(new BasePathMiddleware($app));
$app->addErrorMiddleware(true, true, true);

$app->get('/', function (Request $request, Response $response) {
   $response->getBody()->write('Hello World!');
   return $response;
});

$app->run();

In a Slim application, each route invokes a callback function that returns an HTTP response object. The code above begins by bringing in all the PSR-7 components that the app needs. It then instantiates the app and attaches three middleware classes to it. 

The Slim middleware architecture is derived from the PSR-15 middleware interface. This is a series of concentric layers that encircle the core application, such that the last middleware added will be the first to be executed.

A simplistic overview of how middleware works.

This concentric architecture means that when the Slim application is started, the request object passes through a series of middleware classes, or layers, working from the outside inward. The request enters the outermost middleware, then moves on to the next outermost middleware, continuing until it reaches the application.

For this reason, to catch any errors in the application, you call $app->addErrorMiddleware(true, true, true) last, otherwise any errors thrown from the other middlewares will not be caught.

Once the request object reaches the Slim application, the relevant route is dispatched, resulting in a response object exiting the concentric layers from the inside out. When the object passes the outermost middleware, it is turned into a raw HTTP response and then returned to the client.

Check that the application works

To check that the application works, first start it running, using PHP's built-in web server, on port 8888, by running the command below.

php -S localhost:8888 -t public

Then, open your browser and enter http://localhost:8888/ into the URL panel. You should have a similar output to the screenshot below.

Initial render of the application

Create the database connection

To do this, you’ll first need to add a PSR-4 autoloader, by copying and pasting the following code into composer.json.

"autoload": {
    "psr-4": {
        "App\\": "src"
    }
}

Now, regenerate Composer's autoloader, by running the command below from the project's top-level directory.

composer dumpautoload -o

Next, you need to navigate to the src/Models/db.php file and paste the following code into it. Then, you need to update the values for $host, $user, $pass, and $dbname to match those in your database.

<?php

namespace App\Models;

use \PDO;

class DB
{
    private $host = 'localhost';
    private $user = 'user';
    private $pass = 'password';
    private $dbname = 'project_db';

    public function connect()
    {
        $conn_str = "mysql:host=$this->host;dbname=$this->dbname";
        $conn = new PDO($conn_str, $this->user, $this->pass);
        $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

        return $conn;
    }
}

Create the routes for the HTTP requests

To do this, open public/index.php and include the use statements listed below.

<?php

use App\Model\DB;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Selective\BasePath\BasePathMiddleware;

Next, you need to create a route to fetch all the data from the database. To do that, copy and paste the code below towards the end of public/index.php, after the get request, but before the $app->run();

$app->get('/customers-data/all', function (Request $request, Response $response) {
 $sql = "SELECT * FROM customers";

 try {
   $db = new Db();
   $conn = $db->connect();
   $stmt = $conn->query($sql);
   $customers = $stmt->fetchAll(PDO::FETCH_OBJ);
   $db = null;
  
   $response->getBody()->write(json_encode($customers));
   return $response
     ->withHeader('content-type', 'application/json')
     ->withStatus(200);
 } catch (PDOException $e) {
   $error = array(
     "message" => $e->getMessage()
   );

   $response->getBody()->write(json_encode($error));
   return $response
     ->withHeader('content-type', 'application/json')
     ->withStatus(500);
 }
});

Here we begin by telling the application to perform a get request on /customers-data/all, this will then invoke a call back routine that returns a PSR-7 response object.

To test the application, first, make sure MySQL is running and then use PHP's built-in webserver, by copying and pasting the following command in the terminal.

php -S localhost:8888 -t public

With the application running, open http://localhost:8888/customers-data/all in your browser, where you should see a similar output to the one in the image below.

List all customers in the browser

Now we need to create a route for a POST request. However, Slim's implementation of PSR-7 does not support the sending of data in a JSON format, instead, they provide a BodyParsingMiddleware that handles this task. To do this, add the code below to public/index.php, placing it just after the call to AppFactory::create();.

$app->addBodyParsingMiddleware();

Now, let's create the POST request, by copying and pasting the following code into public/index.php.

<?php

$app->post('/customers-data/add', function (Request $request, Response $response, array $args) {
 $data = $request->getParsedBody();
 $name = $data["name"];
 $email = $data["email"];
 $phone = $data["phone"];

 $sql = "INSERT INTO customers (name, email, phone) VALUES (:name, :email, :phone)";

 try {
   $db = new Db();
   $conn = $db->connect();
  
   $stmt = $conn->prepare($sql);
   $stmt->bindParam(':name', $name);
   $stmt->bindParam(':email', $email);
   $stmt->bindParam(':phone', $phone);

   $result = $stmt->execute();

   $db = null;
   $response->getBody()->write(json_encode($result));
   return $response
     ->withHeader('content-type', 'application/json')
     ->withStatus(200);
 } catch (PDOException $e) {
   $error = array(
     "message" => $e->getMessage()
   );

   $response->getBody()->write(json_encode($error));
   return $response
     ->withHeader('content-type', 'application/json')
     ->withStatus(500);
 }
});

Now, in Postman, create a new POST request, then:

  1. Set the URL to http://localhost:8888/customers-data/add.

And under the Body tab:

  1. Set the encoding-type to "raw"
  2. Set the Content-Type to JSON
  3. Paste the JSON below into the request's body field.
{
   "name" : "amy",
   "email" : "amy@mail.com",
   "phone" : "123449988383"
}

Have a look at the screenshot below if you're not sure what to do.

Add a record to the database using Postman

With the request configured, click Send to make the request. To confirm that a new entry has been created, head back to your browser and refresh it. You should see output similar to the example below.

List all three customers in the browser

Next, we’ll amend the customer whom we just added, by adding her last name. To do that, copy and paste the following route into public/index.php.

$app->put(
    '/customers-data/update/{id}',
    function (Request $request, Response $response, array $args) 
{
 $id = $request->getAttribute('id');
 $data = $request->getParsedBody();
 $name = $data["name"];
 $email = $data["email"];
 $phone = $data["phone"];

 $sql = "UPDATE customers SET
           name = :name,
           email = :email,
           phone = :phone
 WHERE id = $id";

 try {
   $db = new Db();
   $conn = $db->connect();
  
   $stmt = $conn->prepare($sql);
   $stmt->bindParam(':name', $name);
   $stmt->bindParam(':email', $email);
   $stmt->bindParam(':phone', $phone);

   $result = $stmt->execute();

   $db = null;
   echo "Update successful! ";
   $response->getBody()->write(json_encode($result));
   return $response
     ->withHeader('content-type', 'application/json')
     ->withStatus(200);
 } catch (PDOException $e) {
   $error = array(
     "message" => $e->getMessage()
   );

   $response->getBody()->write(json_encode($error));
   return $response
     ->withHeader('content-type', 'application/json')
     ->withStatus(500);
 }
});

To test it, create a PUT request with the following details:
 

URL

http://localhost:8888/customers-data/update/3

Body

{

     "name" : "amy wheelan",

     "email" : "amy@mail.com",

     "phone" : "123449988383"

}

Encoding-Type

raw

Content-Type

JSON

 

As with the previous request, have a look at the screenshot below if you're not sure what to do.

 

Make a PUT request to update a customer with Postman

If you navigate to your localhost, you should have something that looks like the following.

List the updated customers with the browser

To finish, let’s delete the last entry. Add the code below to the public/index.php file.

$app->delete('/customers-data/delete/{id}', function (Request $request, Response $response, array $args) {
 $id = $args["id"];

 $sql = "DELETE FROM customers WHERE id = $id";

 try {
   $db = new Db();
   $conn = $db->connect();
  
   $stmt = $conn->prepare($sql);
   $result = $stmt->execute();

   $db = null;
   $response->getBody()->write(json_encode($result));
   return $response
     ->withHeader('content-type', 'application/json')
     ->withStatus(200);
 } catch (PDOException $e) {
   $error = array(
     "message" => $e->getMessage()
   );

   $response->getBody()->write(json_encode($error));
   return $response
     ->withHeader('content-type', 'application/json')
     ->withStatus(500);
 }
});

To test it, make a DELETE request in Postman with the URL http://localhost:8888/customers-data/delete/3. No other settings need to be set. After doing so, if you reload the page one more time in your browser, you’ll find that the last entry has gone.

You have now successfully created a RESTful API using the Slim 4 framework

Have fun building something awesome!

Ijeoma Nelson is a former Deloitte software and cloud engineer. She discovered a hidden passion for programming after winning Startup Weekend Silicon Valley. These days she can be found planning her travel adventures for 2022 whilst building cool stuff with code. You can reach her via LinkedIn or Twitter.