PHPackages                             monicahq/laradb - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. monicahq/laradb

ActiveLibrary[Database &amp; ORM](/categories/database)

monicahq/laradb
===============

A read-only database browser for Laravel: browse your tables and their rows from a single page. Supports MySQL, PostgreSQL and SQLite.

1.1.0(today)049↑2593.9%MITPHPPHP ^8.1CI failing

Since Aug 27Pushed todayCompare

[ Source](https://github.com/monicahq/laradb)[ Packagist](https://packagist.org/packages/monicahq/laradb)[ GitHub Sponsors](https://github.com/asbiin)[ GitHub Sponsors](https://github.com/djaiss)[ RSS](/packages/monicahq-laradb/feed)WikiDiscussions main Synced today

READMEChangelog (3)Dependencies (13)Versions (5)Used By (0)

[![LaraDb](github/banner.webp)](github/banner.webp)

LaraDB — A read-only database browser for your Laravel application.
===================================================================

[](#laradb--a-read-only-database-browser-for-your-laravel-application)

Have you ever had this problem?

- You develop locally a project, and need to inspect the database.
- You are too lazy to setup connection in TablePlus.
- You wish you had a way to quickly inspect the database.

LaraDB is a package that gives you a route to inspect the local db in your browser.

Install it, open `/db`, and get your tables on the left and their rows on the right — a small phpMyAdmin, without the server, the login screen, or the write access.

Works with **MySQL / MariaDB**, **PostgreSQL** and **SQLite**.

The page ships its own CSS and JavaScript.

---

⚠️ Read this before you install it
----------------------------------

[](#️-read-this-before-you-install-it)

LaraDb renders the contents of your database in a web page. Every row of every table, to anyone who can reach the URL.

- **Install it with `composer require --dev`.** A production deploy running `composer install --no-dev` then never ships the viewer at all.
- **It is disabled outside the `local` environment by default.** Turning it on anywhere else is an explicit decision you have to make.
- **Never expose it without authentication and authorisation.** The default middleware stack is `['web', 'auth']`, which is the bare minimum — any logged-in user passes it. Add a gate (see [Who can reach it](#who-can-reach-it)).
- **Do not enable it on a database holding personal or otherwise sensitive data** unless access is strictly controlled. There is no column masking, no redaction and no audit log in v1.
- It never writes: the routes are `GET` only and the package issues nothing but `SELECT` statements. That protects your data from corruption, not from being read by the wrong person.

---

Installation
------------

[](#installation)

Install it as a **development dependency**:

```
composer require --dev monicahq/laradb
```

The service provider is auto-discovered. In a `local` environment, that is all you need: visit `/db`.

Publish the config to change anything:

```
php artisan vendor:publish --tag=laradb-config
```

The views can be published too, if you want to restyle them:

```
php artisan vendor:publish --tag=laradb-views
```

Usage
-----

[](#usage)

Simply go to `/db` URL in your project, and the screen will shown. You can configure this URL - see section below.

Configuration
-------------

[](#configuration)

`config/laradb.php`:

KeyDefaultWhat it does`enabled``null``null` means "only in `local`". Set `true`/`false` (or `LARADB_ENABLED`) to decide explicitly.`route_prefix``'db'`Where the viewer is mounted.`middleware``['web', 'auth']`The middleware stack applied to both routes.`connection``null`The connection to browse, as named in `config/database.php`. `null` uses the default one.`per_page``25`Rows per page.`max_cell_length``120`Long values are truncated to this many characters, full value in the tooltip. `0` disables it.Each has an environment variable: `LARADB_ENABLED`, `LARADB_ROUTE_PREFIX`, `LARADB_CONNECTION`, `LARADB_PER_PAGE`, `LARADB_MAX_CELL_LENGTH`.

Who can reach it
----------------

[](#who-can-reach-it)

Three separate things decide that. They are a stack, not alternatives: each is a place the viewer can be stopped, and they fail independently. The warning at the top of this file names them; here is how to set each one.

**1. Whether it is installed at all.** `composer require --dev` keeps LaraDb out of a production build entirely — `composer install --no-dev` on deploy and there is no package, no service provider, no route to protect. Nothing else here is as strong, because nothing else here can be misconfigured.

**2. Whether the routes are registered.** `enabled` is `null` by default, which means "only in `local`". A staging or production box has no `/db` even if the package did end up in the build. Turning it on anywhere else is a deliberate act, and it is the point at which layer 3 stops being optional:

```
LARADB_ENABLED=true
```

**3. Who gets past the middleware.** This is the one you have to write. The default stack is `['web', 'auth']`, which only proves the visitor is *someone* — every logged-in user of your application passes it, including the test account you made six months ago. Put an authorisation check on top:

```
// app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    Gate::define('viewLaraDb', function ($user) {
        return $user->is_admin;   // whatever "may read the database" means here
    });
}
```

```
// config/laradb.php
'middleware' => ['web', 'auth', 'can:viewLaraDb'],
```

The stack applies to the whole route group, so the page, the HTML fragment and the JSON endpoint are all behind the same gate. A user who fails it gets a 403.

Emptying this setting does not turn the gate off — `null` and `[]` both fall back to `['web', 'auth']`. Laravel drops a route group's `middleware` key when it is not set, so the alternative would be publishing the whole database unauthenticated because a config value was blank. If you genuinely want the viewer reachable without logging in, ask for it: `['web']`.

### What is not one of the three

[](#what-is-not-one-of-the-three)

`route_prefix` moves the viewer; it does not hide it. A URL is not a secret: it turns up in access logs, browser history, `Referer` headers and whatever error tracker you have installed, and it is one shoulder-surf from being public. Set it because `/db` collides with a route of your own, or to keep LaraDb out of the way — not to a random string you then count as a layer of defence.

Routes
------

[](#routes)

Both routes are named and `GET` only.

RouteNameReturns`GET /db``laradb.index`The full page. `?table=` selects a table, `?page=` a page.`GET /db/tables/{table}``laradb.table`The rows of one table, as an HTML fragment.Both also accept `?column=` and `?value=`, which narrow the table to the rows whose column equals that value — this is what following a foreign key resolves to. The column has to be one a foreign key actually points at, or the request is a 404; the value is always bound, never interpolated. `?from=` is the label the chip shows, and is ignored unless it names a real foreign key.

The fragment endpoint also speaks JSON, with `?format=json` or an `Accept: application/json` header:

```
{
  "table": "users",
  "columns": [
    {"name": "id", "type": "integer", "nullable": false, "primary_key": true, "default": null, "foreign_key": null},
    {"name": "account_id", "type": "integer", "nullable": true, "primary_key": false, "default": null, "foreign_key": "accounts.id"}
  ],
  "rows": [{"id": 1, "account_id": 3, "name": "Ada"}],
  "page": 1,
  "per_page": 25,
  "total": 42,
  "last_page": 2,
  "sql": "SELECT * FROM \"users\" LIMIT 25 OFFSET 0",
  "duration_ms": 0.42,
  "filter": null
}
```

Using the core without Laravel
------------------------------

[](#using-the-core-without-laravel)

The reading side depends on nothing but PDO, so it works anywhere:

```
use LaraDb\DriverFactory;

$pdo = new PDO('sqlite:database.sqlite');
$driver = DriverFactory::fromPdo($pdo);

foreach ($driver->listTables() as $table) {
    echo $table->name, "\n";
}

$page = $driver->getRows('users', page: 2, perPage: 25);

// And the row a foreign key points at:
$account = $driver->getRows('accounts', 1, 25, new RowFilter('id', '3'));
```

`DriverInterface` is the package's public contract:

```
// Reading
public function listTables(): array;                                     // TableInfo[]
public function getColumns(string $table): array;                        // ColumnInfo[]
public function getRowCount(string $table, ?RowFilter $filter = null): int;
public function getRows(string $table, int $page, int $perPage, ?RowFilter $filter = null): TablePage;
public function name(): string;

// Describing — everything the chrome is built from
public function serverVersion(): ?string;
public function databaseName(): ?string;      // the db name, or the sqlite file
public function sizeInBytes(): ?int;
public function indexCount(): ?int;
public function metadata(): array;            // engine settings, in display order
public function getForeignKeys(string $table): array;  // column => "table.column"
public function foreignKeyTargets(): array;   // table => the columns keys point at
public function describe(): DatabaseInfo;     // all of the above, gathered once
public function queryCount(): int;
```

The describing half is nullable throughout, and deliberately so: reading a system catalogue is a privilege. A connection whose user cannot do it gets a working viewer with a quieter header, never a 500.

`foreignKeyTargets()` is what decides whether a `RowFilter` is allowed: filtering on a column it does not list raises `UnknownColumnException`, which the controller turns into a 404.

What each engine reports for `metadata()`:

KeysSQLite`page`, `journal`, `enc`, `fk`, `schema`MySQL`engine`, `charset`, `collation`PostgreSQL`enc`, `collation`, `schema`Any change to `DriverInterface` is a breaking change and gets a major version bump.

Testing
-------

[](#testing)

```
composer test          # Pint, PHPStan and PHPUnit
composer test:unit     # PHPUnit only
```

The unit and feature suites run on in-memory SQLite and need nothing installed. The MySQL and PostgreSQL suites skip themselves unless a server is reachable; point them at one with `LARADB_MYSQL_DSN` / `LARADB_PGSQL_DSN` (plus the matching `_USERNAME` and `_PASSWORD`). CI runs them against real services on every push.

Contributing
------------

[](#contributing)

Bug reports and pull requests are welcome. Please keep Pint and PHPStan green.

Changelog
---------

[](#changelog)

Releases and their notes are generated by [semantic-release](https://github.com/semantic-release/semantic-release) from the commit history. See the [releases page](https://github.com/monicahq/laradb/releases).

License
-------

[](#license)

MIT. See [LICENSE](LICENSE).

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance100

Actively maintained with recent releases

Popularity12

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 77.8% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

3

Last Release

0d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/5e66caa78edaffa8e41c951f9bb469ae0c91ecdf1e1dc7bdfee847d127a5d4d4?d=identicon)[asbin](/maintainers/asbin)

---

Top Contributors

[![djaiss](https://avatars.githubusercontent.com/u/61099?v=4)](https://github.com/djaiss "djaiss (7 commits)")[![asbiin](https://avatars.githubusercontent.com/u/25419741?v=4)](https://github.com/asbiin "asbiin (2 commits)")

---

Tags

phpbrowserlaraveldatabasemysqlsqlitepostgresqldbdeveloper-toolsViewer

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/monicahq-laradb/health.svg)

```
[![Health](https://phpackages.com/badges/monicahq-laradb/health.svg)](https://phpackages.com/packages/monicahq-laradb)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.4M354](/packages/psalm-plugin-laravel)[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.5k31.8M163](/packages/laravel-cashier)[api-platform/laravel

API Platform support for Laravel

58190.1k21](/packages/api-platform-laravel)[pressbooks/pressbooks

Pressbooks is an open source book publishing tool built on a WordPress multisite platform. Pressbooks outputs books in multiple formats, including PDF, EPUB, web, and a variety of XML flavours, using a theming/templating system, driven by CSS.

45844.8k1](/packages/pressbooks-pressbooks)[fleetbase/core-api

Core Framework and Resources for Fleetbase API

1239.7k25](/packages/fleetbase-core-api)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

265.2k](/packages/aedart-athenaeum)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
