Code Monkey home page Code Monkey logo

codeigniter-rest-api's Introduction

Codeigniter 3 API Rest

license

A RESTful server implementation for Codeigniter 3 based on CodeIgniter RestServer

Table of contents

  1. Requirements
  2. Installation
  3. Implementation
  4. Usage
  5. Postman collection

Requirements

  • PHP: >=7.4
  • Codeigniter: ^3.1.0
  • Composer

Installation

N.B: The current version 1.0.* requires php 7.4 or higher (php supported versions)

This library uses Composer to by installed.

Run this command (recommended) in the same path as your composer.json file:

composer require moudarir/codeigniter-rest-api

Or, In your composer.json file, add the following code in require section:

    {
        "require": {
          "moudarir/codeigniter-rest-api": "^1.0"
        }
    }

And then run:

composer install

Implementation

Language/Translation

You can find the file associated with your language in the application/languages/ folder. Based on the $config['language'] setting in your application/config/config.php configuration file.

Supported languages:

  • English
  • French
  • Arabic

Some changes to perform

The first thing to do is copy the rest-api.php configuration file into the config folder of your codeigniter application

Tip: If you find that the default configuration works for you, then copying the configuration file is optional.

Make sure that the enable_hooks, subclass_prefix and composer_autoload keys in application/config/config.php file are set as following:

$config['enable_hooks'] = true;
$config['subclass_prefix'] = 'Core';
$config['composer_autoload'] = true; // Or the path to 'autoload.php' file. Ex: APPPATH.'vendor/autoload.php'

Next, append the following code to your application/config/hooks.php file:

$hook['pre_system'][] = [
    'class' => 'InitAppCore',
    'function' => 'initialize',
    'filename' => 'InitAppCore.php',
    'filepath' => 'hooks'
];

Then, create a new file called InitAppCore.php in application/hooks/ folder and put the following code:

<?php
defined('BASEPATH') || exit('No direct script access allowed');

/**
 * @property InitAppCore
 */
class InitAppCore
{

    /**
     * @return void
     */
    public function initialize(): void
    {
        spl_autoload_register([__CLASS__, 'customCores']);
    }

    /**
     * @param string $class_name
     */
    public function customCores(string $class_name)
    {
        if (strpos($class_name, 'CI_') !== 0) {
            $class_file = $class_name.'.php';
            if (is_readable(APPPATH.'core'.DIRECTORY_SEPARATOR.$class_file)) {
                require_once(APPPATH.'core'.DIRECTORY_SEPARATOR.$class_file);
            }
        }
    }
}

And now, we can create a custom core class to use in our Controllers.

Referring to subclass_prefix param, we should prefix the class name with Core, Ex: /application/core/CoreApi.php.

<?php
defined('BASEPATH') || exit('No direct script access allowed');

use Moudarir\CodeigniterApi\Http\Server;

class CoreApi extends Server
{

    /**
     * CoreServer constructor.
     */
    public function __construct()
    {
        parent::__construct();
    }
}

IMPORTANT

Execute the dumping/queries.sql file to create the tables needed for the API to work properly.

Tables that will be created are users, api_keys and api_key_logs.

You're now ready to begin using the library ๐Ÿ‘Œ.

A word about controller methods and requests

Basicly, There is a relationship between controller method and request method.

If you send a GET request, the controller method MUST BE named as indexGet

Request examples:

GET /users HTTP/1.1 => indexGet
GET /users/1 HTTP/1.1 => indexGet
POST /users HTTP/1.1 => indexPost
POST /users/login HTTP/1.1 => loginPost

Usage

Adding some routes for the next example in application/config/routes.php file.

$route['users'] = [
    "get" => "users",
    "post" => "users",
    "head" => "users",
    "options" => "users"
];
$route['users(\.)([a-zA-Z0-9_-]+)(.*)'] = [
    "get" => "users/format/$2$3",
    "post" => "users/format/$2$3"
];
$route['users/([0-9]+)'] = [
    "get" => "users/index/id/$1",
    "put" => "users/index/id/$1"
];
$route['users/([0-9]+)(\.)([a-zA-Z0-9_-]+)(.*)'] = [
    "get" => "users/index/id/$1/format/$3$4",
    "put" => "users/index/id/$1/format/$3$4"
];
$route['users/login'] = [
    "post" => "users/login"
];
$route['users/login(\.)([a-zA-Z0-9_-]+)(.*)'] = [
    "post" => "users/login/format/$2$3"
];

And now, we can create our application/controllers/Users.php controller:

<?php
defined('BASEPATH') || exit('No direct script access allowed');

use Firebase\JWT\JWT;
use Moudarir\CodeigniterApi\Models\ApiKey;
use Moudarir\CodeigniterApi\Models\User;

/**
 * @property Users
 */
class Users extends CoreApi
{

    /**
     * ApiUsers constructor.
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * @see ApiUsers::indexGet()
     */
    public function indexGet()
    {
        $id = $this->get('id');
        $entity = new User();

        if ($id !== null) {
            if ((int)$id <= 0) {
                self::getResponse()->badRequest();
            }

            $item = $entity->find($id);

            if ($item === null) {
                self::getResponse()->notFound();
            }

            self::getResponse()->ok(['item' => $item]);
        }

        
        $options = [
            'page' => $this->get('page'),
            'limit' => $this->get('limit')
        ];
        $total = $entity->count($options);
        $response = [
            'total' => $total,
            'items' => $total === 0 ? [] : $entity->all($options),
        ];

        if ($options['page'] !== null) {
            $response['page'] = (int)$options['page'] === 0 ? 1 : (int)$options['page'];
        }

        self::getResponse()->ok($response);
    }

    /**
     * @see ApiUsers::indexPost()
     */
    public function indexPost()
    {
        $post = $this->post();
        $errors = [];

        if (array_key_exists('email', $post)) {
            $email = $this->post('email');

            if (empty($email) || filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
                $errors['email'] = "This field is not a valid email address.";
            }
        } else {
            $errors['email'] = "This field is required.";
        }

        if (!empty($errors)) {
            self::getResponse()->error($errors);
        }

        $hashedPassword = password_hash($post['password'], PASSWORD_ARGON2I, [
            'memory_cost' => 1 << 12, // 4MB
            'time_cost' => 2,
            'threads' => 2
        ]);
        $this->db->trans_start();
        $data = [
            'firstname' => $post['firstname'],
            'lastname' => $post['lastname'],
            'email' => $post['email'],
            'password' => $hashedPassword,
        ];
        $user_id = (new User())->add($data);

        if ($user_id === null) {
            $this->db->trans_rollback();
            self::getResponse()->error("Error occurred during account creation.");
        }

        $akEntity = new ApiKey();
        $akData = [
            'user_id' => $user_id,
            'key' => $akEntity->setKey(),
            'username' => $akEntity->setUsername(),
            'password' => $akEntity->setPassword(),
        ];
        $api_key_id = $akEntity->add($akData);

        if ($api_key_id === null) {
            $this->db->trans_rollback();
            self::getResponse()->error("Error occurred during account creation.");
        }

        if ($this->db->trans_status() === false) {
            $this->db->trans_rollback();
        } else {
            $this->db->trans_commit();
        }

        self::getResponse()->ok([
            'message' => "User account created successfully.",
            'data' => [
                'user_id' => $akData['user_id'],
                'api_key' => $akData['key'],
                'username' => $akData['username'],
                'password' => $akData['password'],
            ]
        ]);
    }

    /**
     * @see ApiUsers::indexPut()
     */
    public function indexPut()
    {
        self::getResponse()->ok([
            'data' => [
                'info' => $this->getAuthData(),
                'args' => $this->put()
            ]
        ]);
    }

    /**
     * @see ApiUsers::loginPost()
     */
    public function loginPost()
    {
        $secret = getenv("JWT_SECRET");
        $secret !== false || $secret = $this->getApiConfig()['jwt_secret'];
        $user = (new User())->find($this->getApiKey()['user_id']);
        $payload = [
            'iss' => 'http://example.org',
            'aud' => 'http://example.com',
            'iat' => 1356999524,
            'nbf' => 1357000000,
            'exp' => time() + (60 * 60),
            'user' => [
                'user_id' => $user['id'],
                'firstname' => $user['firstname'],
                'lastname' => $user['lastname'],
                'email' => $user['email'],
            ]
        ];
        self::getResponse()->ok([
            'data' => [
                'jwt_key' => JWT::encode($payload, $secret, 'HS256'),
            ]
        ]);
    }
}

Authentication methods

The Rest Server can be used/combined with basic and Bearer authorization type. However, it's can be used without any authorization type (not secure)

Postman collection

Import the collection

Downloaded our Postman collection, and import it into Postman.

Import the environment

We have also provided a Postman Environment that you need to import as well.

Note: To understand what Postman environments are, please check this link.

Edit the environment variables

Update the endpoint variable to point to your Rest server. Ex: (https//myapi.com/) with trailing slash.

โœจ That's it!

You can now use the Postman collection to test available requests.

Note: In the Postman collection, the order of execution of the requests must be respected.

codeigniter-rest-api's People

Contributors

moudarir avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.