How to integrate Supabase with Laravel for database and storage

Last update: December 5th 2025
  • Configuring Laravel to use the Supabase Postgres database involves correctly adjusting the driver, schema, and environment variables.
  • Supabase's specific driver for Laravel automatically resolves common problems with UUID columns in queries and joins.
  • The Flysystem adapter allows you to treat Supabase Storage as just another Laravel disk, easily integrating file uploads.
  • Using privileged service keys and well-configured buckets is key to avoiding write errors and ensuring a stable flow.

Supabase for Laravel

If you work with Laravel and are dedicated to the backend programming and you feel like making the leap to a database modern, managed PostgreSQL like SupabaseYou've probably noticed that it's not enough to just change a couple of variables in the .env file. There are connection details, schemes, authentication, and file storage that, if neglected, can lead to some rather cryptic errors.

Furthermore, when you want to go a step further and use Supabase as file storage When integrated with Laravel's storage system, things get a bit more complex: service keys, buckets, endpoints, custom Flysystem drivers, etc. The good news is that everything—the database, storage, and UUID handling—can be perfectly integrated in a fairly clean way.

Connecting Laravel to the Supabase database

The first step is to have a working Laravel project and link it to the Postgres database provided by Supabase. For this, you need an environment with PHP and Composer updated and create a new project or use an existing one. From the console, simply generate the project with the typical Laravel command and then start setting up the connection.

Once you have the project's skeleton, the usual step is to install a simple authentication system. Laravel Breeze fits in very well because it includes Blade templates and basic login and registration flowThis allows you to quickly verify that your database connection is properly configured and that you can create users without problems.

To obtain the connection details, log in to your Supabase panel and create a new database project (you can do this directly from database.new(which redirects to the wizard). If you don't have an account yet, you'll first see the sign-up screen; if you already have one, you'll go directly to the project settings and the section where you can check the connection string.

Within the project page, in the connection section, you will find a button like "Connect" or similar. When you click it, Supabase shows you several connection string formats (URI, individual parameters, etc.). Copy the complete URI, but remember that you must replace the password for the one you actually use in the database, since a default one or a placeholder is often displayed.

With that information, you need to go to your Laravel project's .env file and update the DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, and DB_PASSWORD variables, or configure the DATABASE_URL variable if you prefer to use the full string format. The goal is for everything to point to the Supabase Postgres cluster and not to your localhost.

Configure the Postgres driver and the Supabase schema in Laravel

In Laravel, the key file for database configuration is config/database.phpAlthough you can directly use the pgsql driver that comes with the framework, when working with Supabase it is common to apply some additional adjustments, especially regarding schemas and Postgres-specific options.

A typical configuration for Postgres in Laravel might look like this, within the connections array, under the key 'pgsql':

'pgsql' => ,

The key here is in the parameter search_pathSupabase, by default, uses the public schema, which is the one exposed through its APIs. If you want to keep your Laravel application separate from that schema and avoid table or policy conflicts, it's highly recommended to change the search_path to your own schema, for example. laravel, as seen in the previous example.

This way, migrations and tables generated by your project will be created in that alternative schema and not in public. This separation makes management much easier. security rules, RLS and access from the Supabase panel without stepping on anything you shouldn't and keeping the database structure organized.

  Google I/O 2025: Dates, news and everything to expect

Once you've adjusted the configuration file, you can launch the migrations using the usual Laravel commands. This will create the authentication tables and any other tables you've defined. If everything is configured correctly, the commands will run against the Supabase Postgres without you having to do anything else.

With the migrations complete, start the development server with `artisan serve` and try registering and logging in users. If no connection or migration errors appear, it means Laravel is working. communicating correctly with Supabase and can continue building your business logic normally.

Using a specific Supabase driver in Laravel

Although the standard pgsql driver works, there is a package that adds a supabase database driver for Laravel, extending the behavior of PostgreSQL with very useful improvements, especially regarding the handling of UUID columns and the way queries are constructed.

This package, distributed as prahsys/laravel-supabase, is installed via Composer and registers an additional driver called supabase that you can use in your config/database.php file. Internally, it's based on Laravel's Postgres driver, but incorporates settings and lookup grammars optimized for the specific Supabase environment.

Once installed, you could declare something like the following within the connections section:

'connections' => ,
    // otras conexiones...
],

The advantage of using this driver is that you still get all the power of the Postgres engine, but it also automatically handles certain delicate details of Supabase, especially when working with UUIDs as primary keys or relationship fieldsThis avoids having to manually write casting for each complicated query.

The package is also designed to fit well with modern versions of the framework and PHP, offering official compatibility with Laravel 10.x, 11.x, 12.x and with PHP from version 8.1 onwards, as well as any standard PostgreSQL database, including of course Supabase.

To ensure everything works correctly, the package includes a suite of automated tests. You can run the tests with the `composer test` command, which will use an in-memory SQLite database for speed, or prepare an `.env.testing` file pointing to your Supabase and run `composer test-supabase` to verify the behavior in a real-world environment. Remote Postgres.

UUID management in Supabase and Laravel

Supabase has a peculiarity with UUID columns: if you try to compare a UUID directly with a text string without casting, the query may fail or return unexpected resultsIn a "plain" Postgres system you could solve this with global casts or custom operators, but in Supabase these global customizations are not allowed.

This implies than a direct inquiry of the style:

SELECT *
FROM users
WHERE id = '123e4567-e89b-12d3-a456-426614174000';

It won't work as you would expect. However, if you conduct the explicit casting:

SELECT *
FROM users
WHERE CAST(id AS TEXT) = '123e4567-e89b-12d3-a456-426614174000';

The query is successful. The problem is that in Laravel, when you write queries with Eloquent or the query builder, you don't want to go putting CAST in each whereThat's where the supabase driver from the aforementioned package comes into play, which takes care of adding those casts for you.

With that driver active, you can perform common queries on the table:

$user = User::find($uuidString);
$user = User::where('id', $uuidString)->first();
$users = User::whereIn('id', )->get();

And not only in direct consultations, but also in joinsFor example, if you want to retrieve posts and join them with the users table using a UUID field, you could do something like this:

$posts = Post::join('users', 'posts.user_id', '=', 'users.id')
    ->where('users.email', 'test@example.com')
    ->get();

The driver is responsible for applying the casts needed to text in the relevant UUID columns, transparently. This way, your code remains idiomatic for Laravel, and you don't need to write raw SQL or use strange tricks in every complex query.

  TurboGears Python: Everything you need to know about the most flexible web framework

If you want finer control over which columns are considered UUIDs, the package offers the trait CastsUuidColumns for your Eloquent models. Simply use it in the model class and define a protected array of additional columns:

use Prahsys\Supabase\Traits\CastsUuidColumns;

class Post extends Model
{
    use CastsUuidColumns;

    protected $uuidColumns = ;
}

This trait does three important things: it includes the primary key as the UUID by default, and it adds any column you declare in the property. $uuidColumns and communicates that information to the query builder so it knows where to apply the casts. Thus, all data access involving UUIDs becomes consistent and automated.

For even more advanced casesYou can register a custom UUID column detector. Using the PostgresGrammar package, you can specify a callback function that, based on the column name or query context, decides whether it should be treated as a UUID. For example:

use Prahsys\Supabase\Database\Query\Grammars\PostgresGrammar;

PostgresGrammar::detectUuidColumnsWith(function ($columnName, $query) {
    return str_contains($columnName, 'uuid_')
        || in_array($columnName, );
});

With this function established, the system can consider as UUIDs all columns whose name has a certain pattern or is within a specific list, adapting to very specific naming conventions of your project.

Integrate Supabase Storage as a file system in Laravel

In addition to the database, many projects need to store images, documents, or other files uploaded by users. Supabase includes a bucket-based storage service that you can leverage as Laravel disk using FlysystemFor this purpose, there is a specific adapter that allows Supabase Storage to be treated as just another driver within config/filesystems.php.

The package in question provides a Flysystem adapter that integrates transparently with the framework's Storage system. It meets the minimum requirements of PHP >= 8.1, Laravel 10.x or 11.x and the PHP fileinfo extension (ext-fileinfo), which Laravel already recommends for handling files. Installation is done with Composer, and once included, you only need to define the supabase disk in the configuration.

In the File config/filesystems.phpWithin the array disks, you would add something similar to the following:

'supabase' => ,
    ],
    'signedUrlExpires' => 60 * 60 * 24,
],

The bucket parameter is usually simply the storage bucket name that you created in the Supabase panel (for example, myapp-file-uploads). The endpoint is the base URL of the project's storage service, also visible in the corresponding section of the panel, and is usually derived from the project URL and region.

The `public` option indicates whether the bucket's contents will be treated as public by default. If `true`, the adapter will generate accessible URLs without a special signature; if `false`, the `defaultUrlGeneration` option comes into play, which can force the generation of signed URLs with an expiration time specified by `signedUrlExpires`. This configuration allows you to balance the load. safety and comfort depending on the type of files you handle.

The general disk URL is normally left as null so that the adapter automatically derives it from the endpoint. You should only touch it if you are using a proxy or intermediate CDN and you want the generated routes to point to that domain instead of the native Supabase domain.

Troubleshoot upload errors and understand the key to Supabase Storage

A fairly common problem when trying to upload files to Supabase Storage from Laravel is receiving messages like “Unable to write file at location: uploads/…”This usually indicates that, although the driver is configured, Supabase is denying the write operation due to insufficient permissions or a misconfigured key.

In the supabase disk of config/filesystems.php, the configuration mentions using a "privileged key" in the key field, explicitly stating that a read-only key will not work. This means you need to use a service key that has write permissions on the bucket, not simply a public API key intended for the client or a key intended for S3 compatibility without modification permissions.

In the Supabase panel, within the API and storage configuration section, you will find both the anonymous keys and the service_role or equivalentThese are the ones with extended privileges. It's that service key, and not the public one, that you should place in the SUPABASE_SECRET_ACCESS_KEY variable, which the driver will then read through env('SUPABASE_SECRET_ACCESS_KEY').

  Creating Functions in MySQL: A Complete Guide

If you've been testing with the S3 key from the storage configuration or with the generic API keys from the project, it's very likely that those credentials don't have permission to write to the specific bucket, resulting in the write error. Changing the key value to a valid service key with write permissions And by confirming that the bucket exists and is correctly written, the problem is usually solved.

In addition to the key, it's important to verify that the bucket defined in SUPABASE_STORAGE_BUCKET exactly matches the one created in the Supabase interface, respecting uppercase and lowercase letters, and that the endpoint corresponds to the correct URL for that storage instance. A detail such as an extra character or an incorrect domain can prevent the adapter from working. locate the actual destination of the files.

Workflow with Laravel Breeze, Blade, and Supabase Storage

Once you have your database and storage set up, the next natural step is to integrate everything with your Laravel Breeze interface and Blade templatesThis way, users can register, authenticate, and upload files to Supabase without leaving the Laravel ecosystem.

Within your controllers, you would use the Storage facade pointing to the supabase disk. For example, to upload a file received from a form with a file input, you could do something like:

if ($request->hasFile('file')) {
    $path = $request->file('file')
        ->store('uploads', 'supabase');
}

This code tells Laravel to use the disk supabase and place the file inside the virtual uploads folder within the configured bucket. If the key and endpoint are correct, the file will be uploaded to Supabase storage, and you can retrieve its path or generate public or signed URLs using standard storage methods.

The advantage of this approach is that your application maintains a single interface for storage, regardless of whether it's using local disk, Amazon S3, Supabase, or another supported service. Switching providers is reduced to adjust config/filesystems.php and the environment variables, without touching the business logic.

By combining this with Blade and Breeze, you can offer upload forms, file listings, and download links fully integrated into your application's user experience. Furthermore, Supabase Storage's bucket- and policy-based approach allows you to leverage its access controls and security rules to define what each user can see or download.

This entire ecosystem of packages, drivers, and configurations allows Laravel to work very comfortably with Supabase, both in the relational data aspect with Postgres and in the... file storage and UUID managementBy properly adjusting the keys, schemas, and drivers, you achieve a very solid integration that avoids many of the typical errors that occur when trying to connect both platforms manually and without these layers of assistance.

Connecting Laravel with Supabase for database and storage, leveraging the dedicated Supabase driver to manage UUIDs seamlessly, and using the Flysystem adapter for storage allows you to build modern applications where The entire complex infrastructure is encapsulated Behind the scenes of Laravel's clean API, from migrations and authentication to uploading files to secure buckets.

blade hastack directive in laravel
Related article:
Blade directive hasStack in Laravel and advanced stack control