Create a quick REST API using Slim framework

During a recent client project, I frequently needed to access a remote database table and update the same for certain fields. This was accomplished using phpMyAdmin on the server. However, it was getting tedious and was prone to accidental updates and deletes. Also, a couple of other developers also needed to make changes to certain fields on the remote database from their local server.

This is all a tedious process and prone to errors. One solution was to create a quick REST api wrapper around the remote database, using which developers could update the database table without any risk of corrupting the data and also with the added benefit of updating the table programmatically.

This post shows how to create a simple REST API using Slim framework. Slim is a PHP micro framework which lets you write quick PHP web applications. Here we will use it to build a REST api. Although I cannot show the exact database schema used, as this would not make sense without the context, I’ve provided a simple schema for the examples. The schema shows a students table which provides student scores along with their names.

CREATE TABLE IF NOT EXISTS `students` (
  `student_id` int(10) NOT NULL auto_increment,
  `score` int(10) default '0',
  `first_name` varchar(50) default NULL,
  `last_name` varchar(50) default NULL,
  PRIMARY KEY  (`student_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Installation

You can install Slim framework using Composer or with a manual install. I prefer using Composer. If you will be using Slim in a sub-directory than make sure you use a .htaccess as given below.

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]

Designing our REST api

Before we design the actual code we need to construct the REST endpoints we will be needing to accomplish our goals. In our small example here we will only be needing to get the student score or update the score. Say our url is ‘http://somedomain.com’ and Slim is installed in the ‘api’ directory. We will be needing 3 endpoints:

http://somedomain.com/api/                   #Home endpoint
http://somedomain.com/api/getScore           #Get student score
http://somedomain.com/api/updateScore        #Update student score

The simplest thing you can do with Slim is return a welcome string for the home route. This is the simplest Slim application you can design. The url for our home route will be ‘http://somedomain.com/api/’. This just returns the string ‘Welcome to Slim based API’.

<?php
 
require 'vendor/autoload.php';
$app = new \Slim\Slim();
 
$app->get('/', function() use($app) {
    $app->response->setStatus(200);
    echo "Welcome to Slim 3.0 based API";
}); 
 
$app->run();

Note the use of the use operator to import the $app object in the current anonymous function context. You can also do it differently, by getting an instance of the $app object in the function.

$app->get('/', function() {
    $app = \Slim\Slim::getInstance();
    $app->response->setStatus(200);
    echo "Welcome to Slim 3.0 based API";
});

The next endpoint will be to get a student score, this requires that we pass a student id in the url. For example:

# Get score for student having the id of 3
http://somedomain.com/api/getScore/3

The code for the same is shown below.

$app->get('/getScore/:id', function ($id) {
 
    $app = \Slim\Slim::getInstance();
 
    try 
    {
        $db = getDB();
 
        $sth = $db->prepare("SELECT * 
            FROM students
            WHERE student_id = :id");
 
        $sth->bindParam(':id', $id, PDO::PARAM_INT);
        $sth->execute();
 
        $student = $sth->fetch(PDO::FETCH_OBJ);
 
        if($student) {
            $app->response->setStatus(200);
            $app->response()->headers->set('Content-Type', 'application/json');
            echo json_encode($student);
            $db = null;
        } else {
            throw new PDOException('No records found.');
        }
 
    } catch(PDOException $e) {
        $app->response()->setStatus(404);
        echo '{"error":{"text":'. $e->getMessage() .'}}';
    }
});

This returns the score of a student when we pass a appropriate student id. Once we find the student score we return the complete student information as a JSON string with the appropriate ‘content-type’ and ‘status’ headers. For any error we throw an exception and return a ‘404’ status.

Now for the ‘updateScore’ endpoint. The code is given below.

$app->post('/updateScore', function() {
    $app = \Slim\Slim::getInstance();
    $allPostVars = $app->request->post();
    $score = $allPostVars['score'];
    $id = $allPostVars['id'];
 
    try 
    {
        $db = getDB();
 
        $sth = $db->prepare("UPDATE students 
            SET score = :score 
            WHERE student_id = :id");
 
        $sth->bindParam(':score', $score, PDO::PARAM_INT);
        $sth->bindParam(':id', $id, PDO::PARAM_INT);
        $sth->execute();
 
        $app->response->setStatus(200);
        $app->response()->headers->set('Content-Type', 'application/json');
        echo json_encode(array("status" => "success", "code" => 1));
        $db = null;
 
    } catch(PDOException $e) {
        $app->response()->setStatus(404);
        echo '{"error":{"text":'. $e->getMessage() .'}}';
    }
 
});

As this is a POST request, we gather all the parameters in the $allPostVars variable using the ‘$app->request->post()’ method. As in the previous function we return a JSON success string on a valid update or throw an error.

The complete Slim REST API is shown below. The getDB() function is a db helper which provides a db instance for each route. Note that this is not the best way to code a db access function, I’ve used it just for illustration. In a production application I would use a singleton pattern to get the db object each time.

<?php
 
require 'vendor/autoload.php';
 
function getDB()
{
    $dbhost = "";
    $dbuser = "";
    $dbpass = "";
    $dbname = "";
 
    $mysql_conn_string = "mysql:host=$dbhost;dbname=$dbname";
    $dbConnection = new PDO($mysql_conn_string, $dbuser, $dbpass); 
    $dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    return $dbConnection;
}
 
$app = new \Slim\Slim();
 
$app->get('/', function() use($app) {
    $app->response->setStatus(200);
    echo "Welcome to Slim based API";
}); 
 
$app->get('/getScore/:id', function ($id) {
    $app = \Slim\Slim::getInstance();
 
    try 
    {
        $db = getDB();
        $sth = $db->prepare("SELECT * 
            FROM students
            WHERE student_id = :id");
 
        $sth->bindParam(':id', $id, PDO::PARAM_INT);
        $sth->execute();
 
        $student = $sth->fetch(PDO::FETCH_OBJ);
 
        if($student) {
            $app->response->setStatus(200);
            $app->response()->headers->set('Content-Type', 'application/json');
            echo json_encode($student);
            $db = null;
        } else {
            throw new PDOException('No records found.');
        }
 
    } catch(PDOException $e) {
        $app->response()->setStatus(404);
        echo '{"error":{"text":'. $e->getMessage() .'}}';
    }
});
 
$app->post('/updateScore', function() use($app) {
 
    $allPostVars = $app->request->post();
    $score = $allPostVars['score'];
    $id = $allPostVars['id'];
 
    try 
    {
        $db = getDB();
 
        $sth = $db->prepare("UPDATE students 
            SET score = :score 
            WHERE student_id = :id");
 
        $sth->bindParam(':score', $score, PDO::PARAM_INT);
        $sth->bindParam(':id', $id, PDO::PARAM_INT);
        $sth->execute();
 
        $app->response->setStatus(200);
        $app->response()->headers->set('Content-Type', 'application/json');
        echo json_encode(array("status" => "success", "code" => 1));
        $db = null;
 
    } catch(PDOException $e) {
        $app->response()->setStatus(404);
        echo '{"error":{"text":'. $e->getMessage() .'}}';
    }
 
}); 
 
$app->run();

Testing our REST api

Testing the api is simple, just enter the endpoints in the url and see the response, or better yet you can use curl. For example you can test the home route with curl as given below, which also shows the output.

C:\localhost\test>curl -i http://localhost/projects/api/
 
HTTP/1.1 200 OK
Date: Fri, 13 Mar 2015 05:22:33 GMT
Server: Apache/2.2.11 (Win32) PHP/5.4.31
X-Powered-By: PHP/5.4.31
Content-Length: 25
Content-Type: text/html
 
Welcome to Slim based API

The same with the second route, notice the headers and status values. Also note the last response JSON string.

C:\localhost\test>curl -i http://localhost/projects/api/getScore/3
 
HTTP/1.1 200 OK
Date: Fri, 13 Mar 2015 05:24:56 GMT
Server: Apache/2.2.11 (Win32) PHP/5.4.31
X-Powered-By: PHP/5.4.31
Content-Length: 71
Content-Type: application/json
 
{"student_id":"3","score":"22","first_name":"Ethan","last_name":"Yang"}

The third endpoint requires that you post the student id and the new score that you want to update as a POST request. With CURL you can do as given below.

C:\localhost\test>curl -i --data "id=3&score=36"  http://localhost/projects/api/updateScore
 
HTTP/1.1 200 OK
Date: Fri, 13 Mar 2015 05:27:50 GMT
Server: Apache/2.2.11 (Win32) PHP/5.4.31
X-Powered-By: PHP/5.4.31
Content-Length: 29
Content-Type: application/json
 
{"status":"success","code":1}

Although CURL is quick and handy, my favorite tool to test REST apis is a Chrome extension named Postman, it is a extremely handy REST client especially when it comes to testing POST requests.

In conclusion

Slim framework offers a clean and quick way to build a REST api. Although it does not have built-in libraries for authentication you can easily add 3rd party code for the same. The extremely small size of the base framework allows a quick installation on any server. In future posts we will look into some authentication methods for Slim framework.



16 thoughts on “Create a quick REST API using Slim framework

  1. Don’t you think an update should be done with PUT and not POST ? Post is to create new student record

  2. What is the ‘vendor/autoload.php'; file? How did you get it?
    the slim framework has no autload file

  3. I am getting an Getting notice Undefined index when trying to use updateScore method, get works just fine.

  4. How do you connect the API with the database? I’m running MySQL locally with XAMPP, but I see no way how $db = getDB() ties the API to a particular DB.

  5. The getDB() function is defined in the post. Use that to change the database details for any particular db. Note that this is for demonstration purpose only. In production one would use a model to get the data.

  6. Hi,
    provide access token authentication tutorial for REST API using Slim framework?
    also add how to print level array data in rest api

Comments are closed.