Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filebase Laravel Package

tmarois/filebase

tmarois/filebase is a lightweight flat-file database for Laravel and PHP. It stores records in JSON files and provides a simple, Eloquent-like API for creating, querying, and managing data without configuring a traditional database—ideal for small apps, prototypes, and local tools.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require tmarois/filebase

Three steps to first value:

  1. Pick a storage directory outside the web root — typically storage/app/filebase. Create it and make it writable by the PHP user (chown -R www-data:www-data, mode 775). The package will fail silently-ish or throw on an unwritable path, so verify this first.
  2. Register it as a singleton so every request isn't re-instantiating the store:
// app/Providers/FilebaseServiceProvider.php
use Filebase\Filebase;

public function register(): void
{
    $this->app->singleton(Filebase::class, fn () => new Filebase(
        storage_path('app/filebase'),
        ['pretty' => true, 'lock' => true],
    ));
}
  1. Use it for a settings/preferences store. This is the sweet spot: small, read-mostly, human-editable blobs that don't justify a migration.
$db->set('site.theme', 'dark');
$db->get('site.theme');        // 'dark'
$db->exists('site.theme');     // true
$db->delete('site.theme');

Where to look first: the README's API surface (set, get, exists, delete, flush, dot-notation keys) and the constructor config array (pretty, lock). Then read the source once for the locking and read/write path — you need to know whether a write rewrites the whole store, because that dictates everything about how you should (and shouldn't) use it.

Implementation Patterns

Wrap it behind an interface from day one. The whole value proposition of Filebase is that you can swap it for Redis or a real table later without touching call sites.

interface SettingsStore {
    public function get(string $key, mixed $default = null): mixed;
    public function set(string $key, mixed $value): void;
    public function forget(string $key): void;
}

final class FilebaseSettingsStore implements SettingsStore { /* delegates to Filebase */ }

Bind the interface in the container. When the flat file outgrows itself, you write one new class and change one binding.

Use dot notation for documents, not for records. Nesting config-shaped data (mail.host, mail.port) is exactly the right granularity. Using it to model thousands of "rows" (users.1043.email) is exactly the wrong one — see Gotchas.

One instance per concern. Rather than one giant store, give each domain its own directory (storage/app/filebase/settings, /content, /feature-flags). Isolation limits blast radius, makes backups obvious, and keeps individual files small enough to inspect in an editor.

Cache reads aggressively. On a request that reads the same key three times, you're opening and decoding a file three times unless the library memoizes (don't assume it does). Wrap reads in Laravel's cache with explicit invalidation on set:

public function get(string $key, mixed $default = null): mixed
{
    return Cache::rememberForever("fb:{$key}", fn () => $this->db->get($key) ?? $default);
}

public function set(string $key, mixed $value): void
{
    $this->db->set($key, $value);
    Cache::forget("fb:{$key}");
}

Seed and test with a throwaway directory. In tests, point the store at a sys_get_temp_dir() path or a per-test folder and tear it down in tearDown(). Never let the suite touch the real store — flat files don't roll back, and a stray flush() in a test is unrecoverable.

Commit the store when it's config. For site settings or feature flags that change rarely, checking the JSON into git gives you reviewable diffs, history, and rollback. Turn on pretty so those diffs are readable.

Backups are just cp. No dump tooling required — snapshot the directory before deploys that write to it, or version the files. That's a genuine advantage over a database you don't have ops access to.

Gotchas and Tips

  • Archived (last release Feb 2019). No PHP 8.x maintenance, no security patches, no upstream fixes. Vendor-and-patch or accept the risk. Test on your target PHP version before committing to it — the 2019 codebase predates a lot of modern strictness.
  • Concurrency is the real hazard. There are no transactions. Two PHP-FPM workers doing read-modify-write on the same key can silently lose an update. Turn on 'lock' => true and understand it's file locking for the write window, not a transactional guarantee. If your workload has concurrent writers, this is the wrong tool.
  • Whole-store rewrites are O(n). Every write likely re-serializes and rewrites the backing file. Fine at 50 keys, painful at 50,000. Keep stores small; split by domain rather than growing one file.
  • Crash safety is not guaranteed. A process killed mid-write can leave truncated/invalid JSON. Keep files small, back up before risky writes, and have a recovery path (.bak copy or the git history above).
  • Only JSON-friendly types survive. No closures, no resources, no binary. DateTime round-trips as a string — cast explicitly on read. Enums and value objects need manual serialization.
  • Sanitize keys that touch the filesystem. If a key ever derives from user input, you're one ../ away from writing outside your store. Whitelist the key shape with a regex at the repository boundary, not inside Filebase.
  • Long-lived processes go stale. Queue workers and Octane hold the instance across many jobs. If the library memoizes reads, an external edit or another process's write won't be visible. Resolve a fresh instance per job, or route through the cache layer above with explicit invalidation.
  • Add the store to .gitignore unless it's config-as-code. Mixing the two — tracked settings plus runtime state in the same directory — produces constant noisy merge conflicts. Separate directories, separate rules.
  • Debug by opening the file. The failure mode is almost always "this JSON doesn't parse" or "this directory isn't writable." cat the file, run it through json_decode, check json_last_error(). Turn on pretty while debugging; it costs nothing at this scale.
  • Extend by wrapping, not forking. Since it's archived, a fork becomes your maintenance burden forever. A thin decorator class lets you swap the backend when you've had enough.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource