A web API (Application Programming Interface) is a set of programming instructions to access software applications and consume and share data between them. Developing your own API you can benefit both you and your users and for that we will write about how to build a simple REST API with PHP.
To build our API we will use Slim micro framework. As they mention in their site
Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs.
As we said above an API is an Application Programming Interface for what it should be friendly, simple and easy to use. To help other developers to use our API we should consider writing a good documentation about it, or at least, a very explanatory README file. Such documentation need to be a summary of the service’s scope and the list of methods and access points.
Following the key principles of REST, each resource is represented by a URL, where the action is the HTTP verb used to access it.
A full list of access points is listed below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | URL HTTP Verb Operation /api/users GET Returns an array of users /api/users/:id GET Returns the user with id of :id /api/users POST Adds a new user and return it with its id /api/users/:id PATCH Partially updates the user with id of :id /api/users/:id PUT Updates the complete user with id of :id /api/users/:id DELETE Deletes the user with id of :id /api/users/:id/phonenumbers GET Returns the phonenumbers for the user with id of :id /api/users/:id/phonenumbers/:pid GET Returns the phonenumber with id of :pid for the user with id of :id /api/users/:id/phonenumbers POST Adds a new phonenumber for the user with id of :id /api/users/:id/phonenumbers/:pid PATCH Partially updates the phonenumber with id of :pid for the user with id of :id /api/users/:id/phonenumbers/:pid PUT Updates the phonenumber with id of :pid for the user with id of :id /api/users/:id/phonenumbers/:pid DELETE Deletes the phonenumber with id of :pid for the user with id of :id |
The above README is a good start point, but a more complete documentation would be better. You can consider some libraries to generate documentation like Google APIs Discovery Service, apiDoc or Swagger.
Setup
We define our composer.json with our project information and dependencies:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | { "name": "yourname/users-management", "description": "Simple RESTful API for users management", "license": "MIT", "authors": [ { "name": "Your Name", "email": "you@yourdomain.com" } ], "require": { "slim/slim": "*", "slim/extras": "*", "slim/middleware": "*", "monolog/monolog": "*", "j4mie/paris": "*", "flynsarmy/slim-monolog": "*" }, "archive": { "exclude": ["vendor", ".DS_Store", "*.log"] }, "autoload": { "psr-0": { "API": "lib/" } } } |
As we can see in our composer.json, along with Slim we’re using Paris (lightweight Active Record implementation on top of Idiorm) to access the database layer and Monolog for logging.
Bootstrap our API
The bootstrap.php file is in charge to autoload classes and load configurations.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | // Init application mode if (empty($_ENV['SLIM_MODE'])) { $_ENV['SLIM_MODE'] = (getenv('SLIM_MODE'))? getenv('SLIM_MODE') : 'dev'; } // Init and load configuration $config = array(); $configFile = dirname(__FILE__) . '/config/config.'. $_ENV['SLIM_MODE'] . '.php'; if (is_readable($configFile)) { require_once $configFile; } else { require_once dirname(__FILE__) . '/config/config.default.php'; } // Create App $app = new API\Application($config['app']); // Get log writer $log = $app->getLog(); // Init database try { if (!empty($config['db'])) { \ORM::configure($config['db']['dsn']); if (!empty($config['db']['username']) && !empty($config['db']['password'])) { \ORM::configure('username', $config['db']['username']); \ORM::configure('password', $config['db']['password']); } } } catch (\PDOException $e) { $log->error($e->getMessage()); } // Add some Middlewares here (we discuss about this later) |
First, we get the environment we are using (could be dev, staging, prod or whatever you want and define) and then we load the environment related configuration, it there is not, we load default configuration. Then we create the application which as you can see it’s an Application class which extends the basic Slim class; and at the end we try to connect to the database.
Later we will add some Middleware to our app, you can read about what a Middleware is here.
Front Controller
Our front controller index.php is the unique entry point to our API and it’s where the magic happens. Here we’ll define the HTTP verbs to interact with our API and be able to list/create/edit/delete our “Users” and “Phonenumbers“.
Here is our front controller where we’ll start writing our methods (HTTP verbs):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | require_once dirname(__FILE__) . '/bootstrap.php'; // API group $app->group('/api', function () use ($app, $log) { // Version group $app->group('/v1', function () use ($app, $log) { // GET route for all users $app->get('/users', function () use ($app, $log) { // Some code here }); // GET route $app->get('/users/:id', function ($id) use ($app, $log) { // Some code here }); // POST route, for creating $app->post('/users', function () use ($app, $log) { // Some code here }); // PUT route, for updating $app->put('/users/:id', function ($id) use ($app, $log) { // Some code here }); // DELETE route $app->delete('/users/:id', function ($id) use ($app, $log) { // Some code here }); // GET route for all users's phonenumbers $app->get('/users/:id/phonenumbers', function ($id) use ($app, $log) { // Some code here }); // GET route for specific user's phonenumber $app->get('/users/:id/phonenumbers/:pid', function ($id, $phoneId) use ($app, $log) { // Some code here }); // POST route, for creating a new user's phonenumber $app->post('/users/:id/phonenumbers', function ($id) use ($app, $log) { // Some code here }); // PUT route, for updating a user's phonenumber $app->put('/users/:id/phonenumbers/:pid', function ($id, $phoneId) use ($app, $log) { // Some code here }); // DELETE route, for delete a user's phonenumber $app->delete('/users/:id/phonenumbers/:pid', function ($id, $phoneId) use ($app, $log) { // Some code here }); }); }); |
We’ve created two nested groups, /api and /v1, so we can easily adhere to the “versioning in the URL” best practice to versioning APIs.
So far we’ve installed needed libraries, bootstrapped the app and defined the routes for all the end points our API will have. In the next parts of this post series we will add the logic for every method we’ve defined and we’ll add and write some Middlewares to make a fully functional API.
Keep in touch! 😀