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

Api Resources Laravel Package

juampi92/api-resources

juampi92/api-resources extends Laravel's Eloquent API resources with query-string controls for sparse fieldsets, nested includes, and filtering, letting clients request exactly the data they need while you keep resource definitions clean, consistent, and reusable.

View on GitHub
Deep Wiki
Context7

Getting Started

The package (juampi92/api-resources) is a thin, opinionated layer on top of Laravel's JsonResource. You keep writing toArray($request) as usual, but gain whitelist-based field selection and strict lazy-loading protection. v1.10.0 is a compatibility/maintenance release: it adds Laravel 11.x support and drops --ignore-platform-reqs from the package's own composer scripts. There are no breaking changes, no new public API, and no deprecations — upgrading from 1.9.x is a straight composer update juampi92/api-resources.

Minimal setup:

  1. composer require juampi92/api-resources
  2. Publish the config: php artisan vendor:publish --tag=api-resources-config (verify the exact tag with php artisan vendor:publish if it differs). This is where the strictness knobs live — lazy-loading prevention, unknown-field handling, and resource wrapping.
  3. Extend Juampi92\ApiResources\ApiResource (and its collection counterpart) instead of Illuminate\Http\Resources\Json\JsonResource.
  4. In the controller, build the resource and declare the fields you actually want:
return UserResource::make($user)->only(['id', 'name']);
// or, for collections
return UserResource::collection($users)->only(['id', 'name']);

Where to look first: the published config file (it tells you how strict the package is out of the box), then the package README's only() / conditional-field semantics — that is the one concept that differs from stock Laravel resources.

First realistic use case: an existing endpoint that returns too much data or silently N+1s. Swap the resource base class, add ->only([...]) at the call site, and let the package surface any relation you forgot to eager load.

Implementation Patterns

Two entry points per resource. The resource class defines the full field universe; the call site (controller or a parent resource) narrows it with only(). Treat toArray() as a schema and only() as the query projection — this keeps a single resource reusable across a mobile endpoint (3 fields) and an admin endpoint (15 fields).

Wrap conditionally-expensive fields in the package's inclusion helper (e.g. whenIncluded(...)) rather than when() / whenNotNull(). The field is only evaluated — and therefore only costs you a query or an accessor call — when the caller explicitly asked for it. Pair it with whenLoaded() for relations so the eager load and the field gate stay in sync:

public function toArray($request): array
{
    return [
        'id'    => $this->id,
        'name'  => $this->name,
        'email' => $this->whenIncluded($this->email),
        'posts' => PostResource::collection($this->whenLoaded('posts')),
    ];
}

Nest explicitly, project explicitly. Field selection does not implicitly cascade. If UserResource embeds PostResource::collection(...), the child resource has its own field set — apply only() there too (->only(['id', 'title'])), otherwise the nested payload is whatever the child's toArray() returns. This is the single biggest source of "why is my response still huge" confusion.

Controller-level convention. Standardise on returning Resource::make($model)->only($request->validated('fields') ?? self::DEFAULT_FIELDS) for endpoints with a ?fields= parameter, and pass the resulting array through ->fields()/config introspection when you need to advertise the schema (e.g. OpenAPI generation).

Incremental migration. You don't need to convert everything. Because the base class extends Laravel's JsonResource, a resource can extend ApiResource and still be returned from any existing controller. Convert high-traffic endpoints first — those are where over-fetching and N+1s actually hurt.

Feature tests. Prefer ->resolve() (or ->response()->getData(true)) in tests so assertions run against the array, not a JsonResponse. Assert both the presence of whitelisted keys and the absence of everything else — that's the behaviour this package exists to guarantee.

Gotchas and Tips

  • only() on an undefined field is a failure mode, not a no-op. Depending on the unknown-field config, selecting a key that toArray() never returns will either throw or silently drop the projection. Keep field names in a class constant (const FIELDS = ['id', 'name', 'email'];) and reuse it at call sites and in tests.
  • Lazy-loading protection fires on accessor- and relation-induced queries, not just with() omissions. If a resource starts throwing after an unrelated refactor, the cause is usually a model accessor or an $appends attribute touching a relation. Audit $appends before disabling the guard.
  • Don't disable strictness to "just ship it". Turning off lazy-loading prevention or unknown-field checks globally via config is a one-line escape hatch that quietly reverts the package to plain JsonResource. If you need it locally, scope it in tests only.
  • Config caching. In production the published config is baked in via config:cache; changing strictness requires a redeploy/recache, not just a file edit. This bites people debugging an environment-specific difference between local and prod.
  • Wrapping is inherited from Laravel, not the package. data wrapping, JsonResource::withoutWrapping(), and pagination envelopes behave exactly as in core — check middleware/AppServiceProvider first when the shape surprises you, before blaming the resource.
  • Collections need the package's collection class. Returning a plain ResourceCollection or a AnonymousResourceCollection built from the base JsonResource will bypass the field-projection logic. Use Resource::collection($items) off an ApiResource subclass.
  • Version floor. This release is the first with Laravel 11 support. If you're on Laravel 11, pin ^1.10; on Laravel 10 or below, anything ^1.9 remains fine. The removed --ignore-platform-reqs only affects the package's own CI scripts, not consumers.
  • Extension points are shallow by design. If you need cross-cutting behaviour (e.g. auto-adding created_at to every resource), prefer a base class in your app over patching vendor config — the package's surface area is small and deliberately unopinionated about your domain.
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager