DEV Community

Cover image for How to create a RESTful API with PHP and the Elementary Framework
Nana Axel
Nana Axel

Posted on • Updated on

How to create a RESTful API with PHP and the Elementary Framework

Introduction

With new web technologies, when we want to build a web site we think about front-end frameworks like React.js, AngularJS, Vue.js and so on... But what happen for the dark side of the website, the back-end?

For a simple website, like a one page portfolio or a landing page, the development of the back-end is not very necessary. But when we want to interact with a database or upload files, this become mandatory.

The problem is when we use front-end web technologies like React.js, we cannot directly interact with any back-end server language (PHP, Python, Ruby, and even Javascript with Node.js, etc...). This is where the RESTful API model takes place.

With a back-end server implementing the REST software architecture, the only thing you have to do from the front-end is to make an AJAX request for an URI to your server, passing to it some values as request parameters, and waiting for the server response.

This is what we will learn with this How-To. We will create a RESTful API server, using the PHP language, and a framework I have built, the Elementary Framework.

Prerequisites

Before start, make sure you have:

  • A PHP IDE or editor: Guys at Microsoft have made an awesome work on Visual Studio Code, a free and powerful code editor.
  • Composer: Composer is a dependencies manager for PHP projects. You can download it at https://getcomposer.org/
  • A RESTful API development environment: Since we will not create a front-end app in this How-To, we will use instead a RESTful API development environment. You can check the Postman project, it's a complete API development environment software, and the one I we will use for our project.

Part 1 - Preparing the project

We will start by creating our new project. Create a new folder, with the name you want (in this tutorial, the project name will be php_rest_api) and open a terminal into it.

Since Elementary Framework modules are composer packages, we will install the module responsible of our API (WaterPipe) with composer. In the terminal you have previously opened, type:

composer init
Enter fullscreen mode Exit fullscreen mode

This will initialize an empty composer project. Just follow the initialization process to the end. When the process finish, you can now install WaterPipe with:

composer require elementaryframework/water-pipe
Enter fullscreen mode Exit fullscreen mode

And wait the process finish, this will download the source code of the project and install it in your own. When the installation succeed, type again:

composer dump-autoload -o
Enter fullscreen mode Exit fullscreen mode

This will generate an optimized autoloader file (useful for production). For a normal autoload file, just omit the -o switch.

Now we have installed WaterPipe, the next step is to create the entry point of our API. For our project, it will be a simple index.php file. So, create a file named index.php and insert into it:

<?php

// Require composer autoload file
require "./vendor/autoload.php";

// Require the main WaterPipe class
use ElementaryFramework\WaterPipe\WaterPipe;

// Create the base pipe
$basePipe = new WaterPipe;

// Run the pipe and serve the API
$basePipe->run();
Enter fullscreen mode Exit fullscreen mode

What we are doing ?

  • We first require the autoloader file from composer
  • We can now require the main class of WaterPipe, using the namespace (this is the magic of the autoloader)
  • We create a new instance of WaterPipe into $basePipe
  • We run the pipe with $basePipe->run()

This is the minimal content of our project. If you upload this to your server and open the web page into your browser, you will surely get an error, don't worry, this is because we are running an empty pipe, we have to add some water to it ;-)

Part 2 - Thinking our API

Before write an API, we have to know exactly what we want to do, and how it will be done. You have to make this work alone for your project, since you exactly know your needs.

What we want to do?

Here we have to find API requests we have to write, from analyzing the front-end requirements (user registration, files uploading, database access, etc...).

For example, in this project we will:

  • Create an URI used to login the user
  • Create an URI used to retrieve a list blog posts
  • Create an URI used to retrieve a single blog post
  • Create an URI used to access an asset file stored into the server
  • Disallow access to all other requests

How it will be done?

Here we have to find how requests will be executed. Just take a paper and imagine a request sent to your server and how the server handle it, what kind of data have to be sent from the browser, what will be sent back from the server, what resource will be used, etc... This is an important process not only in this case but in the software development generally.

For example, in this project, the /api/login URI have to receive, with the POST method, the username and the password of the user who want to login, if informations are correct, we can create a session with these informations, and then, return a successful signal to the client. If informations are incorrect, return an unsuccessful signal to the client, followed by an error message.

Repeat these steps until you have reviewed all you have to do in your API. If so, you can go ahead and continue to the next part.

Part 3 - Writing our API

Receiving requests

The API of WaterPipe is designed to be easily understandable. When you are writing your API with it, there are some methods you have to know:

  • get(string $uri, callable $action) : Which run the specified $action when receiving a GET request at the given $uri
  • post(string $uri, callable $action) : Which run the specified $action when receiving a POST request at the given $uri
  • put(string $uri, callable $action) : Which run the specified $action when receiving a PUT request at the given $uri
  • delete(string $uri, callable $action) : Which run the specified $action when receiving a DELETE request at the given $uri
  • patch(string $uri, callable $action) : Which run the specified $action when receiving a PATCH request at the given $uri
  • head(string $uri, callable $action) : Which run the specified $action when receiving a HEAD request at the given $uri
  • request(string $uri, callable $action) : Which run the specified $action when receiving any kind of request at the given $uri

So going to more practice! Open our index.php file in your code editor/IDE, we are going to write our first request:

<?php

// Require composer autoload file
require "./vendor/autoload.php";

// Require the main WaterPipe class
use ElementaryFramework\WaterPipe\WaterPipe;

// Require the Request class
use ElementaryFramework\WaterPipe\HTTP\Request\Request;
// Require the Response class
use ElementaryFramework\WaterPipe\HTTP\Response\Response;

// Create the base pipe
$basePipe = new WaterPipe;

// Handle any kind of requests made at the root of the pipe
$basePipe->request("/", function (Request $req, Response $res) {
    $res->sendText("Welcome to my API");
});

// Run the pipe and serve the API
$basePipe->run();
Enter fullscreen mode Exit fullscreen mode

What we are doing ?

  • We require the Request class
  • We require the Response class
  • We write a request handler for the / URI (the root URI)
  • We send the text Welcome to my API as the response

Upload this to your server and open your browser, you certainly see the text Welcome to my API shown into your browser, you have wrote your first request ! Nothing awesome, I know...

I think you have now some ideas on how to write request handlers with WaterPipe. This request was just used for a demonstration, you can now delete it and just leave use statements.

Handling a POST request

Now we can go ahead and write our first request defined in the part 2, the login URI:

<?php

// Start a session
session_start();

// Require composer autoload file
require "./vendor/autoload.php";

// Require the main WaterPipe class
use ElementaryFramework\WaterPipe\WaterPipe;

// Require the Request class
use ElementaryFramework\WaterPipe\HTTP\Request\Request;
// Require the Response class
use ElementaryFramework\WaterPipe\HTTP\Response\Response;

// Create the base pipe
$basePipe = new WaterPipe;

// Our login request
$basePipe->post("/api/login", function (Request $req, Response $res) {
    // Get request data sent by the client
    $body = $req->getBody();
    $username = $body["username"];
    $password = $body["password"];

    // Check data
    // Since this How-To will not handle connections to database,
    // We will made a simple comparison here...
    if ($username === $password) {
        // Create the user session
        $_SESSION["username"] = $username;
        // Send a successful signal to the client
        $res->sendJson(array(
            "success" => true
        ));
    } else {
        // Send an unsuccessful signal to the client
        $res->sendJson(array(
            "success" => false,
            "error" => "Username and password mismatch"
        ));
    }
});

// Run the pipe and serve the API
$basePipe->run();
Enter fullscreen mode Exit fullscreen mode

What we are doing ?

  • We start a session with session_start()
  • We create our login request with the POST method
  • We retrieve request body through $req->getBody()
  • We compare retrieved data for equality (you have to use your own login algorithm here)
    • On comparison success
      • We create the user session
      • We send the successful signal to the client (as a JSON response)
    • On comparison fails
      • We send the unsuccessful signal with the error message to the client (as a JSON response)

Now we are going to test our API. Start you API development environment software, and configure it to test the API we have just wrote. Send some data and see what is returned, when the username and the password sent to the server are equals, you will certainly get a successful signal:

Successful signal

And when these informations mismatch, you got an unsuccessful signal:

Unsuccessful signal

Handling a GET request

Now we are about to write one of our blog posts requests. We will write the request responsible to return the list of published posts. I think you certainly have some ideas on what we have to do ;-)

For next examples, to do not repeat code, consider that we are writting code snippets just before the $basePipe->run() instruction:

// Our posts retrieving request
$basePipe->get("/api/posts/get", function (Request $req, Response $res) {
    // Retrieve posts from the database, or any other data storage...
    // Since this How-To will not handle connections to database,
    // We will just return static data here...
    $res->sendJSON(array(
      "success" => true,
      "posts" => array(
        array(
          "title" => "First blog post",
          "description" => "The first public post of this blog",
          "author" => "Nana Axel"
        ),
        array(
          "title" => "Second blog post",
          "description" => "The second public post of this blog",
          "author" => "Nana Axel"
        ),
        array(
          "title" => "Another blog post",
          "description" => "Just another public post of this blog",
          "author" => "Nana Axel"
        )
      )
    ));
});
Enter fullscreen mode Exit fullscreen mode

Simple I know, but the logic is here, you just have to retrieve the list of blog posts somewhere, send the list as a JSON object to the client, and parse it with your front-end framework to display it into your web page.

Now we will made something more excitant, we will retrieve data for a single post:

// Our single post retrieving request
$basePipe->get("/api/posts/get/:id", function (Request $req, Response $res) {
    // Get the id parameter from the URI
    $params = $req->uri->getParams();
    $postId = intval($params["id"]);

    // Retrieve post from the database, or any other data storage...
    // Since this How-To will not handle connections to database,
    // We will just return static data here...
    $post = array();
    $success = true;

    switch ($postId) {
      case 1:
        $post = array(
          "title" => "First blog post",
          "description" => "The first public post of this blog",
          "content" => "This first post is not very excitant, just a way for me to say Hello, World !",
          "author" => "Nana Axel",
          "published_date" => "01/10/2019 06:01",
          "category" => null
        );
        break;

      case 2:
        $post = array(
          "title" => "Second blog post",
          "description" => "The second public post of this blog",
          "content" => "I love C#, Microsoft guys have built an awesome language! Now I'm developing a game engine with it. It will support all platforms and allow user to also make game scripts with Lua and Javascript :-D",
          "author" => "Nana Axel",
          "published_date" => "01/10/2019 06:05",
          "category" => "Dev"
        );
        break;

      case 3:
        $post = array(
          "title" => "Another blog post",
          "description" => "Another public post of this blog",
          "content" => "React.js or AngularJS? Who is the best? Just leave your opinion as comment ;-)",
          "author" => "Nana Axel",
          "published_date" => "01/10/2019 06:07",
          "category" => "Poll"
        );
        break;

      default:
        $success = false;
        break;
    }

    // Send the retrieved post
    $res->sendJSON(array(
      "success" => $success,
      "post" => $post
    ));
});
Enter fullscreen mode Exit fullscreen mode

Have you seen how we have got the id of the post to retrieve? Now, using your API development environment software, try to access to /api/posts/get/1 for example and see the result, it is the first post which is returned!

This is the way WaterPipe uses to get values from the request URI. You are able to use an infinite number of parameters, WaterPipe will just check if a request URI matches with the given one, and will automatically parse this URI to retrieve parameters value.

Now going to a more complex request, we want to send an image to the client. Reading this, some people will say:

Why a request like that? We can just use the URL to the file!

Some times, it is not possible... Due to the project architecture, or when it is necessary to do not let the user know how files are structured in your server.

// Our image retrieving request
$basePipe->get("/assets/images/(.+)", function (Request $req, Response $res) {
    // Retrieve the path to the image from the request
    $params = $req->uri->getParams();
    $path = $params[0];

    // Just send the image file from our secret folder
    $res->sendFile("/path/to/our/secret/assets/folder/images/{$path}");
});
Enter fullscreen mode Exit fullscreen mode

Hell yeah! WaterPipe have a full support of regex-based requests! The only difference with the previous example is when using named parameters in requests, the parameter value is accessed from the request URI with his name ($params["id"]). And when using regex-based parameters in request, the parameter value is accessed from the request URI with his position in the URI ($params[0]).

Hacking requests

It is possible to combine named and regex-based parameters. Even in this case, the previous rule apply.

$basePipe->get("/assets/:type/(.+)", function (Request $req, Response $res) {
    // Retrieving parameters from the request
    $params = $req->uri->getParams();
    $type = $params["type"];
    $path = $params[1]; // Position 1 because the parameter :type occupy the position 0

    // Just send the file from our secret folder
    $res->sendFile("/path/to/our/secret/assets/folder/{$type}/{$path}");
});
Enter fullscreen mode Exit fullscreen mode

To handle request like /path/of/request?param1=value1&param2=value2, you don't have to explicitly specify GET parameters, and you dont have to use regex. Just create a request handler with the /path/of/request URI, and retrieve GET parameters into it:

$basePipe->get("/path/of/request", function (Request $req, Response $res) {
    // Retrieving GET parameters from the request
    $params = $req->getParams();
    $param1 = $params["param1"];
    $param2 = $params["param2"];

    $res->sendText("param1={$param1}&param2={$param2}");
});
Enter fullscreen mode Exit fullscreen mode

GET parameters are retrieved with the getParams() method of the Request class. This is totally different from the getParams() method of the RequestUri class which returns URI parameters.

Sending responses

With the previous set of examples, we have used some shortcut methods to directly send a response in a predefined format. All shortcut methods are:

  • sendHTML(string $html, int $status = 200): Used to send an HTML response.
  • sendJSONString(string $json, int $status = 200): Used to send a JSON response.
  • sendJSON(array $json, int $status = 200): Used to send a JSON response.
  • sendText(string $text, int $status = 200): Used to send a plain text response.
  • sendFile(string $path, int $status = 200, string $mime = null): Used to send a file content as a response, can be an image, a css/js file, an HTML file, etc...

When you want to have the full power on the server response, you have to configure the response yourself.

To let you see how you can achieve this, we will send the successful signal to the client with raw methods.

The first step is to define the header of the response:

// Create a new response header
$header = new ResponseHeader();

// Define the response header
$res->setHeader($header);
Enter fullscreen mode Exit fullscreen mode

Since we want to send a JSON response, we have to define the Content-Type header value to application/json. If you don't know what we are talking about, I suggest you to read about HTTP headers and MIME types.

// Create a new response header
$header = new ResponseHeader();
// Define header values
$header->setContentType("application/json");

// Define the response header
$res->setHeader($header);
Enter fullscreen mode Exit fullscreen mode

The second step is to define the response status.

// Create a new response header
$header = new ResponseHeader();
// Define header values
$header->setContentType("application/json");

// Create a new response status
$status = new ResponseStatus(ResponseStatus::OkCode);

$res
  ->setHeader($header) // Define the response header
  ->setStatus($status) // Define the response status
  ;
Enter fullscreen mode Exit fullscreen mode

The last step is to define the response body.

// Create a new response header
$header = new ResponseHeader();
// Define header values
$header->setContentType("application/json");

// Create a new response status
$status = new ResponseStatus(ResponseStatus::OkCode);

// Build the response body
$body = json_encode(array(
  "success" => true
));

$res
  ->setHeader($header) // Define the response header
  ->setStatus($status) // Define the response status
  ->setBody($body) // Define the response body as string
  ;
Enter fullscreen mode Exit fullscreen mode

When you have finished to setup your response, just send it to the client.

// Create a new response header
$header = new ResponseHeader();
// Define header values
$header->setContentType("application/json");

// Create a new response status
$status = new ResponseStatus(ResponseStatus::OkCode);

// Build the response body
$body = json_encode(array(
  "success" => true
));

$res
  ->setHeader($header) // Define the response header
  ->setStatus($status) // Define the response status
  ->setBody($body) // Define the response body as string
  ->send() // Send the response to the client and close
  ;
Enter fullscreen mode Exit fullscreen mode

Manually send response parts

With WaterPipe, you are able to send manually response parts (headers and body) by using methods :

  • sendHeaders() to send headers defined by setHeaders(ResponseHeader) ;
  • sendBody() to send the response body defined by setBody(string) ;
  • close() to ensure that your response is closed after everything is sent.
// Define your headers using $res->setHeaders($headers) according to the request...

// Send the response headers first
$res->sendHeaders();

// Compute the response body (read database, file, custom content, etc...)

// Define the response body using $res->setBody($body)

// Send the response body
$res->sendBody();

// You can redefine the body and send it mutiple times in the same response!

// Doing some shutdown process...

// Close the response
$res->close();

// Everything after a $res->close() call will not be executed!
Enter fullscreen mode Exit fullscreen mode

Handling common HTTP errors

It's common that an user fall into a 404 error, or your server have some troubles and return a 500 error. WaterPipe allow you to easily handle these errors, for example we will disallow access to all other requests to our server.

$basePipe->error(ResponseStatus::NotFoundCode, function (Request $req, Response $res) {
    // Just send a forbidden response
    $res->sendText("Forbidden.", ResponseStatus::ForbiddenCode);
});
Enter fullscreen mode Exit fullscreen mode

What we are doing ?

  • We create a request handler for the 404 HTTP error
  • We send back to the client a 403 HTTP error (Forbidden error)

In other words we are transforming all possible 404 errors from our API to a 403 error.

Now you know all the basics to build your API with WaterPipe. If you are interested, just continue to play with it, or browse the source code to learn more.

Part 3 - Some advanced tips

Using sub-pipes

For now, our index.php file look at this:

<?php

// Start a session
session_start();

// Require composer autoload file
require "./vendor/autoload.php";

// Require the main WaterPipe class
use ElementaryFramework\WaterPipe\WaterPipe;

// Require the Request class
use ElementaryFramework\WaterPipe\HTTP\Request\Request;
// Require the Response class
use ElementaryFramework\WaterPipe\HTTP\Response\Response;
// Require the ResponseStatus class
use ElementaryFramework\WaterPipe\HTTP\Response\ResponseStatus;

// Create the base pipe
$basePipe = new WaterPipe;

// Our login request
$basePipe->post("/api/login", function (Request $req, Response $res) {
    // Get request data sent by the client
    $body = $req->getBody();
    $username = $body["username"];
    $password = $body["password"];

    // Check data
    // Since this How-To will not handle connections to database,
    // We will made a simple comparison here...
    if ($username === $password) {
        // Create the user session
        $_SESSION["username"] = $username;
        // Send a successful signal to the client
        $res->sendJson(array(
            "success" => true
        ));
    } else {
        // Send an unsuccessful signal to the client
        $res->sendJson(array(
            "success" => false,
            "error" => "Username and password mismatch"
        ));
    }
});

// Our posts retrieving request
$basePipe->get("/api/posts/get", function (Request $req, Response $res) {
    // Retrieve posts from the database, or any other data storage...
    // Since this How-To will not handle connections to database,
    // We will just return static data here...
    $res->sendJSON(array(
      "success" => true,
      "posts" => array(
        array(
          "title" => "First blog post",
          "description" => "The first public post of this blog",
          "author" => "Nana Axel"
        ),
        array(
          "title" => "Second blog post",
          "description" => "The second public post of this blog",
          "author" => "Nana Axel"
        ),
        array(
          "title" => "Another blog post",
          "description" => "Just another public post of this blog",
          "author" => "Nana Axel"
        )
      )
    ));
});

// Our single post retrieving request
$basePipe->get("/api/posts/get/:id", function (Request $req, Response $res) {
    // Get the id parameter from the URI
    $params = $req->uri->getParams();
    $postId = intval($params["id"]);

    // Retrieve post from the database, or any other data storage...
    // Since this How-To will not handle connections to database,
    // We will just return static data here...
    $post = array();
    $success = true;

    switch ($postId) {
      case 1:
        $post = array(
          "title" => "First blog post",
          "description" => "The first public post of this blog",
          "content" => "This first post is not very excitant, just a way for me to say Hello, World !",
          "author" => "Nana Axel",
          "published_date" => "01/10/2019 06:01",
          "category" => null
        );
        break;

      case 2:
        $post = array(
          "title" => "Second blog post",
          "description" => "The second public post of this blog",
          "content" => "I love C#, Microsoft guys have built an awesome language! Now I'm developing a game engine with it. It will support all platforms and allow user to also make game scripts with Lua and Javascript :-D",
          "author" => "Nana Axel",
          "published_date" => "01/10/2019 06:05",
          "category" => "Dev"
        );
        break;

      case 3:
        $post = array(
          "title" => "Another blog post",
          "description" => "Another public post of this blog",
          "content" => "React.js or AngularJS? Who is the best? Just leave your opinion as comment ;-)",
          "author" => "Nana Axel",
          "published_date" => "01/10/2019 06:07",
          "category" => "Poll"
        );
        break;

      default:
        $success = false;
        break;
    }

    // Send the retrieved post
    $res->sendJSON(array(
      "success" => $success,
      "post" => $post
    ));
});

$basePipe->get("/assets/:type/(.+)", function (Request $req, Response $res) {
    // Retrieving parameters from the request
    $params = $req->uri->getParams();
    $type = $params["type"];
    $path = $params[1]; // Position 1 because the parameter :type occupy the position 0

    // Just send the file from our secret folder
    $res->sendFile("/path/to/our/secret/assets/folder/{$type}/{$path}");
});

$basePipe->error(ResponseStatus::NotFoundCode, function (Request $req, Response $res) {
    // Just send a forbidden response
    $res->sendText("Forbidden.", ResponseStatus::ForbiddenCode);
});

// Run the pipe and serve the API
$basePipe->run();
Enter fullscreen mode Exit fullscreen mode

Imagine that in the next ages of our project we want to change our URI, replacing /api/** to /backend/**. With our current file, we have to replace /api/** at each requests, I know it will be not too difficult, but imagine if you have much more requests.

It's for these cases (and much more others) that sub-pipe becomes important. It allow you to run an entire pipe within a single base URI. Now let us edit our file to implement a sub-pipe for our api:

<?php

// Start a session
session_start();

// Require composer autoload file
require "./vendor/autoload.php";

// Require the main WaterPipe class
use ElementaryFramework\WaterPipe\WaterPipe;

// Require the Request class
use ElementaryFramework\WaterPipe\HTTP\Request\Request;
// Require the Response class
use ElementaryFramework\WaterPipe\HTTP\Response\Response;
// Require the ResponseStatus class
use ElementaryFramework\WaterPipe\HTTP\Response\ResponseStatus;

// Create the base pipe
$basePipe = new WaterPipe;

// Create the sub-pipe for requests starting with /api/
$apiPipe = new WaterPipe;

// Our login request
$apiPipe->post("/login", function (Request $req, Response $res) {
  // Request execution...
});

// Our posts retrieving request
$apiPipe->get("/posts/get", function (Request $req, Response $res) {
  // Request execution...
});

// Our single post retrieving request
$apiPipe->get("/posts/get/:id", function (Request $req, Response $res) {
  // Request execution...
});

// Add the API sub-pipe to the base pipe
$basePipe->pipe("/api", $apiPipe);

$basePipe->get("/assets/:type/(.+)", function (Request $req, Response $res) {
  // Request execution...
});

$basePipe->error(ResponseStatus::NotFoundCode, function (Request $req, Response $res) {
  // Request execution...
});

// Run the pipe and serve the API
$basePipe->run();
Enter fullscreen mode Exit fullscreen mode

To append a sub-pipe to the root pipe, we use the pipe(string $baseUri, WaterPipe $pipe) method from the WaterPipe class. This method will tell to the root pipe to run the given sub-pipe requests when and only when the client request starts with the given base URI.

This is helpful when your file is growing, and you want to separate your api within files, like api.users.php, api.posts.php, api.assets.php, etc... and merge all these files into index.php.

Sub-pipes can also contains other sub-pipes, and so on...

Using middlewares

Now we want to allow access to our API only for connected users, and when a connected user send a request, we want to add into the response a custom HTTP header, which will hold the username. Instead of editing all request handlers to check if the user is connected and hack the response header, we can create a Middleware, to execute a specific action before the request is executed, and another before the response is sent.

class ApiMiddleware extends Middleware
{
    public function beforeExecute(Request &$request)
    {
        // If the session doesn't exists and we are not executing the login request
        if (!isset($_SESSION["username"]) && !$request->uri->is("/api/login")) {
            // Just send a forbidden response
            (new Response)->sendText("Forbidden", ResponseStatus::ForbiddenCode);
        }
    }

    public function beforeSend(Response &$response)
    {
        // If the session exists
        if (isset($_SESSION["username"])) {
            $header = $response->getHeader();
            $header->setField("X-Username", $_SESSION["username"]);
            $response->setHeader($header);
            // DO NOT send the response here, except if you have to change the response to be sent (send an error instead for example)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

And then use it into our base pipe.

$basePipe->use(new ApiMiddleware);
Enter fullscreen mode Exit fullscreen mode

Part 4 - Deploy it!

Since when used, WaterPipe handle all your incoming requests, it's important to configure your HTTP server to redirect all (or some of) these requests to the root pipe of your project (in this case, index.php).

Apache

For Apache, you have to edit the .htaccess file located in the root of your app, and add these lines:

RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
Enter fullscreen mode Exit fullscreen mode

This will send all requests to the index.php file (you can replace it with any other file which contains your root pipe).

NGiNX

Configure NGiNX is pretty easy, you just have to add (or update) the location / command of your server block. It have to look like:

location / {
    try_files $uri $uri/ /index.php?$args;
}
Enter fullscreen mode Exit fullscreen mode

This will say to NGiNX to redirect all non existing paths to the index.php file (you can replace it with any other file which contains your root pipe).

If you want to redirect all paths without distinction, you have to do:

location / {
    try_files /index.php?$args;
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

With this article, we have learn How to build a RESTful API server using PHP and the Elementary Framework. We have seen steps by steps, the basics and some advanced tips, on how to use WaterPipe, the part of the Elementary Framework responsible of handling requests and sending responses. With what we have done, you surely know how to use this package to create more than a RESTful API, but also URL routing frameworks for your projects and more!

Thank you for reading this article to the end. I'm open to any questions, suggestions, and - constructive - critics about this article, and my framework, just post a comment! Sorry if you search for an API documentation of this framework, there is no one for now, but I'm actively working on this. Also, any contribution is kindly appreciated, just open an issue, or more, a pull request!

What next?

GitHub logo ElementaryFramework / WaterPipe

URL routing framework, requests/responses handler, and HTTP client for PHP

WaterPipe Logo

WaterPipe

downloads downloads downloads downloads

A powerful routing framework and requests/responses handler for PHP

WaterPipe is a library which allows you to easily handle HTTP requests and responses with PHP, giving you all the power to build a fully RESTful API, to create a routing framework for your web application, etc...

Example

<?php
use ElementaryFramework\WaterPipe\WaterPipe;

use ElementaryFramework\WaterPipe\HTTP\Request\Request;

use ElementaryFramework\WaterPipe\HTTP\Response\Response;
use ElementaryFramework\WaterPipe\HTTP\Response\ResponseStatus;
use ElementaryFramework\WaterPipe\HTTP\Response\ResponseHeader;

// Create the root pipe
$root = new WaterPipe;

// Add a new route to the pipe with HTTP GET method (the home page)
$root->get("/", function (Request $req, Response $res) {
    $res->sendHtml("<b>Welcome
Enter fullscreen mode Exit fullscreen mode

Top comments (6)

Collapse
 
jimmylipham profile image
Jimmy Lipham

Love this, excellent job! This really allows people to get up and running quickly with something they commonly need to do in PHP... creating REST services. I'll refer a few people to this article for sure in the future!

Collapse
 
na2axl profile image
Nana Axel

Thanks for your support Jimmy 😁

Collapse
 
josephgregory profile image
josephgregory

Beau travail jeune homme

Collapse
 
na2axl profile image
Nana Axel

Merci 🙏🏾

Collapse
 
wiltek94 profile image
Steve Wiltek

Trop fort!!

Collapse
 
landryjohn profile image
Landry John Méli • Edited

Great post !! Good job 👍...