Laravel DynamoDB

repository·main·Indexed 41 days ago

https://github.com/kitar/laravel-dynamodb

An Eloquent-like model and Query Builder interface for Amazon DynamoDB. It allows Laravel developers to interact with DynamoDB using familiar syntax, providing support for CRUD operations, atomic counters, Global Secondary Indexes (GSI), and a custom User Provider for authentication.

Tokens
9.3K
Snippets
35
Records
37
Agent score
52%

What's inside Laravel DynamoDB

  1. Extend the DynamoDB Model class

    main

    The Kitar\Dynamodb\Model\Model class extends Eloquent, allowing you to use mutators and serialization. However, unlike standard Eloquent, it forwards calls to the DynamoDB Query Builder instead of a relational query builder, and it cannot handle relations.

    Required and Optional Attributes

    NameRequiredDescription
    tableyesName of the Table.
    primaryKeyyesName of the Partition Key.
    sortKeyName of the Sort Key.
    sortKeyDefaultDefault value for the Sort Key.

    Implementation Examples

    Table with only a Partition Key:

    use Kitar\Dynamodb\Model\Model;
    
    class ProductCatalog extends Model
    {
        protected $table = 'ProductCatalog';
        protected $primaryKey = 'Id';
        protected $fillable = ['Id', 'Price', 'Title'];
    }

    Table with a Sort Key:

    class Thread extends Model
    {
        protected $table = 'Thread';
        protected $primaryKey = 'ForumName';
        protected $sortKey = 'Subject';
        protected $fillable = ['ForumName', 'Subject'];
    }

    Table with a Sort Key and a Default Sort Key: If sortKeyDefault is set, it will be used when instantiating or calling find() without providing a sort key.

    class User extends Model
    {
        protected $table = 'User';
        protected $primaryKey = 'email';
        protected $sortKey = 'type';
        protected $sortKeyDefault = 'profile';
        protected $fillable = ['name', 'email', 'password', 'type'];
    }
  2. Authenticate users with DynamoDB using a custom User Provider

    main

    To use DynamoDB for user authentication in Laravel, your model must implement the Illuminate\\\Contracts\\Auth\\Authenticatable contract. You must then register a custom user provider and update your authentication configuration.

    1. Register the custom user provider

    In your App/Providers/AuthServiceProvider.php, register the dynamodb driver using Kitar\Dynamodb\Model\AuthUserProvider. This provider accepts the hasher, the model class, and optional API token configuration.

    2. Update authentication configuration

    In config/auth.php, set the driver to dynamodb and specify the model. If you are using API token authentication, you must also provide api_token_name and api_token_index.

    3. Handle unique validation for registration

    Because DynamoDB does not support the standard Laravel unique validation rule in the same way relational databases do, you should use a closure in your validation logic to manually check for existing records using User::find($value).

    // 1. Register provider in App/Providers/AuthServiceProvider.php
    use Kitar\Dynamodb\Model\AuthUserProvider;
    
    public function boot()
    {
        $this->registerPolicies();
    
        Auth::provider('dynamodb', function ($app, array $config) {
            return new AuthUserProvider(
                $app['hash'],
                $config['model'],
                $config['api_token_name'] ?? null,
                $config['api_token_index'] ?? null
            );
        });
    }
    
    // 2. Configure config/auth.php
    'providers' => [
        'users' => [
            'driver' => 'dynamodb',
            'model' => App\User::class,
            'api_token_name' => 'api_token',
            'api_token_index' => 'api_token-index'
        ],
    ],
    
    // 3. Example validation in a Controller
    $request->validate([
        'email' => [
            'required', 'string', 'lowercase', 'email', 'max:255', 
            function ($attribute, $value, $fail) {
                if (User::find($value)) {
                    $fail('The '.$attribute.' has already been taken.');
                }
            }
        ],
    ]);
  3. Use the Query Builder without a Model

    main

    You can interact with DynamoDB tables directly using the DB facade without needing to define an Eloquent-style model. This is useful for simple operations or when working outside of a standard Laravel environment by manually instantiating a Kitar\Dynamodb\Connection.

    // Inside Laravel
    $result = DB::table('Thread')->scan();
    
    // Outside Laravel
    $connection = new Kitar\Dynamodb\Connection([
        'key' => env('AWS_ACCESS_KEY_ID'),
        'secret' => env('AWS_SECRET_ACCESS_KEY'),
        'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
        'token' => env('AWS_SESSION_TOKEN', null),
        'endpoint' => env('DYNAMODB_ENDPOINT', null),
        'prefix' => '',
    ]);
    $result = $connection->table('Thread')->scan();
  4. Use DynamoDB in non-Laravel projects

    main

    If you are not using Laravel, you can manually instantiate a Kitar\Dynamodb\Connection and use the Query Builder:

    $connection = new Kitar\Dynamodb\Connection([
        'key' => env('AWS_ACCESS_KEY_ID'),
        'secret' => env('AWS_SECRET_ACCESS_KEY'),
        'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
        'token' => env('AWS_SESSION_TOKEN', null),
        'endpoint' => env('DYNAMODB_ENDPOINT', null),
        'prefix' => '',
    ]);
    
    $connection->table('your-table')->... 
  5. Paginate DynamoDB results

    main

    DynamoDB limits single operations to 1 MB of data. To retrieve more, you must use pagination via exclusiveStartKey().

    1. Perform a query/scan with a limit().
    2. Capture the LastEvaluatedKey from the response.
    3. Pass that key into the next request using exclusiveStartKey().

    If using an Eloquent Model, you can use the helper method $products->getLastEvaluatedKey().

    // Manual pagination via Query Builder
    $response = DB::table('ProductCatalog')->limit(5)->scan();
    $nextKey = $response['LastEvaluatedKey'];
    
    $nextPage = DB::table('ProductCatalog')
        ->exclusiveStartKey($nextKey)
        ->limit(5)
        ->scan();
    
    // Pagination via Model
    $products = ProductCatalog::limit(5)->scan();
    $nextKey = $products->getLastEvaluatedKey();
  6. Configure DynamoDB connection in Laravel

    main

    To use DynamoDB in a Laravel application, follow these two steps:

    1. Update config/database.php

    Add a dynamodb connection to your connections array:

    'connections' => [
        'dynamodb' => [
            'driver' => 'dynamodb',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
            'token' => env('AWS_SESSION_TOKEN', null),
            'endpoint' => env('DYNAMODB_ENDPOINT', null),
            'prefix' => '', // table prefix
        ],
        ...
    ],

    2. Update .env

    Set your default database connection to dynamodb:

    DB_CONNECTION=dynamodb

    Note for Laravel 11+: Laravel 11 and later versions default to the database driver for session, cache, and queue, which are incompatible with this package. You must switch them to alternative drivers (e.g., file or sync):

    SESSION_DRIVER=file
    CACHE_STORE=file
    QUEUE_CONNECTION=sync
    'connections' => [
        'dynamodb' => [
            'driver' => 'dynamodb',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
            'token' => env('AWS_SESSION_TOKEN', null),
            'endpoint' => env('DYNAMODB_ENDPOINT', null),
            'prefix' => '',
        ],
    ],
  7. Use specialized where clauses for DynamoDB expressions

    main

    The builder provides specialized methods to construct DynamoDB-specific expressions. These are routed to dedicated internal query builders to ensure they are compiled into the correct expression type (FilterExpression, ConditionExpression, or KeyConditionExpression).

    Available patterns include:

    • filterWhere... (e.g., filterWhereIn, filterWhereBetween)
    • conditionWhere... (e.g., conditionOrWhere, conditionBetween)
    • keyConditionWhere... (e.g., keyConditionIn)

    Note: The exact method names are generated dynamically via __call based on the combination of boolean (or), type (filter, condition, key_condition), and operator (in, between).

  8. Register a custom AuthUserProvider for DynamoDB

    main

    The Kitar\Dynamodb\Model\AuthUserProvider is a custom Laravel user provider designed to work with DynamoDB-backed models. It supports standard identifier-based authentication as well as API token-based authentication using a DynamoDB Global Secondary Index (GSI).

    To use it, you must instantiate it with the following parameters:

    • hasher: An implementation of Illuminate\Contracts\Hashing\Hasher.
    • model: The fully qualified class name of your DynamoDB user model.
    • apiTokenName (optional): The name of the attribute in your DynamoDB table that stores the API token.
    • apiTokenIndex (optional): The name of the DynamoDB index used to query the API token efficiently.

    When apiTokenName and apiTokenIndex are provided, retrieveByCredentials can look up users by their token using a key condition query on the specified index.

    // Example of how you might instantiate the provider in a ServiceProvider
    $this->app->make('auth')->provider('dynamodb', function ($app, array $config) {
        return new \Kitar\Dynamodb\Model\AuthUserProvider(
            $app->make('hash'),
            $config['model'],
            $config['api_token_name'] ?? null,
            $config['api_token_index'] ?? null
        );
    });
  9. Register the DynamoDB database driver

    main

    The DynamodbServiceProvider automatically extends Laravel's database manager to include a dynamodb driver. This allows you to use DynamoDB as a database connection within your Laravel application by specifying dynamodb as the driver in your config/database.php file.

    // In config/database.php
    'connections' => [
        'dynamodb' => [
            'driver' => 'dynamodb',
            // ... other configuration options
        ],
    ],
  10. Use AuthUserProvider for API Token authentication

    main

    The AuthUserProvider supports retrieving users via an API token if configured correctly. This is useful for stateless authentication where a client provides a token instead of a username/password.

    To enable this, ensure the provider is initialized with:

    1. An apiTokenName (the attribute name in DynamoDB).
    2. An apiTokenIndex (the GSI name in DynamoDB).

    When retrieveByCredentials is called with an array containing only the token key, the provider performs a query on the specified index:

    // If apiTokenName is 'api_token' and apiTokenIndex is 'TokenIndex'
    $credentials = ['api_token' => 'some-secret-token'];
    $user = $auth->guard()->attempt($credentials);
    // Internal logic used by the provider for token lookup:
    $model->index($this->apiTokenIndex)
         ->keyCondition($this->apiTokenName, '=', $apiToken)
         ->query()
         ->first();
  11. Define a DynamoDB Model

    main

    To use DynamoDB with Eloquent, extend the Kitar\Dynamodb\Model\Model class. You must define the table name, the partition key (primary key), and optionally a sort key.

    Key properties to set:

    • $table: The name of the DynamoDB table.
    • $primaryKey: The name of the Partition Key.
    • $sortKey: The name of the Sort Key (optional).
    • $sortKeyDefault: A default value to apply to the sort key if it is not provided during instantiation (optional).

    Note that $incrementing is automatically set to false in the constructor because DynamoDB does not use auto-incrementing integer IDs in the same way relational databases do.

    namespace App\Models;
    
    use Kitar\Dynamodb\Model\Model;
    
    class User extends Model
    {
        protected $table = 'users';
        protected $primaryKey = 'user_id';
        protected $sortKey = 'email';
        protected $sortKeyDefault = 'no-email';
    }