visit
One is a software and its documentation, and this is used very liberally. For example, the old JavaScript library jQuery has a documentation so developers know how to use it. This documentation web site is referred to as the jQuery API. Other examples are documentations for , , or even . It is usually specified in the URL, even if it's not directly on the page.
Thanks to , I learned that a REST API refers to sets of pages to which clients make HTTP requests, and they return back data, possibly querying a database, in a specific structure. I like to think of a REST API as part of a filtration system:
A REST API in a filtration system
This concept is called information hiding, and it is important for simplicity and security. But before I could begin hiding information, I had to obtain information. I began by choosing a language.{
"statusCode": 200,
"data": [
{
"id": "10",
"title": "Go to Grocery Store",
"body": "Go to this grocery store",
"user_id": "115",
"completed": "1",
"time_updated": "1038829292",
"parent_id": "0",
"priority": "0",
"source": "task"
}
]
}
Once the client receives the above response and parses the JSON, it can retrieve the value of any property, such as
statusCode
.Because I was deploying on Vercel, I needed to store all the endpoints in the api directory and configure the
now.json
file. The now.json
file tells vercel-php which files were serverless functions. These serverless functions served as the API endpoints that sent the response data in JSON format. Vercel Function Logs
In addition, I've seen APIs specify their version (e.g. v1). So far, this was my file structure (note the composer.json is for including PHP libraries)api/
v1/
?
?
now.json
composer.json
<?php
header("HTTP/1.1 200 OK");
header("Content-Type: application/json; charset=UTF-8");
echo json_encode(["english" => "Hello world!", "french" => "Bonjour!", "german" => "Guten tag!"];
?>
Notice the header function calls, establishing the server response headers. The
200 OK
is an HTTP Status Code that tells the client that everything ran okay and it can now access the data. The json_encode
turns a PHP Associative Array into JSON format. The API deploys through Vercel and has the following as its now.json
file:{
"functions": {
"api/v1/*.php": {
"runtime": "[email protected]"
}
},
"routes": [
{ "src": "/v1/greeting", "dest": "/v1/greeting.php" }
]
}
The
functions
object specifies the directories that have API endpoints and will always have the same runtime property. The routes
array contains paths at the location of dest
that rewrite the url to src
. In the above example, /v1/greeting.php
simply becomes /v1/greeting
. The second level of complexity was to interact with the database. I created a separate folder called
include
and put it in the api
directory in case I wanted to have a v2
. Remember, putting everything in one file still makes it an API, but I put database handling in a separate class to make my code and modular. When I have more than one response object (such as a GET and POST request to the same endpoint, or multiple endpoints), I could call code I had already written. My modified file structure looked like this:api/
include/
Config.php
SimpleRest.php
DBHandler.php
v1/
greeting.php
now.json
composer.json
I copied the
SimpleRest
code from , expanding to fit to my API.For every response from this point, I had only to call
SimpleRest::setHttpHeaders(200)
or another status code, replacing multiple header()
calls with one method call of my own class. Database Interaction
First and foremost is a Config.php file for database interaction. I used the
mysqli_connect()
call to connect with my database credentials to the MySQL database I host on GoDaddy. Next, I stored the PHP Connection object inside an instance variable named $conn
in the DBHandler
class in the constructor. For every database-querying function, I wrote an instance method in the DBHandler class. Once my database, app, and API got larger, I expanded this into its own folder and namespace, but for now, I could keep everything in one class. For instance, I wrote the
createNewUser()
method for a POST request to the register.php
endpoint. Below is the code of this method. public function createNewUser($firstname, $lastname, $email, $password) {
if ($this->newUserValid($email)) {
$query = "INSERT INTO firstborumdatabase.users
(first_name, last_name, email, pass, registration_date)
VALUES ('$firstname', '$lastname', '$email', SHA2('$password', 512), NOW())
";
$newBorumUserResult = $this->executeQuery($query);
if (mysqli_affected_rows($dbc) == 1) { # Query ran okay
$accountId = $this->executeQuery("SELECT id FROM firstborumdatabase.users ORDER BY registration_date DESC LIMIT 1");
$accountId = mysqli_fetch_array($accountId, MYSQLI_BOTH);
$apikey = $this->generateApiKey();
// If the generated api key is taken, keep generating until a unique one is found
while ($this->apiKeyExistsInDatabase($apikey) != true) {
$apikey = $this->generateApiKey();
}
// Insert the newly created Borum user into the Borum Jot `users` table
$newBorumJotUserResult = $this->executeQuery("INSERT INTO users (borum_user_id, time_created, api_key) VALUES ($accountId, NOW(), '$apikey')");
if (mysqli_affected_rows($dbc) == 1) { # Query ran okay
return [
"ok" => true,
"statusCode" => 200
];
}
}
return [
"error" => [
"message" => "The user could not be validated at this time"
],
"statusCode" => 500
];
} else {
return [
"error" => [
"message" => "User with that email already exists on Borum"
],
"statusCode" => 500
];
}
}
case 'POST':
SimpleRest::handlePostParameterValidation("name");
$newnoteres = $handler->createNote($_POST['name']);
SimpleRest::setHttpHeaders($newnoteres["statusCode"]);
echo json_encode($newnoteres);
The
$handler
variable instantiates a new DBHandler
object (or a subclass of that). I put a statusCode
property in each JSON response so I could easily set the status code header. And that's what I did for every new response that I needed to create - I made a new method that queried the database in a
DBHandler
class. If you know how to use PHP with MySQL, and you followed everything described above, you're ready to make your own REST API! Don't I have to document it? What about unit tests? Stay tuned for Part 2, where I'll be covering all of this and more!Previously published at