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

Dto Api Bundle Laravel Package

liquidrazor/dto-api-bundle

A Laravel bundle for building API layers around data transfer objects. It helps structure request and response payloads, keep controllers thin, and maintain consistent, typed data flow between your application and its API endpoints.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: liquidrazor/dto-api-bundle is a Symfony bundle, not a Laravel package. There is no service provider, no facade, no config/dto-api.php. You cannot composer require it into a Laravel app and expect it to boot. Your day-to-day leverage as a Laravel developer is in two places: (a) you work in a polyglot org where a Symfony service exposes an API built with it, or (b) you mine it as a reference implementation for a DTO-first JSON API in Laravel.

If you have a Symfony app to try it in:

  1. composer require liquidrazor/dto-api-bundle — pin the exact version, given zero stars and zero dependents.
  2. Register the bundle in config/bundles.php and check config/packages/ for the bundle's config tree (run bin/console config:dump-reference <bundle_alias> to discover the alias rather than guessing it).
  3. Look at vendor/liquidrazor/dto-api-bundle/src/ in this order: the DTO base class/interface, the PHP attributes used to describe fields, the response/streaming factory, and the OpenAPI generator. Those four files are the whole mental model.
  4. Define one DTO with typed public properties, return it from a controller through the bundle's response helper, then hit the autogenerated docs route (bin/console debug:router | grep -i doc) to confirm the schema round-trips.

If you only have Laravel: clone the repo and read it as a spec. The bundle's value to you is the opinion that a DTO is the single source of truth for validation, serialization, and OpenAPI. You will re-implement that with spatie/laravel-data + dedoc/scramble, and the bundle shows you the seams to design around.

Implementation Patterns

One DTO per endpoint, thin controllers. The bundle's contract is that the DTO shape is the API shape. In Laravel terms:

final class StoreOrderData extends Data
{
    public function __construct(
        public string $customer_email,
        /** @var LineItemData[] */
        public array $lines,
    ) {}
}

Keep the controller to StoreOrderData::from($request)->toArray() or a single service call. If you find yourself transforming in the controller, the DTO is wrong.

Streaming as the default for collections. The bundle's streaming responses map cleanly onto response()->streamJson() in Laravel 10+:

return response()->streamJson(
    (function () use ($query) {
        foreach ($query->lazy(500) as $row) {
            yield OrderData::from($row);
        }
    })(),
    headers: ['X-Accel-Buffering' => 'no'],
);

Use lazy()/cursor() behind the generator, never get(). The bundle's whole point is that the DTO is the unit of emission, so each yield should be a valid, fully-shaped record — not a partial row.

OpenAPI from types, not annotations. The bundle derives schemas from DTO property types and attributes. In Laravel, dedoc/scramble does the same inference, so the discipline transfers: annotate arrays with @var Foo[], avoid mixed, and let the tooling fail loudly on untyped properties. Wire a CI check that diffs the generated spec against a committed copy so drift is caught in review.

Consuming the generated spec. If the Symfony service is upstream, pull its /api/doc.json into your Laravel BFF as a build artifact and generate typed clients from it (openapi-generator, or hand-rolled casts to your laravel-data classes). Contract tests against that snapshot catch upstream breaking changes before deploy.

Port checklist. Attributes that mark a field as read-only/write-only/grouped map to laravel-data's #[MapInputName], #[MapOutputName], and #[Groups]. Request validation belongs in DTO rules, not FormRequest, if you want the bundle's one-source-of-truth property.

Gotchas and Tips

  • Maturity risk is real. Zero stars, zero dependents, and a release date that post-dates today are all reasons to vendor-fork it ("repositories": [{"type": "vcs", ...}]) and read every line before trusting it in production. Assume no upstream fixes.
  • Symfony/Laravel impedance mismatch. There is no DtoApiServiceProvider. Don't try to auto-register it, don't expect Laravel's container to resolve its services, and don't expect config() access to bundle config. If you need it in a Laravel app, you are writing an adapter, not integrating.
  • Streaming breaks in proxies. Nginx buffers by default; set X-Accel-Buffering: no and confirm your load balancer doesn't re-buffer. Test through the real edge, not artisan serve.
  • Headers are sent after the first byte. Any exception after the stream starts cannot become a 500 — it becomes a truncated body and a client-side parse error. Validate before opening the stream, and emit a terminal sentinel object or a structured error frame inside the stream so clients can distinguish "done" from "died."
  • Memory looks fine locally and isn't. Streaming only helps if the query is lazy and you aren't eager-loading everything up front. Profile with memory_get_peak_usage() on 100k rows.
  • Schema drift is silent. Union types, mixed, and untyped array elements degrade to {} or anyOf blobs in the generated spec. Treat a spec diff as a review gate, not a post-deploy curiosity.
  • Debugging the bundle itself: bin/console debug:router, debug:container --tag=<bundle_tag>, and config:dump-reference will tell you more than the README. In Laravel, the equivalents are php artisan route:list and a hard look at the committed spec.
  • Extension points to look for: custom normalizers/serializers, a response factory you can subclass, and any OpenAPI schema decorator hook. Those are the places the author expected you to deviate — everything else is meant to be boring, so resist customizing it.
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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib