Skip to main content

Overview

Laravel Breeze API includes database migrations for authentication, sessions, and background jobs. All migrations follow Laravel’s migration conventions.

Running Migrations

Fresh Migration

php artisan migrate

Reset and Migrate

php artisan migrate:fresh

Rollback

php artisan migrate:rollback

Core Migrations

Users Table

File: database/migrations/0001_01_01_000000_create_users_table.php Creates the users table, password reset tokens table, and sessions table.
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });

        Schema::create('password_reset_tokens', function (Blueprint $table) {
            $table->string('email')->primary();
            $table->string('token');
            $table->timestamp('created_at')->nullable();
        });

        Schema::create('sessions', function (Blueprint $table) {
            $table->string('id')->primary();
            $table->foreignId('user_id')->nullable()->index();
            $table->string('ip_address', 45)->nullable();
            $table->text('user_agent')->nullable();
            $table->longText('payload');
            $table->integer('last_activity')->index();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('users');
        Schema::dropIfExists('password_reset_tokens');
        Schema::dropIfExists('sessions');
    }
};
Tables Created:

users

ColumnTypeAttributes
idBIGINT UNSIGNEDPrimary key
nameVARCHAR(255)Required
emailVARCHAR(255)Unique
email_verified_atTIMESTAMPNullable
passwordVARCHAR(255)Hashed
remember_tokenVARCHAR(100)Nullable
created_atTIMESTAMPAuto
updated_atTIMESTAMPAuto

password_reset_tokens

ColumnTypeAttributes
emailVARCHAR(255)Primary key
tokenVARCHAR(255)Required
created_atTIMESTAMPNullable

sessions

ColumnTypeAttributes
idVARCHAR(255)Primary key
user_idBIGINT UNSIGNEDForeign key, nullable, indexed
ip_addressVARCHAR(45)Nullable
user_agentTEXTNullable
payloadLONGTEXTRequired
last_activityINTEGERIndexed

Cache Table

File: database/migrations/0001_01_01_000001_create_cache_table.php
return new class extends Migration
{
    public function up(): void
    {
        Schema::create('cache', function (Blueprint $table) {
            $table->string('key')->primary();
            $table->mediumText('value');
            $table->integer('expiration')->index();
        });

        Schema::create('cache_locks', function (Blueprint $table) {
            $table->string('key')->primary();
            $table->string('owner');
            $table->integer('expiration')->index();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('cache');
        Schema::dropIfExists('cache_locks');
    }
};
Tables Created:

cache

ColumnTypeAttributes
keyVARCHAR(255)Primary key
valueMEDIUMTEXTRequired
expirationINTEGERIndexed

cache_locks

ColumnTypeAttributes
keyVARCHAR(255)Primary key
ownerVARCHAR(255)Required
expirationINTEGERIndexed

Jobs Tables

File: database/migrations/0001_01_01_000002_create_jobs_table.php
return new class extends Migration
{
    public function up(): void
    {
        Schema::create('jobs', function (Blueprint $table) {
            $table->id();
            $table->string('queue')->index();
            $table->longText('payload');
            $table->unsignedTinyInteger('attempts');
            $table->unsignedInteger('reserved_at')->nullable();
            $table->unsignedInteger('available_at');
            $table->unsignedInteger('created_at');
        });

        Schema::create('job_batches', function (Blueprint $table) {
            $table->string('id')->primary();
            $table->string('name');
            $table->integer('total_jobs');
            $table->integer('pending_jobs');
            $table->integer('failed_jobs');
            $table->longText('failed_job_ids');
            $table->mediumText('options')->nullable();
            $table->integer('cancelled_at')->nullable();
            $table->integer('created_at');
            $table->integer('finished_at')->nullable();
        });

        Schema::create('failed_jobs', function (Blueprint $table) {
            $table->id();
            $table->string('uuid')->unique();
            $table->text('connection');
            $table->text('queue');
            $table->longText('payload');
            $table->longText('exception');
            $table->timestamp('failed_at')->useCurrent();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('jobs');
        Schema::dropIfExists('job_batches');
        Schema::dropIfExists('failed_jobs');
    }
};
Tables Created:

jobs

ColumnTypeAttributes
idBIGINT UNSIGNEDPrimary key
queueVARCHAR(255)Indexed
payloadLONGTEXTRequired
attemptsTINYINT UNSIGNEDRequired
reserved_atINTEGER UNSIGNEDNullable
available_atINTEGER UNSIGNEDRequired
created_atINTEGER UNSIGNEDRequired

job_batches

ColumnTypeAttributes
idVARCHAR(255)Primary key
nameVARCHAR(255)Required
total_jobsINTEGERRequired
pending_jobsINTEGERRequired
failed_jobsINTEGERRequired
failed_job_idsLONGTEXTRequired
optionsMEDIUMTEXTNullable
cancelled_atINTEGERNullable
created_atINTEGERRequired
finished_atINTEGERNullable

failed_jobs

ColumnTypeAttributes
idBIGINT UNSIGNEDPrimary key
uuidVARCHAR(255)Unique
connectionTEXTRequired
queueTEXTRequired
payloadLONGTEXTRequired
exceptionLONGTEXTRequired
failed_atTIMESTAMPDefault: now

Personal Access Tokens Table

File: database/migrations/2026_02_06_051602_create_personal_access_tokens_table.php For Laravel Sanctum token authentication.
return new class extends Migration
{
    public function up(): void
    {
        Schema::create('personal_access_tokens', function (Blueprint $table) {
            $table->id();
            $table->morphs('tokenable');
            $table->text('name');
            $table->string('token', 64)->unique();
            $table->text('abilities')->nullable();
            $table->timestamp('last_used_at')->nullable();
            $table->timestamp('expires_at')->nullable()->index();
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('personal_access_tokens');
    }
};
Table Schema:
ColumnTypeAttributes
idBIGINT UNSIGNEDPrimary key
tokenable_typeVARCHAR(255)Polymorphic
tokenable_idBIGINT UNSIGNEDPolymorphic
nameTEXTRequired
tokenVARCHAR(64)Unique
abilitiesTEXTNullable
last_used_atTIMESTAMPNullable
expires_atTIMESTAMPNullable, indexed
created_atTIMESTAMPAuto
updated_atTIMESTAMPAuto

Models

User Model

File: app/Models/User.php
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var list<string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }
}
Model Features:
  • Traits:
    • HasFactory - Enables model factories
    • Notifiable - Enables notifications (email verification, password reset)
  • Mass Assignment:
    • name, email, password can be mass assigned
  • Hidden Attributes:
    • password and remember_token are hidden from JSON serialization
  • Attribute Casting:
    • email_verified_at cast to datetime
    • password automatically hashed

Factories

UserFactory

File: database/factories/UserFactory.php
namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;

class UserFactory extends Factory
{
    /**
     * The current password being used by the factory.
     */
    protected static ?string $password;

    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        return [
            'name' => fake()->name(),
            'email' => fake()->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => static::$password ??= Hash::make('password'),
            'remember_token' => Str::random(10),
        ];
    }

    /**
     * Indicate that the model's email address should be unverified.
     */
    public function unverified(): static
    {
        return $this->state(fn (array $attributes) => [
            'email_verified_at' => null,
        ]);
    }
}
Usage:
// Create verified user
$user = User::factory()->create();

// Create unverified user
$user = User::factory()->unverified()->create();

// Create multiple users
$users = User::factory()->count(10)->create();

Seeders

DatabaseSeeder

File: database/seeders/DatabaseSeeder.php
namespace Database\Seeders;

use App\Models\User;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    use WithoutModelEvents;

    /**
     * Seed the application's database.
     */
    public function run(): void
    {
        // User::factory(10)->create();

        User::factory()->create([
            'name' => 'Test User',
            'email' => '[email protected]',
        ]);
    }
}
Running Seeders:
# Run all seeders
php artisan db:seed

# Run specific seeder
php artisan db:seed --class=DatabaseSeeder

# Fresh migrate with seed
php artisan migrate:fresh --seed

Database Configuration

Multiple Database Support

Laravel supports MySQL, PostgreSQL, SQLite, and SQL Server. MySQL (Default):
.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=backend
DB_USERNAME=root
DB_PASSWORD=
PostgreSQL:
.env
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=backend
DB_USERNAME=postgres
DB_PASSWORD=
SQLite:
.env
DB_CONNECTION=sqlite
DB_DATABASE=/absolute/path/to/database.sqlite

Creating Custom Migrations

Generate Migration

php artisan make:migration create_posts_table

Migration Structure

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::create('posts', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained()->cascadeOnDelete();
            $table->string('title');
            $table->text('content');
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

Next Steps

Controllers

Learn about controller implementation

Testing

Write tests for your database

Build docs developers (and LLMs) love