Chapter 1: Routing

The api.php file defines all API routes.

Basic Structure

<?php
// pwa/api.php

require_once '../smartmaker-api-prepend.php';

use SmartAuth\Api\AuthController;
use SmartAuth\Api\RouteController as Route;
use MonModule\Api\ItemController;

// Authentication routes (SmartAuth)
Route::get('login',     AuthController::class, 'index');
Route::post('login',    AuthController::class, 'login');
Route::get('refresh',   AuthController::class, 'refresh');
Route::post('logout',   AuthController::class, 'logout', true);

// Your business routes
Route::get('items',         ItemController::class, 'index', true);
Route::get('items/{id}',    ItemController::class, 'show', true);
Route::post('items',        ItemController::class, 'create', true);
Route::put('items/{id}',    ItemController::class, 'update', true);
Route::delete('items/{id}', ItemController::class, 'delete', true);

// Fallback (no route matches)
json_reply('Access denied', 403);

Route Syntax

Route::action(path, Controller::class, method, protected);
Parameter Description
action HTTP method: get, post, put, delete
path API path (ex: items, items/{id})
Controller::class PHP class to call
method Class method
protected true = authentication required

Public vs Protected Routes

// Public route (accessible without token)
Route::get('public/info', InfoController::class, 'index', false);
Route::get('public/info', InfoController::class, 'index');  // false by default

// Protected route (JWT token required)
Route::get('items', ItemController::class, 'index', true);

Dynamic Parameters

Parameters in braces are passed to the controller:

// Route
Route::get('items/{id}', ItemController::class, 'show', true);
Route::get('items/{id}/files/{fileId}', ItemController::class, 'getFile', true);

// Call: GET /items/123
// In the controller:
public function show($payload)
{
    $id = $payload['id'];  // 123
}

// Call: GET /items/123/files/456
// In the controller:
public function getFile($payload)
{
    $id = $payload['id'];          // 123
    $fileId = $payload['fileId'];  // 456
}

POST/PUT Data

JSON data is automatically parsed:

// Route
Route::post('items', ItemController::class, 'create', true);

// Call: POST /items with body { "label": "Test", "price": 29.99 }
// In the controller:
public function create($payload)
{
    $label = $payload['label'];  // "Test"
    $price = $payload['price'];  // 29.99
}

Complete Example

<?php
// pwa/api.php

require_once '../smartmaker-api-prepend.php';

use SmartAuth\Api\AuthController;
use SmartAuth\Api\RouteController as Route;
use MonModule\Api\ProductController;
use MonModule\Api\CategoryController;
use MonModule\Api\FileController;

// ========================================
// AUTHENTICATION (SmartAuth)
// ========================================
Route::get('login',     AuthController::class, 'index');
Route::post('login',    AuthController::class, 'login');
Route::get('refresh',   AuthController::class, 'refresh');
Route::post('logout',   AuthController::class, 'logout', true);
Route::post('device',   AuthController::class, 'device', true);

// ========================================
// PRODUCTS
// ========================================
Route::get('products',              ProductController::class, 'index', true);
Route::get('products/{id}',         ProductController::class, 'show', true);
Route::post('products',             ProductController::class, 'create', true);
Route::put('products/{id}',         ProductController::class, 'update', true);
Route::delete('products/{id}',      ProductController::class, 'delete', true);

// Search with filters
Route::post('products/search',      ProductController::class, 'search', true);

// ========================================
// CATEGORIES
// ========================================
Route::get('categories',            CategoryController::class, 'index', true);
Route::get('categories/{id}',       CategoryController::class, 'show', true);

// ========================================
// FILES
// ========================================
Route::get('files/{element}/{parentId}/{ref}',
    FileController::class, 'download', true);

Route::post('files/{element}/{parentId}',
    FileController::class, 'upload', true);

// ========================================
// PUBLIC ROUTES
// ========================================
Route::get('public/info',           InfoController::class, 'index');

// ========================================
// FALLBACK
// ========================================
json_reply('Access denied', 403);

The smartmaker-api-prepend.php File

This file (generated by SmartBoot) initializes the environment:

<?php
// smartmaker-api-prepend.php

// Required Dolibarr headers
$res = 0;
if (!$res && file_exists("../main.inc.php")) {
    $res = @include "../main.inc.php";
}
if (!$res && file_exists("../../main.inc.php")) {
    $res = @include "../../main.inc.php";
}

// Load SmartAuth
require_once DOL_DOCUMENT_ROOT . '/smartauth/class/api.class.php';

// Autoloader for module classes
spl_autoload_register(function ($class) {
    $prefix = 'MonModule\\Api\\';
    $base_dir = __DIR__ . '/smartmaker-api/';

    $len = strlen($prefix);
    if (strncmp($prefix, $class, $len) !== 0) {
        return;
    }

    $relative_class = substr($class, $len);
    $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';

    if (file_exists($file)) {
        require $file;
    }
});

JSON Responses

Controllers return an array [data, HTTP code]:

// Success
return [['items' => $items], 200];
return [['id' => $newId], 201];  // Created
return ['Updated', 200];

// Errors
return ['Not Found', 404];
return ['Bad Request', 400];
return ['Forbidden', 403];
return ['Server Error', 500];

Key Points to Remember

  1. Route::action() to define each endpoint
  2. true as last parameter for authenticated routes
  3. Dynamic parameters with {name}
  4. Fallback required at the end of the file
  5. The controller receives everything in $payload

Back to Module | Next Chapter: Controllers ->