---
url: https://record-api-docs.vercel.app/guide/configuration.md
---

# 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
});
```

### Step 7: Queue Configuration (Optional but Recommended)

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,
    ],
];
```
