dmits/laravel-d1

Laravel integration for Cloudflare Workers services.

1.1.1 2025-11-01 03:02 UTC

This package is not auto-updated.

Last update: 2026-02-01 03:13:58 UTC


README

Latest Stable Version Total Downloads Latest Unstable Version License PHP Version Require

A seamless Laravel integration for Cloudflare D1 databases. This package allows you to use Cloudflare D1 (SQLite-compatible serverless database) with Laravel's Eloquent ORM and Query Builder, just like any other database connection.

Features

  • πŸš€ Full Laravel Integration - Use Eloquent ORM, Query Builder, and Migrations
  • πŸ”Œ PDO-Compatible - Works with Laravel's database abstraction layer
  • πŸ”’ Security-First - Built-in input validation, path traversal protection, and error sanitization
  • 🌐 Serverless Ready - Perfect for Cloudflare Workers and edge computing
  • πŸ“¦ Easy Setup - Simple configuration and environment variables
  • βœ… Production Ready - Comprehensive security measures and testing

Requirements

  • PHP 8.1 or higher
  • Laravel 9.x or higher
  • Cloudflare account with D1 database
  • Cloudflare API token with D1 read/write permissions

Installation

Install the package via Composer:

composer require dmits/laravel-d1

The package will automatically register its service provider.

Configuration

1. Get Your Cloudflare Credentials

You'll need three pieces of information from your Cloudflare account:

  • API Token: Create one at Cloudflare API Tokens with D1 read/write permissions
  • Account ID: Found in your Cloudflare dashboard URL or sidebar
  • Database ID: The UUID of your D1 database instance

2. Configure Environment Variables

Add the following to your .env file:

Required Variables

# Cloudflare API Credentials (Required)
CLOUDFLARE_TOKEN=your_api_token_here
CLOUDFLARE_ACCOUNT_ID=your_account_id_here
CLOUDFLARE_D1_DATABASE_ID=your_database_uuid_here

Optional Security Variables

# Security Configuration (Optional - defaults shown)
D1_SECURE_MODE=true                    # Enable strict validation and error masking
D1_MASK_DB_IDS=true                    # Mask database IDs in error messages (production)
D1_VALIDATE_QUERIES=true               # Validate SQL query structure
D1_ALLOW_SCHEMA_OPERATIONS=false       # Allow CREATE/DROP/ALTER (set to false in production)

Optional Performance Variables

# Performance Configuration (Optional - defaults shown)
D1_CACHE_ENABLED=true                  # Enable query result caching
D1_CACHE_TTL=300                       # Cache TTL in seconds (5 minutes)
D1_BATCH_ENABLED=true                  # Enable query batching
D1_BATCH_MAX_SIZE=102400               # Max batch size in bytes (100KB)
D1_BATCH_MAX_QUERIES=100               # Max queries per batch

Optional Rate Limiting Variables

# Rate Limiting Configuration (Optional - defaults shown)
D1_RATE_LIMIT_ENABLED=true             # Enable rate limiting
D1_RATE_LIMIT_MAX_ATTEMPTS=100         # Max requests per time window
D1_RATE_LIMIT_DECAY_SECONDS=60         # Time window in seconds

Optional Retry Configuration Variables

# Retry Configuration (Optional - defaults shown)
D1_RETRY_ENABLED=true                  # Enable automatic retries
D1_RETRY_MAX_RETRIES=3                 # Maximum retry attempts
D1_RETRY_INITIAL_DELAY_MS=100          # Initial delay in milliseconds
D1_RETRY_MAX_DELAY_MS=5000             # Maximum delay in milliseconds
D1_RETRY_BACKOFF_MULTIPLIER=2.0        # Exponential backoff multiplier

Optional EU Configuration Variables

# EU D1 Configuration (Optional - defaults shown)
D1_EU_ENABLED=false                    # Use EU endpoint for lower latency
D1_EU_API_URL=https://api.cloudflare.com/client/v4  # EU API endpoint

3. Publish Configuration (Optional)

Publish the D1 configuration file to customize security and performance settings:

php artisan vendor:publish --tag=d1-config

This creates config/d1.php with all available options.

4. Add Database Connection

Update your config/database.php file to include the D1 connection:

'connections' => [
    // ... your existing connections
    
    'd1' => [
        'driver' => 'd1',
        'prefix' => '',
        'database' => env('CLOUDFLARE_D1_DATABASE_ID', ''),
        'api' => 'https://api.cloudflare.com/client/v4',
        'auth' => [
            'token' => env('CLOUDFLARE_TOKEN', ''),
            'account_id' => env('CLOUDFLARE_ACCOUNT_ID', ''),
        ],
        // Optional: Security and performance configuration
        'rate_limit' => [
            'enabled' => env('D1_RATE_LIMIT_ENABLED', true),
            'max_attempts' => env('D1_RATE_LIMIT_MAX_ATTEMPTS', 100),
            'decay_seconds' => env('D1_RATE_LIMIT_DECAY_SECONDS', 60),
        ],
        'retry' => [
            'enabled' => env('D1_RETRY_ENABLED', true),
            'max_retries' => env('D1_RETRY_MAX_RETRIES', 3),
        ],
        'cache' => [
            'enabled' => env('D1_CACHE_ENABLED', true),
            'ttl' => env('D1_CACHE_TTL', 300),
        ],
    ],
],

4. Set Default Connection (Optional)

To use D1 as your default database connection:

'default' => env('DB_CONNECTION', 'd1'),

Or in your .env:

DB_CONNECTION=d1

Usage

Using Eloquent Models

Once configured, you can use D1 just like any Laravel database connection:

use App\Models\User;

// Create a new user
$user = User::create([
    'name' => 'John Doe',
    'email' => 'john@example.com',
]);

// Query users
$users = User::where('email', 'john@example.com')->get();

// Update
$user->update(['name' => 'Jane Doe']);

// Delete
$user->delete();

Using Query Builder

use Illuminate\Support\Facades\DB;

// Select
$users = DB::connection('d1')
    ->table('users')
    ->where('status', 'active')
    ->get();

// Insert
DB::connection('d1')->table('users')->insert([
    'name' => 'John Doe',
    'email' => 'john@example.com',
]);

// Update
DB::connection('d1')
    ->table('users')
    ->where('id', 1)
    ->update(['status' => 'inactive']);

// Raw queries
$results = DB::connection('d1')->select(
    'SELECT * FROM users WHERE created_at > ?',
    [now()->subDays(30)]
);

Using Migrations

Create migrations just like you would for any Laravel database:

php artisan make:migration create_users_table

Then in your migration:

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::connection('d1')->create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::connection('d1')->dropIfExists('users');
    }
};

Run migrations:

php artisan migrate --database=d1

Using Raw PDO (Advanced)

For advanced use cases, you can use the PDO interface directly:

use dmits\db1\D1\Pdo\D1Pdo;
use dmits\db1\CloudflareD1Connector;

$pdo = new D1Pdo(
    dsn: 'sqlite::memory:', // DSN is not used, but required for PDO
    connector: new CloudflareD1Connector(
        database: env('CLOUDFLARE_D1_DATABASE_ID'),
        token: env('CLOUDFLARE_TOKEN'),
        accountId: env('CLOUDFLARE_ACCOUNT_ID'),
        apiUrl: 'https://api.cloudflare.com/client/v4',
    ),
);

$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([1]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);

How It Works

Cloudflare D1 doesn't support direct SQL protocol connections. This package:

  1. Intercepts Laravel database queries through the d1 driver
  2. Converts queries and bindings to Cloudflare D1 API format
  3. Sends requests to Cloudflare's D1 /query endpoint
  4. Returns results in a format compatible with Laravel's database layer

All of this happens transparently - you use D1 exactly like you would MySQL, PostgreSQL, or SQLite.

Testing

This package includes comprehensive test coverage for security and performance.

Start the Test Worker

In one terminal:

cd tests/worker
npm ci
npm run start

The worker will start on http://127.0.0.1:8787.

Run Tests

# Run all tests
composer test

# Run security tests only
composer test:security

# Run performance tests only
composer test:performance

# Run specific test suite
vendor/bin/phpunit tests/D1Test.php
vendor/bin/phpunit tests/SecurityTest.php
vendor/bin/phpunit tests/PerformanceTest.php

Security Audit

Check for known vulnerabilities in dependencies:

composer audit
composer security-check  # Runs audit + security tests

Testing Configuration

For testing, use these environment variables:

# Required for testing
CLOUDFLARE_TOKEN=test
CLOUDFLARE_ACCOUNT_ID=1234
CLOUDFLARE_D1_DATABASE_ID=DB1
DB_CONNECTION=d1

# Optional: Disable security features for testing
D1_SECURE_MODE=false
D1_ALLOW_SCHEMA_OPERATIONS=true
D1_CACHE_ENABLED=false
D1_RATE_LIMIT_ENABLED=false
D1_RETRY_ENABLED=false

And in your test configuration, point to the local worker:

'api' => 'http://127.0.0.1:8787/api/client/v4',

Security

Security is our top priority. This package implements comprehensive security measures:

  • βœ… Input Validation - All parameters are validated before use
  • βœ… Path Traversal Protection - Prevents URL manipulation attacks
  • βœ… Error Message Sanitization - Prevents information disclosure (DB-IDs masked in production)
  • βœ… HTTPS Enforcement - Secure communication in production
  • βœ… SQL Injection Prevention - Parameterized queries + query structure validation
  • βœ… Rate Limiting - Prevents API abuse (configurable per connection)
  • βœ… Query Validation - Blocks dangerous SQL keywords (DROP, TRUNCATE, etc.)
  • βœ… Secure Credentials - Config-based secrets management
  • βœ… CSRF/XSS Protection - Security middleware available
  • βœ… Authorization Policies - Policies/Gates for D1 access control

Security Configuration

Publish the configuration file to customize security settings:

php artisan vendor:publish --tag=d1-config

Then configure in config/d1.php:

'security' => [
    'secure_mode' => true,              // Enable strict validation
    'mask_db_ids' => true,               // Mask DB IDs in error messages (production)
    'validate_queries' => true,          // Validate SQL query structure
    'allow_schema_operations' => false,  // Block CREATE/DROP/ALTER in production
],

Security Best Practices

  1. Never commit credentials - Always use environment variables
  2. Use HTTPS - Only allow HTTP for localhost in development
  3. Validate input - Use Laravel's validation in your application layer
  4. Use Query Builder - Prefer Eloquent/Query Builder over raw SQL
  5. Keep updated - Regularly update dependencies: composer audit
  6. Enable secure mode - Set D1_SECURE_MODE=true in production
  7. Use rate limiting - Configure appropriate limits for your use case
  8. Monitor error logs - Watch for suspicious patterns

Security Checklist

  • [ ] Credentials stored in environment variables (never in code)
  • [ ] D1_SECURE_MODE=true in production
  • [ ] D1_ALLOW_SCHEMA_OPERATIONS=false in production
  • [ ] Rate limiting configured appropriately
  • [ ] Dependencies updated (composer audit passes)
  • [ ] Error logging configured (sensitive data masked)
  • [ ] HTTPS enforced in production
  • [ ] Authorization policies implemented

For detailed security information, see SECURITY.md.

Reporting Security Issues

Please do not create public issues for security vulnerabilities.

Instead, email d.mueller@dustiiin.de with details. We respond to security reports within 48 hours.

Limitations

  • D1 supports SQLite syntax but has some limitations compared to full SQLite
  • Transactions are supported but work differently than traditional databases
  • Some advanced SQLite features may not be available
  • Queries are executed via API, so there's network latency

Refer to Cloudflare D1 documentation for full feature details.

Troubleshooting

Connection Errors

Error: "Invalid database ID format"

  • Verify your CLOUDFLARE_D1_DATABASE_ID is a valid UUID
  • Check that the database ID exists in your Cloudflare account

Error: "Authentication failed"

  • Verify your API token has D1 read/write permissions
  • Check that your CLOUDFLARE_ACCOUNT_ID is correct
  • Ensure your token hasn't expired

Query Errors

Error: "SQL query cannot be empty"

  • Ensure you're not passing empty queries
  • Check that your migrations ran successfully

Error: "Unsupported fetch mode"

  • Currently supports PDO::FETCH_ASSOC and PDO::FETCH_OBJ
  • Use Eloquent or Query Builder for best compatibility

Performance

This package is optimized for high performance with zero overhead:

  • βœ… Query Caching - Automatic caching for SELECT queries (Redis/Memcached)
  • βœ… Query Batching - Batch multiple queries into single requests (respects 100KB limit)
  • βœ… Bulk Insert Optimization - 20x faster bulk inserts via optimized SQL
  • βœ… Connection Pooling - Efficient connection management
  • βœ… Retry with Exponential Backoff - Handles EU-D1 dropouts automatically
  • βœ… Rate Limiting - Prevents API abuse while maintaining performance
  • βœ… Zero Overhead - Direct REST API, minimal latency (<50ms per query)

Performance Configuration

'performance' => [
    'cache_enabled' => true,           // Enable query result caching
    'cache_ttl' => 300,                // Cache TTL in seconds (5 minutes)
    'batch_enabled' => true,           // Enable query batching
    'batch_max_size' => 102400,        // Max batch size (100KB)
    'batch_max_queries' => 100,        // Max queries per batch
],

'retry' => [
    'enabled' => true,                 // Enable automatic retries
    'max_retries' => 3,                 // Max retry attempts
    'initial_delay_ms' => 100,         // Initial delay (exponential backoff)
    'max_delay_ms' => 5000,            // Maximum delay
    'backoff_multiplier' => 2.0,       // Backoff multiplier
],

Performance Benchmarks

  • 1000 Inserts: <200ms total (with batching)
  • Query Validation: <5ms per query
  • Cache Operations: <5ms per operation
  • Rate Limiter: <100ms for 1000 operations
  • Bulk Insert Optimization: 20x faster than individual inserts

Run performance tests:

composer test:performance

EU D1 Optimization

For EU-based applications, configure EU endpoint for lower latency:

'eu' => [
    'enabled' => true,
    'api_url' => 'https://api.cloudflare.com/client/v4', // EU endpoint
],

EU D1 Tips:

  • Use EU endpoint for EU users (lower latency)
  • Enable retry logic for handling transient dropouts
  • Use caching to reduce API calls
  • Batch queries when possible
  • Monitor slow queries (>1000ms logged automatically)

Contributing

We welcome contributions! Please see CONTRIBUTING.md for details.

When contributing:

  • Follow PSR-12 coding standards
  • Write tests for new features
  • Update documentation as needed
  • Ensure all tests pass before submitting

License

This package is open-sourced software licensed under the Apache-2.0 license.

Credits

Links

Made with ❀️ for the Laravel and Cloudflare communities