Creating a RESTful API with Flight Framework
Tags: php, restapi, tutorial
Published: 2024-06-15 20:38:03

In this article, we'll walk through building a RESTful API using the [Flight framework](https://docs.flightphp.com), which is a simple yet powerful PHP micro-framework. Flight aims to be simple to understand what is going on with your application and leave you in control without too much "magic" happening. We'll cover the basics of setting up the framework, creating a user resource in a SQLite database, and implementing a simple authentication middleware. 

### Getting Started with Flight

First, let's set up Flight and create our project. You can download Flight from its [GitHub repository](https://github.com/flightphp/core) or installed via [composer](https://getcomposer.org). Composer is generally the way to go.

#### Installation

You can install Flight via Composer. Create a new directory for your project and run the following command:

```bash
composer require flightphp/core
```

#### Setting Up Your Project

Here is the example directory structure for your project:

```
project-root/
├── public/
│   └── index.php
├── config/
│   └── config.php
├── middleware/
│   └── AuthMiddleware.php
├── vendor/
└── composer.json
```

Create an `index.php` file in your public directory. This file will serve as the entry point for your application.

```php
<?php
require __DIR__.'/../vendor/autoload.php';

Flight::route('/', function(){
    echo 'Hello, world!';
});

Flight::start();
```

Open up your terminal application and type `php -S localhost:8080 -t public/` and navigate to `http://localhost:8080` to see the "Hello, world!" message. If you get an error message about a port already being used, go ahead and try another port like `8081`.

### Creating the User Resource

Now, let's create a user resource with CRUD operations. We'll use SQLite for the database for the simplicity of this tutorial, but you can use any database of your choice.

#### Database Connection

Create a `config.php` file to store your database credentials:

```php
<?php
// config.php
return [
    'database_path' => __DIR__.'/../flight_app.sqlite',
];
```

#### CRUD Operations

In your `index.php` file, include the database connection and define CRUD operations for the user resource.

```php
<?php
require __DIR__.'/../vendor/autoload.php';
$config = require __DIR__.'/../config/config.php';

// This is where you can register a database connection so you can use it in any of your routes below
Flight::register('db', \flight\database\PdoWrapper::class, [ 'sqlite:'.$config['database_path'] ], function($db){
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
});

// This will create the database and the users table if it doesn't exist already
if(file_exists($config['database_path']) === false) {
    $db = Flight::db();
    $db->runQuery("CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT NOT NULL UNIQUE, password TEXT NOT NULL)");
}

// A group helps group together similar routes for convenience
Flight::group('/users', function(\flight\net\Router $router) {

    // Get all users
    $router->get('', function(){
        $db = Flight::db();
        $users = $db->fetchAll("SELECT * FROM users");
        Flight::json($users);
    });

    // Get user by id
    $router->get('/@id', function($id){
        $db = Flight::db();
        $user = $db->fetchRow("SELECT * FROM users WHERE id = :id", [ ':id' => $id ]);
        if (!empty($user['id'])) {
            Flight::json($user);
        } else {
            Flight::jsonHalt([ 'message' => 'User not found' ], 404);
        }
    });

    // Create new user
    $router->post('', function(){
        $data = Flight::request()->data;
        $db = Flight::db();
        $result = $db->runQuery("INSERT INTO users (name, email, password) VALUES (:name, :email, :password)", [ 
            ':name' => $data['name'], 
            ':email' => $data['email'], 
            ':password' => password_hash($data['password'], PASSWORD_BCRYPT) 
        ]);
        Flight::json([ 'id' => $db->lastInsertId() ], 201);
    });

    // Update user
    $router->put('/@id', function($id){
        $data = Flight::request()->data->getData();
        $db = Flight::db();
        $result = $db->runQuery("UPDATE users SET name = :name, email = :email, password = :password WHERE id = :id", [
            ':id' => $id,
            ':name' => $data['name'],
            ':email' => $data['email'],
            ':password' => password_hash($data['password'], PASSWORD_BCRYPT)
        ]);
        Flight::json([ 'message' => 'User updated successfully' ]);
    });

    // Delete user
    $router->delete('/@id', function($id){
        $db = Flight::db();
        $stmt = $db->runQuery("DELETE FROM users WHERE id = :id", [ ':id' => $id ]);
        Flight::json([ 'message' => 'User deleted successfully' ]);
    });

});

Flight::start();
```

### Simple Authentication Middleware

Next, let's implement a simple authentication middleware to protect our API routes.

Create a new file called `AuthMiddleware.php`:

```php
<?php
// AuthMiddleware.php

class AuthMiddleware {

    public function before() {
        $headers = Flight::request()->getHeaders();
        if (isset($headers['Authorization']) === true) {
            $token = $headers['Authorization'];
            // Normally, you would validate the token here. For simplicity, we'll just check if it's "secret"
            if ($token == 'secret') {
                return true;
            }
        }
        Flight::jsonHalt([ 'message' => 'Unauthorized' ], 401);
    }

}
```

Include this middleware in your `index.php` file and protect your routes:

```php
<?php
require __DIR__.'/../vendor/autoload.php';
$config = require __DIR__.'/../config/config.php';
require __DIR__.'/../middleware/AuthMiddleware.php';

// code to set up database connection (as shown above)

Flight::group('/users', function(\flight\net\Router $router) {

    // previously defined routes here

// This is where you put the middleware
}, [ new AuthMiddleware() ]);

// Repeat for other routes...
```

### Example Usage

To test your API, you can use tools like Postman or cURL. Here’s an example using cURL to get all users:

#### Creating a User

```bash
curl -X POST -H "Authorization: secret" -d '{"name":"John Doe", "email":"john@example.com", "password":"password"}' http://localhost:8080/users
```

This will create a new user and return the user ID.

#### Getting All Users

```bash
curl -H "Authorization: secret" http://localhost:8080/users
```

This will return a JSON response with all the users in your database.

#### Getting a User by ID

Now that you have created a user, you can get the user by ID. You can get the ID from the response of the previous request.

```bash
curl -H "Authorization: secret" http://localhost:8080/users/1
```

#### Updating a User

You can update a user by sending a PUT request with the user ID and the updated data.

```bash
curl -X PUT -H "Authorization: secret" -d '{"name":"Jane Doe", "email":"jane@example.com", "password":"mynewpassword"}' http://localhost:8080/users/1
```

#### Deleting a User

You can delete a user by sending a DELETE request with the user ID.

```bash
curl -X DELETE -H "Authorization: secret" http://localhost:8080/users/1
```

### Wrapping Up

Flight is a great choice for building simple and lightweight applications quickly. It offers the essential features needed for creating a RESTful API while being easy to learn and use. For many projects, Flight has plenty of features to help maintain the long term needs for your project. For more complex applications, you might consider using more robust frameworks like Laravel, CakePHP, or CodeIgniter, depending on your specific needs.

If you made it this far, here is the [code](https://github.com/n0nag0n/flightphp-restapi-user) used in this article.