PHPackages                             nylo/smalljson - 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. nylo/smalljson

ActiveLibrary[API Development](/categories/api)

nylo/smalljson
==============

Shrink Laravel JSON responses with a compact structural encoding and optional deflate. Adds response()-&gt;smallJson() and a drop-in middleware; pairs with the smalljson Dio interceptor for Flutter.

v1.0.0(1mo ago)0169↓60%MITPHPPHP ^8.1CI passing

Since Jul 13Pushed 1mo agoCompare

[ Source](https://github.com/nylo-core/laravel-small-json)[ Packagist](https://packagist.org/packages/nylo/smalljson)[ Docs](https://github.com/nylo-core/smalljson)[ RSS](/packages/nylo-smalljson/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (4)Versions (4)Used By (0)

SmallJson for Laravel
=====================

[](#smalljson-for-laravel)

Shrink your API's JSON responses with a compact, reversible structural encoding — plus optional deflate — and serve them from a familiar one-liner:

```
return response()->smallJson(new UserResource($user));
```

Clients that advertise support get the small payload; everyone else transparently gets plain JSON. The [`smalljson`](https://pub.dev/packages/smalljson) Flutter package decodes it with a drop-in Dio interceptor, so your app code never knows the encoding happened.

How it works
------------

[](#how-it-works)

SmallJson pulls every object key into a single key table and collapses uniform collections into row tuples:

```
// before — 133 bytes
[
  {"id": 1, "name": "Ada",   "email": "ada@example.com"},
  {"id": 2, "name": "Grace", "email": "grace@example.com"},
  {"id": 3, "name": "Alan",  "email": "alan@example.com"}
]

// after — 118 bytes here; the key savings multiply with every row
{"_sj":1,"m":"p","k":["id","name","email"],"b":[2,[0,1,2],[1,"Ada","ada@example.com"],[2,"Grace","grace@example.com"],[3,"Alan","alan@example.com"]]}
```

For large payloads it can additionally deflate the result (mode `z`).

> **This is compression, not encryption.** The payload is obfuscated to casual eyes, but anyone with the (open) spec can decode it — confidentiality comes from TLS.

Measured on realistic Laravel API payloads (`composer bench`):

*All sizes in KB; % saved vs plain JSON.*

Payloadplain JSONsmalljson `p`smalljson `z`plain + gzip`p` + gzipusers index, 100 rows + meta37.5 KB24.7 KB (−34.0%)5.2 KB (−86.0%)4.0 KB (−89.3%)3.9 KB (−89.5%)users index, 15 rows5.5 KB3.8 KB (−31.4%)1.2 KB (−78.7%)0.9 KB (−83.8%)0.9 KB (−83.8%)single user + 10 posts1.7 KB1.4 KB (−21.6%)0.7 KB (−62.0%)0.5 KB (−73.3%)0.5 KB (−71.0%)Full benchmark output```
users index (100 rows + meta)
----------------------------------------------------------
  plain JSON                     37.5 KB
  smalljson (p)                  24.7 KB   34.0%
  smalljson (z)                   5.2 KB   86.0%
  plain + http gzip               4.0 KB   89.3%
  smalljson (p) + http gzip       3.9 KB   89.5%

users index (15 rows)
----------------------------------------------------------
  plain JSON                      5.5 KB
  smalljson (p)                   3.8 KB   31.4%
  smalljson (z)                   1.2 KB   78.7%
  plain + http gzip               0.9 KB   83.8%
  smalljson (p) + http gzip       0.9 KB   83.8%

single user + 10 posts
----------------------------------------------------------
  plain JSON                      1.7 KB
  smalljson (p)                   1.4 KB   21.6%
  smalljson (z)                   0.7 KB   62.0%
  plain + http gzip               0.5 KB   73.3%
  smalljson (p) + http gzip       0.5 KB   71.0%

```

Read that table honestly: **if your web server already gzips JSON responses, SmallJson's byte savings are modest.** It shines when HTTP compression isn't in play — chunked/streamed responses, misconfigured proxies, shared hosting, internal service calls — and mode `z`brings its own compression with it. When the encoded form wouldn't be smaller, SmallJson automatically sends plain JSON instead, so it never costs you bytes.

Requirements
------------

[](#requirements)

- PHP 8.1+ (`ext-json`, `ext-zlib`)
- Laravel 10, 11, 12 or 13

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

[](#installation)

```
composer require nylo/smalljson
```

The service provider and `SmallJson` facade are auto-discovered. Optionally publish the config:

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

Usage
-----

[](#usage)

### The macro

[](#the-macro)

Works exactly like `response()->json()` — same arguments, same serialisation semantics:

```
Route::get('/users', function () {
    return response()->smallJson(User::query()->paginate());
});

return response()->smallJson(new UserResource($user), 201, ['X-Request-Id' => $id]);
```

### The middleware (zero controller changes)

[](#the-middleware-zero-controller-changes)

Prefer this if you want existing endpoints — resource responses, paginators, even JSON validation errors — encoded without touching any controller:

```
// per route / group
Route::middleware('smalljson')->group(function () {
    Route::apiResource('users', UserController::class);
});

// or for the whole API, in bootstrap/app.php (Laravel 11+)
->withMiddleware(function (Middleware $middleware) {
    $middleware->api(append: [\SmallJson\Http\Middleware\SmallJsonResponses::class]);
})
```

The middleware only rewrites `JsonResponse`s, skips `HEAD` requests, and leaves anything that isn't plain JSON (JSONP, binary, streams) untouched.

> Returning a resource from a route wraps it in `data` before the middleware runs, so the wrapper is preserved. The macro mirrors `response()->json()` instead, which — like Laravel itself — does not apply resource wrapping.

### Manual encode/decode

[](#manual-encodedecode)

```
use SmallJson\Facades\SmallJson;

$envelope = SmallJson::encode($data);          // string, honours config modes
$data = SmallJson::decode($envelope, true);    // assoc arrays; false for stdClass
```

Useful for websockets, queued payloads, and cache entries. The codec itself (`SmallJson\Codec\Codec`) is framework-free if you need it outside Laravel.

Negotiation
-----------

[](#negotiation)

By default nothing changes for clients that don't opt in:

1. A capable client sends `X-Small-Json: pz` (the modes it accepts). The Flutter interceptor does this automatically.
2. The server responds with `Content-Type: application/vnd.smalljson+json` and the encoded body — or plain JSON when encoding wouldn't help. `Vary: X-Small-Json` is set either way so shared caches keep the variants apart.

Set `'negotiate' => false` to encode for every client — only do this when you control all consumers. Browsers calling your API with the header from JavaScript will need `X-Small-Json` whitelisted in your CORS `allowed_headers`.

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

[](#configuration)

KeyDefaultMeaning`enabled``true` (`SMALLJSON_ENABLED`)Master switch; off = plain JSON everywhere`modes``'pz'` (`SMALLJSON_MODES`)Encodings the server may produce`negotiate``true` (`SMALLJSON_NEGOTIATE`)Require the request header before encoding`request_header``'X-Small-Json'`Capability header name`content_type``application/vnd.smalljson+json`Marker content type on encoded responses`min_bytes``0` (`SMALLJSON_MIN_BYTES`)Skip encoding below this plain-JSON size`only_when_smaller``true`Fall back to plain JSON unless encoding shrinks it`deflate.min_bytes``1024`Only try mode `z` at or above this envelope size`deflate.level``6`DEFLATE level (1–9)`stats_header``false` (`SMALLJSON_STATS`)Add `X-Small-Json-Stats` with the savingsTip: while evaluating, set `SMALLJSON_STATS=true` and watch the header: `X-Small-Json-Stats: mode=z; plain=37462; sent=5236; saved=86.0%`.

Clients
-------

[](#clients)

- **Flutter / Dart** — the [`smalljson`](https://pub.dev/packages/smalljson) package's `SmallJsonInterceptor` (Dio). Advertises support, decodes bodies (including error responses), rewrites the content type back to `application/json`.
- **Browser JS** — the packed format decodes in ~30 lines of JavaScript (mode `z`additionally needs `new DecompressionStream('deflate-raw')`).
- **PHP** — `SmallJson::decode()` in this package.

Testing
-------

[](#testing)

```
composer test    # unit + feature (testbench) + cross-implementation vectors; 100% line coverage
composer bench   # the size benchmark shown above
```

The suite includes shared wire-format vectors (`tests/Fixtures/vectors.json`) verified byte-for-byte against the Dart implementation — both directions.

License
-------

[](#license)

MIT © [Anthony Gordon](https://nylo.dev)

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance90

Actively maintained with recent releases

Popularity15

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

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

48d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/f8380256da4946ec2f597ab2abd608def753b4cd676ef8f0c07eb79fb6ff2c1c?d=identicon)[nylo](/maintainers/nylo)

---

Top Contributors

[![Copilot](https://avatars.githubusercontent.com/in/1143301?v=4)](https://github.com/Copilot "Copilot (3 commits)")[![agordn52](https://avatars.githubusercontent.com/u/17294994?v=4)](https://github.com/agordn52 "agordn52 (2 commits)")

---

Tags

jsonapilaravelcompressionpayloadbandwidthflutterdiosmalljson

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/nylo-smalljson/health.svg)

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

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3365.5M359](/packages/psalm-plugin-laravel)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

80732.6M270](/packages/laravel-mcp)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

818355.4k3](/packages/defstudio-telegraph)[api-platform/laravel

API Platform support for Laravel

58190.1k22](/packages/api-platform-laravel)[simplestats-io/laravel-client

Server-side analytics for Laravel that follows the full funnel from visit to registration to payment, attributed to the channel that drove it. Revenue, MRR, churn and ad-spend profit (ROAS/CAC) per channel. GDPR compliant, ad-blocker proof.

5226.7k](/packages/simplestats-io-laravel-client)[forjedio/inertia-table

Backend-driven dynamic tables for Laravel + Inertia.js

272.5k](/packages/forjedio-inertia-table)

PHPackages © 2026

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