A RESTful server implementation for Codeigniter 3 based on CodeIgniter RestServer
Break changes made in version
3.0
. Full code refactoring and new route system implemented inspired from Luthier CI.
- PHP:
7.4
to8.2
tested. - Codeigniter:
^3.1.13
. - Composer
The current version
3.0.*
requires php 7.4 or higher (php supported versions)
This library uses Composer to be installed.
Run this command line (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": "^3.0"
},
...
}
And then run:
composer install
You can find the file associated with your language in the application/language/
folder. Based on the $config['language']
setting in your [your-project]/application/config/config.php
configuration file.
- English
- French
- Arabic
The first thing to do is copying all required files in your CI project:
application/config/rest-api-server.php
=>[your-project]/application/config/rest-api-server.php
application/controllers/DefaultController.php
=>[your-project]/application/controllers/DefaultController.php
application/language/*/rest-api-server_lang.php
=>[your-project]/application/language/*/rest-api-server_lang.php
application/routes/*
=>[your-project]/application/routes/*
DO NOT change the
[your-project]/application/config/rest-api-server.php
and[your-project]/application/language/*/rest-api-server_lang.php
filenames.
Make sure that the enable_hooks
and composer_autoload
keys in [your-project]/application/config/config.php
file are set as following:
$config['enable_hooks'] = true;
$config['composer_autoload'] = true; // Or the path to 'autoload.php' file. Ex: APPPATH.'vendor/autoload.php'
Next, set the following code in [your-project]/application/config/hooks.php
file:
$hook = \Moudarir\CodeigniterApi\Routes\Hook::initialize();
and in [your-project]/application/config/routes.php
file:
$route = \Moudarir\CodeigniterApi\Routes\Router::getRoutes();
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
, api_key_limits
and api_key_logs
.
You're now ready to begin using the library 👌.
The implementation of old routes is deprecated. The routes are now simplified for best use. See Usage.
Adding some routes for the next example in [your-project]/application/routes/api.php
file (if not exists).
\Moudarir\CodeigniterApi\Routes\Router::group('users', ['namespace' => 'api'], function () {
\Moudarir\CodeigniterApi\Routes\Router::get('', 'apiUsers@index');
\Moudarir\CodeigniterApi\Routes\Router::post('', 'apiUsers@create');
\Moudarir\CodeigniterApi\Routes\Router::post('login', 'apiUsers@login');
\Moudarir\CodeigniterApi\Routes\Router::put('{id}', 'apiUsers@update');
\Moudarir\CodeigniterApi\Routes\Router::get('{id}', 'apiUsers@show');
});
// This will generate route array like this:
/**
$route = [
"users" => [
"GET" => "api/apiUsers/index",
"POST" => "api/apiUsers/create",
],
"users/login" => [
"POST" => "api/apiUsers/login"
],
"users/([0-9]+)" => [
"PUT" => "api/apiUsers/update/$1"
"GET" => "api/apiUsers/show/$1"
],
"default_controller" => "welcome", // Can be changed in '[your-project]/application/config/rest-api-server.php' file.
"translate_uri_dashes" => false, // Can be changed in '[your-project]/application/config/rest-api-server.php' file.
"404_override" => "pageNotFound/index", // Can be changed in '[your-project]/application/config/rest-api-server.php' file.
]
**/
And now, we can create our [your-project]/application/controllers/api/ApiUsers.php
controller:
<?php
defined('BASEPATH') || exit('No direct script access allowed');
use Firebase\JWT\JWT;
use Moudarir\CodeigniterApi\Exceptions\DatabaseCreateException;
use Moudarir\CodeigniterApi\Http\Server;
use Moudarir\CodeigniterApi\Models\ApiKey;
use Moudarir\CodeigniterApi\Models\User;
class ApiUsers extends Server
{
public function index()
{
$entity = new User();
$page = $this->get('page');
$total = $entity->count();
$response = [
'total' => $total,
'items' => $total === 0 ? [] : $entity->all(['page' => $page, 'limit' => $this->get('limit')]),
];
if ($page !== null) {
$response['page'] = (int)$page === 0 ? 1 : (int)$page;
}
self::getResponse()->ok($response);
}
public function show(int $id)
{
if ($id <= 0) {
self::getResponse()->badRequest();
}
$item = (new User())->find($id);
if ($item === null) {
self::getResponse()->notFound();
}
self::getResponse()->ok(['item' => $item]);
}
public function create()
{
$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);
}
$entity = new User();
try {
$hashedPassword = password_hash($post['password'], PASSWORD_ARGON2I, [
'memory_cost' => 1 << 12,
'time_cost' => 2,
'threads' => 2
]);
$entity::getDatabase()->trans_start();
$user = $entity
->setFirstname($post['firstname'])
->setLastname($post['lastname'])
->setEmail($post['email'])
->setPassword($hashedPassword)
->create();
$apikey = (new ApiKey())
->setUserId($user->getId())
->setKey()
->setUsername()
->setPassword()
->create();
if ($entity::getDatabase()->trans_status() === false) {
$entity::getDatabase()->trans_rollback();
} else {
$entity::getDatabase()->trans_commit();
}
self::getResponse()->ok([
'message' => "User account created successfully.",
'data' => [
'user_id' => $user->getId(),
'api_key' => $apikey->getKey(),
'username' => $apikey->getUsername(),
'password' => $apikey->getPassword(),
]
]);
} catch (DatabaseCreateException $e) {
$entity::getDatabase()->trans_rollback();
self::getResponse()->error("Error occurred during account creation.");
}
}
public function update($id)
{
self::getResponse()->ok([
'data' => [
'info' => $this->getAuthData(),
'args' => $this->put(),
'id' => $id,
]
]);
}
public function login()
{
$apiConfig = $this->getApiConfig();
$secret = getenv("JWT_SECRET");
$secret !== false || $secret = $apiConfig['jwt_secret'];
$user = (new User())->find($this->getApiKey()['user_id']);
$payload = [
'iss' => 'http://example.org',
'aud' => 'http://example.com',
'iat' => 1356999524,
'nbf' => 1357000000,
'user' => [
'user_id' => $user['id'],
'firstname' => $user['firstname'],
'lastname' => $user['lastname'],
'email' => $user['email'],
]
];
self::getResponse()->ok([
'data' => [
'jwt_key' => JWT::encode($payload, $secret, $apiConfig['jwt_algorithm']),
]
]);
}
}
The Rest Server can be used with Basic
or Bearer
authorization type. However, it can be used without any authorization type (not secure
).
The request limit currently only works in the Basic
authorization type.
Downloaded our Postman collection, and import it into Postman.
We have also provided a Postman Environment that you need to import as well.
To understand what Postman environments are, please check this link.
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.
In the Postman collection, the order of execution of the requests must be respected.
- Write routing HOW TO USE documentation.
- Improve documentation.