Quickly Build RESTful APIs in PHP with Slim – Part 4

The fourth and final part of this series is focusing on using the Slim Framework’s middleware functionality to validate (to a degree) RESTful API requests. This post will show how to add a simple API key to the requests. There is obviously more one could do, like having a secret key (which would not be passed to the server) to encode some data to further validate the user, but a simple API key is a good first step.

Now, the word middleware may conjure up a lot of Dilbert-type images, but Slim’s middleware is simple and highly functional. To start, let’s modify the route:

   // The old route
   // $this->app->get('/:id', array($this, 'getItem'));
   $this->app->get('/:id/:key', array($this,'verifyKey'), array($this, 'getItem'));

As you can see, there are two differences. The first is that there is now a :key parameter. The second is an array containing a reference to $this and the name of our key verification call, verifyKey. The reason that an array must be passed in, instead of just listing the name of the function, is that this is part of a namespaced PHP Class.

Next, there needs to be a table holding keys. A very basic example:

CREATE TABLE IF NOT EXISTS `apikeys` (
  `apiKey` varchar(32) NOT NULL,
  PRIMARY KEY (`apiKey`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `apiKeys` (`apiKey`) VALUES
('12345'),
('abcde');

One word of caution: you may be tempted to call the table keys and the field key. Those are reserved MySQL words, so to save future headaches, you will probably want to avoid that.

Now, let’s take a look at the verifyKey function. It takes the key that was passed in via the URL and does a quick database lookup to see if it exists. If it doesn’t exist, it returns an error message, and $this->app->stop() is called to stop any further processing. If the key does exist, nothing is done (currently), and the getItem function is subsequently called.

    public function verifyKey(\Slim\Route $route) {
        try { 
            $key = $route->getParam('key');
            $sql = "select * from apiKeys where apiKey = :apiKey";
            $s = $this->dbh->prepare($sql);
            $s->bindParam("apiKey", $key);
            $s->execute();
            
            $keyVerification = $s->fetch(\PDO::FETCH_OBJ);
            if($keyVerification) {
                // nothing to do currently
                // record number of API requests?
            } else {
                // no key found, thus this key is invalid
                $this->app->status(400);
                $result = array("status" => "error", "message" => "You need a valid API key.");
                echo json_encode($result);
                $this->app->stop();
            }
        } catch(\PDOException $e) {
            $this->app->status(500);
            $result = array("status" => "error", "message" => 'Exception: ' . $e->getMessage());
            echo json_encode($result);
            $this->app->stop();
        }
    }

One bit of code I’ll point out:

$this->app->status(400);

This sets the HTTP status code. There’s plenty of discussion on the web on the correct HTTP status code to respond with. See this StackOverflow question on the topic. Regardless, 400 means "Bad Request", which a request needing a proper API key undoubtedly is. When there is a database issue, that would qualify as an internal server error, which is HTTP status code 500.

The reason these codes are important is they let the system or user making these calls know if there is a problem with their request or a problem with the server. A well designed system would not continue to make requests depending on what codes it received.

The nice thing about this middleware functionality is that no changes were made to the original getItem function:

    public function getItem($id) {
       try { 
            $sql = "select * from commodores where id = :id";
            $s = $this->dbh->prepare($sql);
            $s->bindParam("id", $id);
            $s->execute();
            $result = array("status" => "success", "commodore" => $commodore);
            echo json_encode($result);
       } catch(\PDOException $e) {
           echo 'Exception: ' . $e->getMessage();
       }
    }

It has no idea that a key was passed in, let alone that the key was validated against another table, and that is ideal. Some example calls:

URL: http://local.api.ericbrandel.com/v1/get/6/notGonnaWork

{
   "status":"error",
   "message":"You need a valid API key."
}

URL: http://local.api.ericbrandel.com/v1/get/6/12345

{
   "status":"success",
   "commodore":
      {
         "id":"6",
         "name":"Commodore 64",
         "url":"http:\/\/en.wikipedia.org\/wiki\/Commodore_64"
      }
}
This entry was posted in PHP, Web Development and tagged , , , , , , , . Bookmark the permalink.

6 Responses to Quickly Build RESTful APIs in PHP with Slim – Part 4

Leave a Reply to Swapnil Cancel reply

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