Skip to content

Configuration ​

Configuration Guide ​

Core Configuration (config/record.php) ​

php
return [
    // Multi-tenant mode (optional)
    'enable_tenant_id' => false,
    'tenant_column' => 'tenant_id',
    'tenant_header' => 'X-Tenant-ID',
    'table_config_path' => 'records/tables',

    // API route prefix
    'api_prefix' => 'api/v1',

    // permission 
    'permission_separator' => ':', // separator for permission ex: view:invoice
    'restrict_to_own_records' => false, // limit queries to records created by the authenticated user
    'own_records_permission_prefix' => 'viewOwn', // example: viewOwn_invoice

    // Global limits
    'per_page_max' => 10000,
    'limit_max' => 10000,
    'bulk_max' => 1000,

    // Relationship nesting limit
    'max_depth' => 10,

    // Query caching (used by QueryCacheService)
    'cache' => [
        'enabled' => env('RECORD_API_CACHE_API', false),
        'ttl' => 3600,
        'prefix' => 'record_api',
        'per_table' => [],
    ],
    
    // Global RPC function configurations
    'global_functions' => [],

    // Table configurations
    'tables' => [
        // Your table configurations here
    ],
];

Large Schemas (Many Tables) ​

For applications with many tables and global RPC functions, you can split configurations into multiple files and merge them in config/record.php. For example:

php
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use Mylekha\RecordApi\Types\RecordTableType;

$tables = [
    // Core tables defined inline
];
$globalFunctions = [];

$tablesDirectory = __DIR__ . '/records/tables';
$globalFunctionsDirectory = __DIR__ . '/records/globalFunctions';

if (is_dir($tablesDirectory)) {
    $directoryIterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($tablesDirectory)
    );

    foreach ($directoryIterator as $file) {
        if (!$file->isFile() || $file->getExtension() !== 'php') {
            continue;
        }

        $path = $file->getPathname();
        $config = require $path;

        if ($config instanceof RecordTableType) {
            $name = pathinfo($path, PATHINFO_FILENAME);
            $tables[$name] = $config;
        } elseif (is_array($config)) {
            $tables = array_merge($tables, $config);
        }
    }
}

if (is_dir($globalFunctionsDirectory)) {
    $globalFunctionsDirectoryIterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($globalFunctionsDirectory)
    );

    foreach ($globalFunctionsDirectoryIterator as $file) {
        if (!$file->isFile() || $file->getExtension() !== 'php') {
            continue;
        }

        $path = $file->getPathname();
        $config = require $path;
        if (!is_array($config)) {
            continue;
        }

        $group = pathinfo((string) $path, PATHINFO_FILENAME);
        foreach ($config as $functionName => $functionConfig) {
            if (!is_string($functionName) || $functionName === '') {
                continue;
            }

            $normalizedFunctionName = ltrim($functionName, '/');
            $prefixedFunctionName = str_contains($normalizedFunctionName, '/')
                ? $normalizedFunctionName
                : $group . '/' . $normalizedFunctionName;

            $globalFunctions[$prefixedFunctionName] = $functionConfig;
        }
    }
}

return [
    'api_prefix' => 'api/v1',
    'enable_tenant_id' => false,
    'tenant_column' => 'tenant_id',
    'tenant_header' => 'X-Tenant-ID',
    'max_depth' => 10,
    'cache' => [
        'enabled' => env('RECORD_API_CACHE_API', false),
        'ttl' => 3600,
        'prefix' => 'record_api',
        'per_table' => [],
    ],
    'global_functions' => $globalFunctions,
    'tables' => $tables,
];

Each file under config/records/tables can return a single RecordTableType or an array of [table_name => RecordTableType]. Each file under config/records/globalFunctions must return an array. File name becomes group prefix for keys without / (example: auth.php + login => auth/login).

Authentication Setup ​

Record API does not ship its own authentication driver. Instead, it uses the Laravel auth guard you configure in config/record-api.php, which can point to any driver (JWT, Sanctum, Passport, etc.):

php
return [
    'auth' => [
        'guard' => env('RECORD_API_AUTH_GUARD', 'api'),
    ],
];

Configure your desired guard in config/auth.php and set RECORD_API_AUTH_GUARD accordingly.

Option A: JWT Authentication ​

bash
# Install JWT package
composer require php-open-source-saver/jwt-auth
php artisan vendor:publish --provider="PHPOpenSourceSaver\JWTAuth\Providers\LaravelServiceProvider"
php artisan jwt:secret

Configure your User model:

php
use PHPOpenSourceSaver\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements JWTSubject
{
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    public function getJWTCustomClaims()
    {
        return [];
    }
}

Option B: Laravel Sanctum ​

bash
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate

Middleware Configuration ​

Record API routes already include request.id. If you want request IDs on your own endpoints too, apply it to your routes:

php
Route::middleware(['api', 'auth:api', 'request.id'])->group(function () {
    // Your API routes
});

For optimal performance with audit logging, configure queues:

bash
# Install Redis (recommended)
composer require predis/predis

# Or use database queues
php artisan queue:table
php artisan migrate

Update your .env:

env
QUEUE_CONNECTION=redis
# or
QUEUE_CONNECTION=database

Start the queue worker:

bash
php artisan queue:work

Step 8: Permissions Setup (Optional) ​

If you install Spatie Laravel Permission, you can set it up like this:

bash
# Publish the permission migration
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"

# Run the migration
php artisan migrate

Create basic permissions for your tables:

php
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;

// Create permissions for your tables
Permission::create(['name' => 'view_users']);
Permission::create(['name' => 'create_users']);
Permission::create(['name' => 'update_users']);
Permission::create(['name' => 'delete_users']);

// Create roles and assign permissions
$adminRole = Role::create(['name' => 'admin']);
$adminRole->givePermissionTo(['view_users', 'create_users', 'update_users', 'delete_users']);

$userRole = Role::create(['name' => 'user']);
$userRole->givePermissionTo(['view_users']);

Add the trait to your User model:

php
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use HasRoles;
    
    // Your model code
}

Per-Table Custom Permission Names ​

Instead of relying on the auto-generated pmsName:action pattern, you can declare exact permission names per action directly on RecordTableType using the permissions property:

php
'items' => new RecordTableType(
    table: 'items',
    pmsName: 'item',  // fallback for any action not in the permissions map
    permissions: [
        'read'    => 'view_list_item',
        'create'  => 'insert_new_item',
        'update'  => 'update_existing_item',
        'delete'  => 'remove_item',
        'restore' => 'restore_item',
    ],
),

Partial overrides are supported — actions not listed fall back to the standard pmsName-based generation. Values can also be arrays for OR-logic: 'read' => ['view_item', 'admin_access'].

No breaking change: permissions: null (the default) preserves identical behavior to previous versions.

Configuration ​

Publishing Configuration Files ​

bash
# Publish main package configuration
php artisan vendor:publish --tag=record-api-config

# Publish all configurations (record, audit, record-api)
php artisan record-api:setup

Main Configuration (config/record-api.php) ​

php
return [
    'response' => [
        'include_request_id' => env('RECORD_API_INCLUDE_REQUEST_ID', true),
    ],
    'openapi' => [
        'output' => env('RECORD_API_OPENAPI_OUTPUT', 'storage/openapi-schema.json'),
        'info' => [
            'title' => env('APP_NAME', 'Laravel API'),
            'version' => '2.0.0',
            'description' => 'Comprehensive API documentation with dynamic CRUD operations...',
        ],
        'servers' => [
            [
                'url' => env('APP_URL', 'http://localhost'),
                'description' => 'Development server',
            ],
        ],
    ],
];

Record Configuration (config/record.php) ​

Controls database table operations and OpenAPI generation:

php
return [
    'enable_tenant_id' => false,
    'api_prefix' => 'api/v1',
    'global_functions' => [
        // Define custom RPC functions here
        // 'functionName' => YourFunctionClass::class,
    ],
    // Table-specific configurations...
];

Advanced Configuration ​

Performance Optimization ​

1. Database Optimization ​

php
// config/record.php
return [
    // Limit relationship nesting depth for better performance
    'max_depth' => 2,

    // Enable/disable query caching
    'cache' => [
        'enabled' => true,
        'ttl' => 3600,
    ],
];

2. Redis Configuration ​

env
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis

3. Queue Optimization ​

bash
# Use multiple queue workers
php artisan queue:work --queue=high,default --tries=3 --timeout=60

# Use Supervisor for production
sudo apt-get install supervisor

Security Configuration ​

1. API Rate Limiting ​

The package routes use throttle:api-reads, throttle:api-writes, and throttle:api-functions. Define those limiters in your application (example shown in the installation steps).

2. CORS Configuration ​

Laravel ships CORS configuration out of the box. Configure it in your app via config/cors.php.

3. API Versioning ​

php
// config/record.php
return [
    'api_prefix' => 'api/v1',
];

Multi-tenancy Setup ​

php
// config/record.php
return [
    'enable_tenant_id' => true,
    'tenant_column' => 'tenant_id',
    'tenant_header' => 'X-Tenant-ID',
];

When enable_tenant_id=true, requests for tables configured with hasTenantId=true must include the tenant header (default: X-Tenant-ID).

Custom Middleware ​

php
// Register custom middleware
protected $middlewareGroups = [
    'api' => [
        \App\Http\Middleware\TenantMiddleware::class,
        \Mylekha\RecordApi\Http\Middleware\RequestId::class,
        \App\Http\Middleware\ApiVersioning::class,
    ],
];

Released under the MIT License.