So what really is an API (Application Programming Interface)?
It's basically a way for 2 applications to communicate to each other.
In english, think of it like a Waiter in a restaurant.
- A Customer tells the Waiter his Order.
- Waiter goes to the Cook.
- Waiter comes back to Customer with the Order.
In Programming,
- An Application requests the API for a Resource.
- API goes to the Server.
- API response to Application with the Resource.
In this post, we will create a basic REST API from scratch using PHP/MySQL.
So what is a REST API?
REST stands for (REpresentational State Transfer)
It's an architecture on how we represent the resources.
In english, it's how the Waiter looks like, how the Waiter is dressed. :D
A great example would be how we would represent CSS files in a URL (Uniform Resource Locator).
We would place all CSS files inside a folder let's say the root/css/*.css.
So we would know by only looking at the URL that we are accessing some CSS file which is the resource the browser will be using for the style.
Quick Explanation:
Here are some brief explanation of the functions that we will be using.
Checks the request method used.
In this example we will be checking these methods
- GET - Just view the resource.
- POST - Adds a new resource.
- PUT - Update a resource.
- DELETE - Removes a resource.
Converts an object or an array to a json string.
- array - A PHP array.
Example:
$array = array(
'name' => 'Apple',
'price' => .5
);
echo json_encode($array);
Will give you a string.
{"name":"Apple","price":0.5}
Gets the content of a given file or URL.
For example,
file_get_contents("https://example.com");
Will get the html of https://example.com.
Returns a String containing the contents of the file/URL
Parses a given string and saves it into the 2nd argument.
For example,
parse_str("name=Apple&price=0.5",$output);
$output will contain:
array(2) {
["name"]=>
string(5) "Apple"
["price"]=>
string(3) "0.5"
}
The Resource:
A Resource is typically anything you want to be returned from your API.
First we need to think of the Resource.
Let's just use the "Restaurant - Waiter" idea.
The Resource we will have will be "Menu".
Let's say you have these Menus in a MySQL Database. And id as Primary auto incremented.
id |
name |
price |
1 |
Cheese Burger |
2.99 |
2 |
Italian Pasta |
4.99 |
3 |
Pepperoni Pizza |
9.99 |
We will also be using a PHP PDO Database Class.
The Endpoints:
An Endpoint is a URL that will tell how our API process the requests.
Since we will have a Menu Resource we are going to represent it in the URL with the following:
- GET /menus/ - This will list all menus.
- GET /menus/{id}/ - This will list a menu.
- POST /menus/ - This will add a new menu.
- PUT /menus/{id}/ - This will update a menu.
- DELETE /menus/{id}/ - This will remove a menu.
Now that we have decided on how the URL will look. Make all requests to all of these redirect to just 1 file. If you don't know how, i have created a post on Formatting a URL in htaccess
Creating a single file to catch all of these request will be much easier than to create a different file for each.
Let's just name our file api.php
Now in the file we need to detect first what Method the request is.
if( !empty($_SERVER['REQUEST_METHOD']) ){ // just making sure it's available.
// Since most of our url design may or may not have an id attached.
// We check if id is existing and save it in a variable.
$id = !empty($_GET['id']) ? trim($_GET['id'],"/") : 0;
// create a Database Instance.
$db = new Database(
"MySQLHost",
"myDatabaseName",
"myUserName",
"myUserPassword"
);
// Prepare a variable for our Response
$response = [];
switch( strtolower($_SERVER['REQUEST_METHOD']) ){
case 'get':
// do get here
break;
case 'post':
// do post here
break;
case 'put':
// do put here
break;
case 'delete':
// do delete here
break;
}
}
Each Request Method will behave differently.
get
case 'get':
if( $id ){
// Select a row from Menus with the equivalent id
$response['Menus'] = $db->Select("Select * from `Menus` where `id` = :id",[
'id' => $id
]);
}else{
// Select all rows from Menus
$response['Menus'] = $db->Select("Select * from `Menus`");
}
// output Response in the browser
die( json_encode($response) );
break;
Sending a GET /menus/1 will show:
[{"id":1,"name":"Cheese Burger","price":"2.99"}]
Sending a GET /menus/ will show:
[{"id":1,"name":"Cheese Burger","price":"2.99"},{"id":2,"name":"Italian Pasta","price":"4.99"},{"id":3,"name":"Pepperoni Pizza","price":"9.99"}]
post
case 'post':
if( !empty($_POST) ){ // checks if we have post variables
// Insert and save the row id
$menu_id = $db->Insert("Insert into `Menus`( `name` , `price`) values ( :name , :price )",[
'name' => $_POST['name'],
'price' => $_POST['price'],
]);
if( $menu_id ){ // will contain an integer id of the last inserted row.
$response['message'] = "Inserted.";
}else{
$response['message'] = "Not Inserted.";
}
}else{
// we need post data
$response['message'] = "Post Data isn't present.";
}
// output Response in the browser
die( json_encode($response) );
break;
Sending a POST /menus/ with a data name=Fries and price=1.99 will show:
[{"message":"Inserted."}]
put
case 'put':
parse_str(file_get_contents("php://input"),$putdata);
if( !empty($putdata) && $id ){
// checks if we have post variables and an id is present in the URL
$db->Update("Update `Menus` set `name` = :name, `price` = :price where `id` = :id",[
'name' => $putdata['name'],
'price' => $putdata['price'],
'id' => $id
]);
// let's check the row we updated
$exist = $db->Select("Select * from `Menus` where `id` = :id and `name` = :name and `price` = :price",[
'name' => $putdata['name'],
'price' => $putdata['price'],
'id' => $id
]);
if( $exist ){
// if the row exists
$response['message'] = "Updated.";
}else{
$response['message'] = "Not Updated.";
}
}else if( empty($putdata) ){
// we need post data
$response['message'] = "Post Data isn't present.";
}else if( empty($id) ){
// we need id
$response['message'] = "An id isn't present.";
}
// output Response in the browser
die( json_encode($response) );
break;
Note: the line parse_str(file_get_contents("php://input"),$putdata);.
There is no $_POST data in a PUT request.
The PUT Data is passed in the body of the request instead of a query string in the url.
Sending a PUT /menus/ with a data id=4 (Assuming there is a id 4 in the Menus Table you want to update), name=French Fries and price=1.49 will show:
[{"message":"Updated."}]
Take note that we are selecting the Menus Table in the checking because MySQL Update only returns number of rows affected in the update.
So for example, updating a row column with the same value will give 0 rows updated.
delete
case 'delete':
if( $id ){ // checks if we have post variables and an id is present in the URL
$db->Remove("Delete from `Menus` where id = :id",[
'id' => $id
]);
// let's check the row we updated
$exist = $db->Select("Select * from `Menus` where `id` = :id",[
'id' => $id
]);
if( !$exist ){ // if the row doesn't exists the it's deleted
$response['message'] = "Deleted.";
}else{
$response['message'] = "Not Deleted.";
}
}else{
$response['message'] = "No Id in the URL.";
}
// output Response in the browser
die( json_encode($response) );
break;
Sending a DELETE /menus/1/ will show:
[{"message":"Deleted."}]
Same with the Update we are selecting the Menus Table in the checking because MySQL Delete only returns number of rows affected in the delete.
So for example, delete an unexsiting row will give 0 rows deleted.
The Full Example:
api.php
if( !empty($_SERVER['REQUEST_METHOD']) ){ // just making sure it's available.
// Since most of our url design may or may not have an id attached.
// We check if id is existing and save it in a variable.
$id = !empty($_GET['id']) ? trim($_GET['id'],"/") : 0;
// create a Database Instance.
$db = new Database(
"MySQLHost",
"myDatabaseName",
"myUserName",
"myUserPassword"
);
// Prepare a varialbe for your Response;
$response = [];
switch( strtolower($_SERVER['REQUEST_METHOD']) ){
case 'get':
if( $id ){
// Select a row from Menus with the equivalent id
$response['Menus'] = $db->Select("Select * from `Menus` where `id` = :id",[
'id' => $id
]);
}else{
// Select all rows from Menus
$response['Menus'] = $db->Select("Select * from `Menus`");
}
// output Response in the browser
die( json_encode($response) );
break;
case 'post':
if( !empty($_POST) ){ // checks if we have post variables
// Insert and save the row id
$menu_id = $db->Insert("Insert into `Menus`( `name` , `price`) values ( :name , :price )",[
'name' => $_POST['name'],
'price' => $_POST['price'],
]);
if( $menu_id ){ // will contain an integer id of the last inserted row.
$response['message'] = "Inserted.";
}else{
$response['message'] = "Not Inserted.";
}
}else{
// we need post data
$response['message'] = "Post Data isn't present.";
}
// output Response in the browser
die( json_encode($response) );
break;
case 'put':
parse_str(file_get_contents("php://input"),$putdata);
if( !empty($putdata) && $id ){
// checks if we have post variables and an id is present in the URL
$db->Update("Update `Menus` set `name` = :name, `price` = :price where `id` = :id",[
'name' => $putdata['name'],
'price' => $putdata['price'],
'id' => $id
]);
// let's check the row we updated
$exist = $db->Select("Select * from `Menus` where `id` = :id and `name` = :name and `price` = :price",[
'name' => $putdata['name'],
'price' => $putdata['price'],
'id' => $id
]);
if( $exist ){
// if the row exists
$response['message'] = "Updated.";
}else{
$response['message'] = "Not Updated.";
}
}else if( empty($putdata) ){
// we need post data
$response['message'] = "Post Data isn't present.";
}else if( empty($id) ){
// we need id
$response['message'] = "An id isn't present.";
}
// output Response in the browser
die( json_encode($response) );
break;
case 'delete':
if( $id ){ // checks if we have post variables and an id is present in the URL
$db->Remove("Delete from `Menus` where id = :id",[
'id' => $id
]);
// let's check the row we updated
$exist = $db->Select("Select * from `Menus` where `id` = :id",[
'id' => $id
]);
if( !$exist ){ // if the row doesn't exists the it's deleted
$response['message'] = "Deleted.";
}else{
$response['message'] = "Not Deleted.";
}
}else{
$response['message'] = "No Id in the URL.";
}
// output Response in the browser
die( json_encode($response) );
break;
}
}
The Response
In our example the response output is in json (JavaScript Object Notation).
There is also xml (Extensible Markup Language).
Both are widely used and supported by many Programming Languages.
We are outputting a json so that it will be available not only in PHP but also on other Programming Languages.
For example, Our API is created in PHP and another Application was created in Java, these 2 can communicate/read the 2 formats (json & xml).
How to Send Request:
You can send request normally on a form with a method="(GET|POST|PUT|DELETE)".
Remember that our API just only send a Menu Resouce and not an HTML Resource (a full page with design).
Our Menu Resouce could be part of the of the HTML Resource, you can use ajax to send request after the content is loaded, or you could use cURL and generate the final html view before showing the HTML Resource on the page.
You can check a post about PHP cURL example on how to send a cURL request.
Tips:
- The above API still have a lot of work to do like Error Logging, Security and Authorization if you want.
So let's say i will do any request with DELETE /menus/{X}/ where {X} is a number = 0 looped till infinity, then all of your menu rows will be deleted.
- To maximize the outpot formatting it is better to use both json and xml format as output.
For example, GET /menus/json will list all menus in a json format or GET /menus/xmls for xml.
- Add appropriate headers in your Response.
For example, if your response is in json add a header('Content-Type: application/json'); before echoing the json_encode(...).
- Add response status in your response.
For example, if someone is accessing GET /menus/{id}/ but the specific records doesn't exist or someone is accessing a malformed url like GET /orange/{id}/, you should return a http status of 404.
Replies (0)
Reply