How I Wrote My First Web API
Tags: php, tutorial
Published: 2020-12-16 13:14:31

As part of my [Borum Jot](https://jot.bforborum.com) project, I made a Web API for all my front-end platforms to create, retrieve, update, and delete (CRUD) data on the database. In this article, I'll discuss what exactly an API is and how I made my own Web API. 

There are two main kinds of APIs. 

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 [programming languages](https://docs.oracle.com/javase/7/docs/api/), [frameworks](https://reactjs.org/docs/react-dom.html), or even [ecosystems](https://docs.microsoft.com/en-us/dotnet/api/). It is usually specified in the URL, even if it's not directly on the page. 

Thanks to [@mankms on Twitter](https://twitter.com/mankms), I learned that a **Web API** refers to sets of pages to which clients make network requests, and they return back data, possibly querying a database, in a specific structure.  I like to think of a Web API as part of a filtration system:

![API as a filtration system](https://dev-to-uploads.s3.amazonaws.com/i/r495o1f51nzi54e4vjkx.png)

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. 

# Choosing a Language and Content Type

I used PHP, a back-end language, for my API. A back-end language sits on the server and accesses data. 

I used JSON, or JavaScript Object Notation, for the content type. It is used to represent data in nested objects. In my API, I sent JSON that looked like this:

```
{
    "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`.

# Host Configuration and File Structure

I now knew I was writing in PHP to make a Web API that sends JSON to a client (the front-end). Surprisingly, where I hosted was very important at this point. I deployed on [Vercel](https://vercel.com), a hosting platform. Although Vercel is designed for JavaScript and React-type projects, the [vercel-php](https://github.com/juicyfx/vercel-php) runtime allows developers to host backend PHP projects on a secure domain (as opposed to directly on the domain registrar, which may require configuring GitHub, paying for an SSL certificate, etc.). 

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](https://dev-to-uploads.s3.amazonaws.com/i/4ibd3j4wptb69y96brb6.png)

_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
```

What would go inside the v1 subdirectory?

# Writing my First Endpoint

Finally, I could begin writing the code (the fun part!).

In its simplest form, a Web API is one PHP file that returns the JSON, such as one of the examples above, on every request. 

```
<?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": "vercel-php@0.3.1"
    },
  },
  "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`. 

---

# Helper Classes and Added Complexity

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 [DRY](https://en.wikipedia.org/wiki/Don't_repeat_yourself) 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 [another site](https://www.dropbox.com/s/eob6p9su51t6wsz/fragrances.zip?dl=0&file_subpath=%2Ffragrances%2FSimpleRest.php), expanding to fit to my  API. 

{% gist https://gist.github.com/Borumer/3b15f8075151c77f16127658736fbf10 %}

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
            ];
        }
    }
```

And in the endpoint file, I would call it and my helper methods inside of one case of a switch statement that checks the request method:

```
case 'POST':
        SimpleRest::handlePostParameterValidation("name");

        $newnoteres = $handler->createNote($_POST['name']);
        
        SimpleRest::setHttpHeaders($newnoteres["statusCode"]);
        echo json_encode($newnoteres);
break;
```

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 Web 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!