PHPackages                             wappo/laravel-schema-api - 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. [API Development](/categories/api)
4. /
5. wappo/laravel-schema-api

ActiveLibrary[API Development](/categories/api)

wappo/laravel-schema-api
========================

This is a package that automatically exposes the DB schema in an api

v1.0.0(8mo ago)037MITPHPPHP ^8.2||^8.3||^8.4CI passing

Since Oct 16Pushed 7mo agoCompare

[ Source](https://github.com/wappoab/laravel-schema-api)[ Packagist](https://packagist.org/packages/wappo/laravel-schema-api)[ Docs](https://github.com/wappo/laravel-schema-api)[ RSS](/packages/wappo-laravel-schema-api/feed)WikiDiscussions main Synced 3w ago

READMEChangelogDependencies (8)Versions (2)Used By (0)

Laravel Schema API
==================

[](#laravel-schema-api)

[![Run Tests](https://github.com/wappoab/laravel-schema-api/actions/workflows/run-test.yml/badge.svg)](https://github.com/wappoab/laravel-schema-api/actions/workflows/run-test.yml)

Automatically expose your Laravel Eloquent models through a RESTful HTTP API with zero configuration. Perfect for building mobile apps, SPAs, or syncing data between systems.

What Does This Package Do?
--------------------------

[](#what-does-this-package-do)

Laravel Schema API automatically creates API endpoints for all your Eloquent models, giving you:

- **Instant REST API**: GET, PUT, DELETE operations on all your models without writing controllers
- **Real-Time Broadcasting**: WebSocket notifications when data changes (via Laravel Echo)
- **Streaming JSON Responses**: Memory-efficient NDJSON streaming for large datasets
- **Incremental Sync**: Built-in `?since` parameter to fetch only changed/deleted records
- **Automatic Relationships**: Stream related models with the `#[ApiInclude]` attribute
- **Cascade Deletes**: Automatically delete/restore related models when parents change
- **Schema-Based Validation**: Auto-generates validation rules from your database schema
- **Type-Safe Client Generation**: Generate TypeScript types and Vue forms from your models

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

[](#installation)

Install the package via composer:

```
composer require wappo/laravel-schema-api
```

Publish the config file (optional):

```
php artisan vendor:publish --tag="schema-api-config"
```

That's it! Your models are now accessible via `/schema-api/{table-name}`.

Quick Start
-----------

[](#quick-start)

Once installed, you can immediately access your models:

```
# List all posts
GET /schema-api/posts

# Get a specific post
GET /schema-api/posts/123

# Create, update, or delete records
PUT /schema-api/sync
```

### Example: Fetching Data

[](#example-fetching-data)

```
curl https://your-app.com/schema-api/posts
```

Response (NDJSON format - one JSON object per line):

```
{"id":"abc-123","type":"posts","attr":{"title":"Hello World","content":"..."}}
{"id":"def-456","type":"posts","attr":{"title":"Another Post","content":"..."}}
```

### Example: Incremental Sync

[](#example-incremental-sync)

Fetch only records modified since a specific timestamp:

```
# Get posts updated since 2025-01-01
GET /schema-api/posts?since=2025-01-01T00:00:00Z
```

This returns both updated records and deleted IDs (if using soft deletes).

### Example: Creating/Updating Records

[](#example-creatingupdating-records)

```
curl -X PUT https://your-app.com/schema-api/sync \
  -H "Content-Type: application/json" \
  -d '[
    {
      "op": "create",
      "type": "posts",
      "id": "new-uuid",
      "attr": {
        "title": "New Post",
        "content": "Hello!"
      }
    },
    {
      "op": "update",
      "type": "posts",
      "id": "existing-uuid",
      "attr": {
        "title": "Updated Title"
      }
    },
    {
      "op": "delete",
      "type": "posts",
      "id": "old-uuid"
    }
  ]'
```

Advanced Usage
--------------

[](#advanced-usage)

### Including Relationships

[](#including-relationships)

Stream related models alongside parent records using the `#[ApiInclude]` attribute:

```
use Wappo\LaravelSchemaApi\Attributes\ApiInclude;

class Order extends Model
{
    #[ApiInclude]
    public function rows(): HasMany
    {
        return $this->hasMany(OrderRow::class);
    }

    #[ApiInclude]
    public function owner(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
}
```

Now when you fetch orders, you'll also get the rows and owner streamed as separate root-level entities:

```
GET /schema-api/orders/123
```

Response:

```
{"id":"123","type":"orders","attr":{"number":1001}}
{"id":"row-1","type":"order_rows","attr":{"specification":"Item 1"}}
{"id":"row-2","type":"order_rows","attr":{"specification":"Item 2"}}
{"id":"user-1","type":"users","attr":{"name":"John Doe"}}
```

### Cascade Delete &amp; Restore

[](#cascade-delete--restore)

Automatically delete or restore related models when the parent changes:

```
use Wappo\LaravelSchemaApi\Attributes\ApiInclude;
use Wappo\LaravelSchemaApi\Concerns\HasApiIncludeCascadeDelete;

class Order extends Model
{
    use HasApiIncludeCascadeDelete, SoftDeletes;

    // Related rows will be automatically deleted when order is deleted
    #[ApiInclude(cascadeDelete: true)]
    public function rows(): HasMany
    {
        return $this->hasMany(OrderRow::class);
    }

    // For models without SoftDeletes, you must explicitly allow hard deletion
    #[ApiInclude(cascadeDelete: true, forceDelete: true)]
    public function metadata(): HasOne
    {
        return $this->hasOne(OrderMetadata::class);
    }
}
```

**Safety Features:**

- Models with `SoftDeletes` are always safe to cascade delete
- Models without `SoftDeletes` require `forceDelete: true` to prevent accidental data loss
- When restoring a soft-deleted parent, only related records deleted at approximately the same time are restored (configurable tolerance)

### Validation

[](#validation)

Validation rules are automatically generated from your database schema:

```
// varchar(255) → string|max:255
// integer → integer
// datetime → date
// etc.
```

Or provide custom validation rules:

```
use Wappo\LaravelSchemaApi\Attributes\UseValidationRulesProvider;
use Wappo\LaravelSchemaApi\Contracts\ValidationRulesProviderInterface;

#[UseValidationRulesProvider(PostValidationRules::class)]
class Post extends Model
{
    // ...
}

class PostValidationRules implements ValidationRulesProviderInterface
{
    public function getRules(): array
    {
        return [
            'title' => 'required|string|max:255',
            'slug' => 'required|string|unique:posts',
            'content' => 'required|string',
        ];
    }
}
```

### Query Modifiers

[](#query-modifiers)

Customize how models are queried using attributes:

```
use Wappo\LaravelSchemaApi\Attributes\ApplyQueryModifier;
use Wappo\LaravelSchemaApi\QueryModifiers\LatestFirstModifier;
use Wappo\LaravelSchemaApi\QueryModifiers\FilterQueryModifier;

#[ApplyQueryModifier(LatestFirstModifier::class)]
#[ApplyQueryModifier(FilterQueryModifier::class)]
class Post extends Model
{
    // Posts will be ordered by created_at DESC
    // and support filtering via query parameters
}
```

Built-in modifiers:

- `LatestFirstModifier` - Order by `created_at DESC`
- `UpdatedFirstModifier` - Order by `updated_at DESC`
- `SortQueryModifier` - Custom sorting
- `FilterQueryModifier` - Filter by query parameters

### Exclude Models from API

[](#exclude-models-from-api)

```
use Wappo\LaravelSchemaApi\Attributes\ApiIgnore;

#[ApiIgnore]
class AdminUser extends Model
{
    // This model will NOT be exposed via the API
}
```

If you want to exclude a model from the HTTP API but still broadcast its changes via WebSockets:

```
#[ApiIgnore(shouldBroadcast: true)]
class InternalAuditLog extends Model
{
    // Not exposed via HTTP, but changes are broadcast to authorized users
}
```

### Real-Time Broadcasting

[](#real-time-broadcasting)

Get instant notifications when data changes using Laravel's broadcasting system:

```
// config/schema-api.php
'broadcasting' => [
    'enabled' => true,
    'mode' => 'model-events', // or 'sync'
],
```

**Two Broadcasting Modes:**

1. **`sync` mode** (default) - Only broadcasts changes from the `/schema-api/sync` endpoint

    - Most predictable and transaction-safe
    - Best for apps that exclusively use the sync endpoint
2. **`model-events` mode** - Broadcasts all Eloquent model changes

    - Captures changes from sync endpoint, console commands, direct Eloquent operations, etc.
    - Respects `#[ApiIgnore]` - won't broadcast excluded models (unless `shouldBroadcast: true`)
    - Best for apps with multiple entry points for data changes

**Client-Side Setup:**

Configure Laravel Echo to listen for real-time updates:

```
// Subscribe to your user's private channel
Echo.private(`user.${userId}`)
  .listen('.model.operation', (operation) => {
    // operation = { id, type, op: 'C'|'U'|'D', attr: {...} }

    if (operation.op === 'C') {
      // Add new record to your UI
    } else if (operation.op === 'U') {
      // Update existing record
    } else if (operation.op === 'D') {
      // Remove deleted record
    }
  });
```

**Authorization:**

By default, the package checks Laravel Gates to determine which users can view a model:

```
// In your AuthServiceProvider
Gate::define('view', function (User $user, Model $model) {
    // Return true if $user can view $model
    return $user->id === $model->user_id;
});
```

For custom authorization logic, bind your own implementation:

```
use Wappo\LaravelSchemaApi\Contracts\ModelViewAuthorizerInterface;

$this->app->singleton(ModelViewAuthorizerInterface::class, function () {
    return new YourCustomAuthorizer();
});
```

**Important:** Make sure you have [Laravel Broadcasting](https://laravel.com/docs/broadcasting) configured with a driver like Pusher, Ably, or Redis.

### Custom JSON Resources

[](#custom-json-resources)

Use your own JSON resource classes:

```
use Wappo\LaravelSchemaApi\Attributes\UseSchemaApiJsonResource;

#[UseSchemaApiJsonResource(PostResource::class)]
class Post extends Model
{
    // ...
}
```

API Endpoints
-------------

[](#api-endpoints)

### List Models

[](#list-models)

```
GET /schema-api/{table}

```

**Query Parameters:**

- `?since=2025-01-01T00:00:00Z` - Incremental sync (returns modified and deleted records)
- `?gzip` - Enable gzip compression

**Response Format:** NDJSON (newline-delimited JSON)

### Get Single Model

[](#get-single-model)

```
GET /schema-api/{table}/{id}

```

**Response Format:** NDJSON with the model and any `#[ApiInclude]` relationships

### List All Available Models

[](#list-all-available-models)

```
GET /schema-api

```

Returns metadata about all exposed models.

### Batch Sync

[](#batch-sync)

```
PUT /schema-api/sync

```

**Request Body:** Array of operations

```
[
  {
    "op": "create|update|delete",
    "type": "table-name",
    "id": "record-id",
    "attr": { /* attributes */ }
  }
]
```

**Response:** NDJSON with the result of each operation

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

[](#configuration)

Publish and edit `config/schema-api.php`:

```
return [
    'date_format' => 'Y-m-d\TH:i:s.u\Z', // ISO 8601 format

    'http' => [
        'base_path' => '/schema-api',
        'middleware' => ['api'],
        'gzip_level' => 6, // 0-9, compression level
        'relationship_batch_size' => 200, // Batch size for loading relationships
    ],

    'broadcasting' => [
        'enabled' => false, // Enable WebSocket broadcasting
        'mode' => 'sync', // 'sync' or 'model-events'
    ],

    'restore_soft_delete_tolerance_in_seconds' => 1, // Tolerance for cascade restore

    'model_resolver' => [
        'driver' => 'namespace', // 'namespace' or 'morph-map'
    ],

    'resource_resolver' => [
        'driver' => 'namespace', // How to find JSON resource classes
    ],
];
```

Performance
-----------

[](#performance)

The package is optimized for large datasets:

- **Streaming Responses**: Uses cursor pagination and NDJSON to handle millions of records without memory issues
- **Efficient Relationship Loading**: Batches parent records and uses `whereIn()` to avoid N+1 queries
- **Optional Compression**: Enable gzip compression with `?gzip` parameter
- **No Model Hydration**: Uses raw queries (`toBase()`) for better performance

Example: Streaming 1 million records uses constant memory (~50MB) instead of loading everything into RAM.

Client Code Generation
----------------------

[](#client-code-generation)

Generate TypeScript types and Vue forms from your models:

```
php artisan app:generate-client-resources
```

This reads your model schemas and generates type-safe client code for your frontend.

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

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

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

Please review [our security policy](../../security/policy) on how to report security vulnerabilities.

Credits
-------

[](#credits)

- [Johan Östling](https://github.com/kjostling)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

35

—

LowBetter than 77% of packages

Maintenance63

Regular maintenance activity

Popularity7

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% 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

Unknown

Total

1

Last Release

251d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/8311f32b5012cd89e84f3411b0dc01978aa37a1ff6a72d636b7c7ca11d5020e8?d=identicon)[kjostling](/maintainers/kjostling)

---

Top Contributors

[![kjostling](https://avatars.githubusercontent.com/u/1138012?v=4)](https://github.com/kjostling "kjostling (106 commits)")

---

Tags

laravelWAPPOlaravel-schema-api

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/wappo-laravel-schema-api/health.svg)

```
[![Health](https://phpackages.com/badges/wappo-laravel-schema-api/health.svg)](https://phpackages.com/packages/wappo-laravel-schema-api)
```

###  Alternatives

[dedoc/scramble

Automatic generation of API documentation for Laravel applications.

2.1k9.9M90](/packages/dedoc-scramble)[spatie/laravel-pdf

Create PDFs in Laravel apps

1.0k4.3M42](/packages/spatie-laravel-pdf)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

815320.5k3](/packages/defstudio-telegraph)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3913.7k](/packages/rawilk-profile-filament-plugin)[simplestats-io/laravel-client

Analytics for Laravel. Track visitors, registrations, and payments. Discover which channels actually drive revenue, not just traffic. Server-side, GDPR compliant, ad-blocker proof.

5019.3k](/packages/simplestats-io-laravel-client)[spatie/laravel-github-webhooks

Handle GitHub webhooks in a Laravel application

93157.3k5](/packages/spatie-laravel-github-webhooks)

PHPackages © 2026

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