Skip to content
Discord Get Started

Laravel

Laravel connects to DB9 through PHP’s PDO PostgreSQL driver and Eloquent ORM. No special adapter or driver is needed — DB9 speaks the PostgreSQL wire protocol, so Laravel’s pgsql connection works out of the box.

  • A DB9 database (create one)
  • PHP 8.1+
  • Composer
  • Laravel 10+
Terminal
db9 create --name laravel-app

db9 create prints the connection string, with the admin password, once — copy it from the Connection String: line. It has this shape:

Output
postgresql://YOUR_TENANT_ID.admin:YOUR_PASSWORD@pg.d0000.db9.io:5432/postgres

YOUR_TENANT_ID is the tenant ID prefix in the generated connection string. Copy the username from the Connection String: line printed by db9 create or from db9 db connect. The examples below use pg.db9.io:5433, which reaches the same server; either host and port works. db9 db status laravel-app shows the connection string without the password; if you lose it, db9 db reset-password laravel-app sets a new password and prints a new connection string.

Settings that take the password on its own need it unencoded: the connection string URL-encodes it (%2B is +, %3D is =), while db9 create --show-password and db9 db reset-password also print it as plain text on the Admin Pass: line.

Set the connection details as environment variables:

Terminal
export DB9_HOST="pg.db9.io"
export DB9_PASSWORD="YOUR_PASSWORD" # unencoded

Update your .env file with the DB9 connection details:

.env
DB_CONNECTION=pgsql
DB_HOST=pg.db9.io
DB_PORT=5433
DB_DATABASE=postgres
DB_USERNAME=YOUR_TENANT_ID.admin
DB_PASSWORD=YOUR_PASSWORD

Then update the pgsql section of config/database.php to include sslmode:

config/database.php
'pgsql' => [
'driver' => 'pgsql',
'host' => env('DB_HOST', 'pg.db9.io'),
'port' => env('DB_PORT', '5433'),
'database' => env('DB_DATABASE', 'postgres'),
'username' => env('DB_USERNAME', 'YOUR_TENANT_ID.admin'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'prefix_indexes' => true,
'search_path' => 'public',
'sslmode' => 'require',
],

If the application already has app/Models/User.php and a create_users_table migration, edit those files. Generating another users migration makes migrate fail with relation "users" already exists.

Otherwise, generate the model and migration:

Terminal
php artisan make:model User -m

Generate a Post model with a migration:

Terminal
php artisan make:model Post -m

Edit the migration for users:

database/migrations/xxxx_xx_xx_create_users_table.php
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamps();
});
}

Edit the migration for posts:

database/migrations/xxxx_xx_xx_create_posts_table.php
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('body')->nullable();
$table->boolean('published')->default(false);
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->timestamps();
});
}

Add relationships to the models:

app/Models/User.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class User extends Model
{
protected $fillable = ['name', 'email'];
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
}
app/Models/Post.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Post extends Model
{
protected $fillable = ['title', 'body', 'published', 'user_id'];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
Terminal
php artisan migrate

Verify the tables exist:

Terminal
php artisan tinker
>>> \DB::select('SELECT tablename FROM pg_tables WHERE schemaname = \'public\'');

Generate a resource controller:

Terminal
php artisan make:controller UserController --resource

Add CRUD logic:

app/Http/Controllers/UserController.php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
class UserController extends Controller
{
public function index()
{
return User::all();
}
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
]);
$user = User::create($validated);
return response()->json($user, 201);
}
public function show(User $user)
{
return $user->load('posts');
}
public function update(Request $request, User $user)
{
$validated = $request->validate([
'name' => 'sometimes|string|max:255',
'email' => 'sometimes|email|unique:users,email,' . $user->id,
]);
$user->update($validated);
return response()->json($user);
}
public function destroy(User $user)
{
$user->delete();
return response()->noContent();
}
}

Register the routes:

routes/api.php
use App\Http\Controllers\UserController;
Route::apiResource('users', UserController::class);

For applications configured in bootstrap/app.php, register routes/api.php in the existing withRouting call. Creating the route file alone leaves /api/users unregistered:

bootstrap/app.php
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)

Start the server and test:

Terminal
php artisan serve
curl http://localhost:8000/api/users
  • Host and port: pg.db9.io:5433 and the pg.d0000.db9.io:5432 that db9 create prints reach the same server; port 5432 works on either host.
  • TLS: Always use sslmode=require in your database config for DB9’s hosted service.
  • Connection pooling: Set DB_CONNECTION pool size through Laravel’s database.php config or use an external pooler like PgBouncer for high-traffic apps.
  • Database name: The database is always postgres. Do not attempt to create additional databases.
  • Queue workers: If running Laravel queues with a database driver, each worker opens its own connection. Size your pool accordingly.

DB9 accepts port 5432 as well as 5433, so the client is not reaching DB9 at all. A refusal from localhost or 127.0.0.1 means Laravel is not using the DB9 host. Check your .env file:

env
DB_HOST=pg.db9.io
DB_PORT=5433

Install the PHP PostgreSQL extension. On Ubuntu/Debian:

Terminal
sudo apt install php-pgsql

On macOS with Homebrew:

Terminal
brew install php

The pdo_pgsql extension is included by default. Verify with php -m | grep pgsql.

If tables were created manually before running migrations, use the --pretend flag to check what SQL would run, then mark migrations as complete:

Terminal
php artisan migrate --pretend
php artisan migrate --path=database/migrations/xxxx_xx_xx_specific_migration.php

Confirm sslmode is set to require in your config/database.php under the pgsql connection. If you are using a DATABASE_URL environment variable, append ?sslmode=require:

env
DATABASE_URL="postgresql://YOUR_TENANT_ID.admin:YOUR_PASSWORD@pg.db9.io:5433/postgres?sslmode=require"

Verify the username format. DB9 expects <tenant_id>.admin. Use the complete username from the generated connection string or db9 db connect; laravel-app.admin fails even with the right password.