PHPackages                             methorz/http-dto - 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. [HTTP &amp; Networking](/categories/http)
4. /
5. methorz/http-dto

ActiveLibrary[HTTP &amp; Networking](/categories/http)

methorz/http-dto
================

Automatic HTTP ↔ DTO conversion via PSR-15 middleware

v2.1.3(1mo ago)054MITPHPPHP ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0CI passing

Since Dec 1Pushed 1mo agoCompare

[ Source](https://github.com/MethorZ/http-dto)[ Packagist](https://packagist.org/packages/methorz/http-dto)[ RSS](/packages/methorz-http-dto/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (28)Versions (8)Used By (0)

methorz/http-dto
================

[](#methorzhttp-dto)

**Automatic HTTP ↔ DTO conversion with validation for PSR-15 applications**

[![CI](https://github.com/MethorZ/http-dto/actions/workflows/ci.yml/badge.svg)](https://github.com/MethorZ/http-dto/actions/workflows/ci.yml)[![codecov](https://camo.githubusercontent.com/200d8a7db1c655b0ffc65806886fde0be5a90211c4632813da21dce03010dd84/68747470733a2f2f636f6465636f762e696f2f67682f4d6574686f725a2f687474702d64746f2f67726170682f62616467652e737667)](https://codecov.io/gh/MethorZ/http-dto)[![PHPStan](https://camo.githubusercontent.com/1bc07920f0d36e55c17e1d38b1caa132cc605f51a82b388c962870b9a747b898/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c253230392d627269676874677265656e2e737667)](https://phpstan.org/)[![PHP Version](https://camo.githubusercontent.com/962aced9b09d89716dbebf186ff899754a096ff1068b6b7988675c2d9fab9331/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253545382e322d626c75652e737667)](https://php.net)[![License](https://camo.githubusercontent.com/8bb50fd2278f18fc326bf71f6e88ca8f884f72f179d3e555e20ed30157190d0d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e2e737667)](LICENSE)

What is this?
-------------

[](#what-is-this)

This package provides **automatic** Data Transfer Object (DTO) handling for PSR-15 middleware applications (Mezzio, Laminas) using the **DtoHandlerWrapper** pattern. It eliminates boilerplate code by:

- 🎯 **Automatically extracting** data from HTTP requests (JSON body, query params, route attributes)
- 🔄 **Automatically mapping** request data to Request DTOs
- ✅ **Automatically validating** Request DTOs using Symfony Validator
- 🚀 **Automatically injecting** validated DTOs as handler parameters
- 📦 **Automatically serializing** Response DTOs to JSON responses

**Key Innovation**: The `DtoHandlerWrapper` pattern wraps your `DtoHandlerInterface` implementations, handling all DTO concerns without requiring global middleware in your pipeline.

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

[](#installation)

```
composer require methorz/http-dto
```

Quick Example
-------------

[](#quick-example)

### Before (Manual Boilerplate)

[](#before-manual-boilerplate)

```
public function handle(ServerRequestInterface $request): ResponseInterface
{
    // 1. Get request body
    $data = $request->getParsedBody();

    // 2. Map to DTO
    $dto = new CreateItemRequest(
        name: $data['name'] ?? '',
        description: $data['description'] ?? ''
    );

    // 3. Validate DTO
    $violations = $this->validator->validate($dto);
    if (count($violations) > 0) {
        return new JsonResponse(['errors' => ...], 422);
    }

    // 4. Execute service
    $result = $this->service->execute($dto);

    // 5. Serialize response
    return new JsonResponse($result->toArray(), 201);
}
```

### After (Automatic! ✨)

[](#after-automatic-)

```
public function __invoke(
    ServerRequestInterface $request,
    CreateItemRequest $dto  // ← Automatically mapped, validated, and injected!
): ItemResponse {           // ← Automatically serialized to JSON!
    return $this->service->execute($dto);  // One line! 🎉
}
```

Features
--------

[](#features)

### 1. Automatic Request → DTO Mapping

[](#1-automatic-request--dto-mapping)

Define Request DTOs with Symfony Validator attributes:

```
use Symfony\Component\Validator\Constraints as Assert;

final readonly class CreateItemRequest
{
    public function __construct(
        #[Assert\NotBlank(message: 'Name is required')]
        #[Assert\Length(min: 3, max: 100)]
        public string $name,

        #[Assert\NotBlank(message: 'Description is required')]
        public string $description,
    ) {}
}
```

### 2. Automatic DTO → Response Serialization

[](#2-automatic-dto--response-serialization)

Define Response DTOs with `JsonSerializableDto`:

```
use Methorz\Dto\Response\JsonSerializableDto;

final readonly class ItemResponse implements JsonSerializableDto
{
    public function __construct(
        public string $id,
        public string $name,
        public string $description,
    ) {}

    public function jsonSerialize(): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'description' => $this->description,
        ];
    }

    public function getStatusCode(): int
    {
        return 201; // Created
    }
}
```

### 3. Handler with Direct DTO Parameters

[](#3-handler-with-direct-dto-parameters)

Implement `DtoHandlerInterface` and use `__invoke()`:

```
use Methorz\Dto\Handler\DtoHandlerInterface;

final readonly class CreateItemHandler implements DtoHandlerInterface
{
    public function __construct(
        private CreateItemService $service,
    ) {}

    public function __invoke(
        ServerRequestInterface $request,
        CreateItemRequest $dto  // ← Injected automatically!
    ): ItemResponse {           // ← Serialized automatically!
        return $this->service->execute($dto);
    }

    // PSR-15 compatibility method (not used directly)
    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        return $this->__invoke($request, new CreateItemRequest('', ''));
    }
}
```

Setup
-----

[](#setup)

### 1. Register DtoHandlerWrapperFactory in ConfigProvider

[](#1-register-dtohandlerwrapperfactory-in-configprovider)

```
use MethorZ\Dto\Factory\DtoHandlerWrapperFactory;
use MethorZ\Dto\RequestDtoMapperInterface;
use MethorZ\Dto\Exception\MappingException;
use MethorZ\Dto\Exception\ValidationException;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;

public function getDependencies(): array
{
    return [
        'factories' => [
            // DTO Handler Wrapper Factory
            DtoHandlerWrapperFactory::class => function (ContainerInterface $container) {
                return new DtoHandlerWrapperFactory(
                    $container->get(RequestDtoMapperInterface::class),
                    $container->get('dto.error_handler'),
                );
            },
        ],
        'services' => [
            // Error handler for DTO validation/mapping failures
            'dto.error_handler' => function (ValidationException|MappingException $e): ResponseInterface {
                if ($e instanceof ValidationException) {
                    return new JsonResponse([
                        'success' => false,
                        'errors' => $e->getErrors(),
                    ], 422);
                }
                return new JsonResponse([
                    'success' => false,
                    'error' => $e->getMessage(),
                ], 400);
            },
        ],
    ];
}
```

### 2. Wrap Your Handlers in Routes

[](#2-wrap-your-handlers-in-routes)

Use `DtoHandlerWrapperFactory` to wrap your `DtoHandlerInterface` implementations:

```
use Item\Application\Handler\CreateItemHandler;
use MethorZ\Dto\Factory\DtoHandlerWrapperFactory;

public function getRoutes(): array
{
    return [
        [
            'allowed_methods' => ['POST'],
            'path'            => '/api/v1/items',
            'middleware'      => [
                DtoHandlerWrapperFactory::class . '::wrap:' . CreateItemHandler::class,
            ],
        ],
    ];
}
```

Complete Flow
-------------

[](#complete-flow)

```
HTTP POST /api/items
    ↓
RouteMiddleware (matches route)
    ↓
DispatchMiddleware
    ↓
DtoHandlerWrapper (wraps CreateItemHandler)
    ├─ Extracts DTO class from handler signature
    ├─ Extracts data from request (JSON body, query params, route attributes)
    ├─ Maps data → CreateItemRequest DTO
    ├─ Validates CreateItemRequest (Symfony Validator)
    ├─ Calls: Handler.__invoke(request, CreateItemRequest)
    │   ├─ Handler calls: service.execute($dto)
    │   └─ Handler returns: ItemResponse (implements JsonSerializableDto)
    ├─ Detects: ItemResponse implements JsonSerializableDto
    ├─ Calls: $response->jsonSerialize()
    ├─ Gets: $response->getStatusCode() → 201
    └─ Returns: JsonResponse(data, 201)
    ↓
HTTP Response: 201 Created
{"id": "...", "name": "...", "description": "..."}

```

**Key Benefits of DtoHandlerWrapper Pattern:**

- ✅ **Single pattern** - One component handles request DTO mapping AND response serialization
- ✅ **Handler-specific** - Only processes requests to DtoHandlerInterface implementations
- ✅ **No middleware overhead** - Doesn't process every request in the pipeline
- ✅ **Cleaner architecture** - Clear separation: middleware for cross-cutting, wrapper for handler-specific
- ✅ **Easy to use** - Just wrap your handler in routes configuration

Error Handling
--------------

[](#error-handling)

The middleware automatically handles validation errors:

```
// HTTP 422 Unprocessable Entity
{
    "status": "error",
    "message": "DTO validation failed",
    "errors": {
        "name": ["Name is required", "Name must be at least 3 characters"],
        "description": ["Description is required"]
    }
}
```

Benefits
--------

[](#benefits)

### For Handlers

[](#for-handlers)

✅ Return DTOs directly (not `ResponseInterface`) ✅ No `ApiResponse` wrapper calls ✅ No manual `->toArray()` calls ✅ Perfect type safety ✅ Ultra clean (often one line!)

### For Response DTOs

[](#for-response-dtos)

✅ Control their own HTTP status code ✅ Self-serializing (`jsonSerialize()`) ✅ Single Responsibility Principle

### For Testing

[](#for-testing)

✅ Test handler returns actual DTO ✅ No mocking `ApiResponse`✅ Test serialization separately ✅ More maintainable

### For Architecture

[](#for-architecture)

✅ Perfect symmetry: Request DTOs IN, Response DTOs OUT ✅ Consistent pattern across all handlers ✅ Type-safe end-to-end

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

[](#requirements)

- PHP 8.2+
- PSR-7 (HTTP Message Interface)
- PSR-15 (HTTP Server Middleware)
- Symfony Validator
- Mezzio or any PSR-15 compatible framework

Related Packages
----------------

[](#related-packages)

This package is part of the MethorZ HTTP middleware ecosystem:

PackageDescription**[methorz/http-dto](https://github.com/methorz/http-dto)**Automatic HTTP ↔ DTO conversion (this package)**[methorz/http-problem-details](https://github.com/methorz/http-problem-details)**RFC 7807 error handling middleware**[methorz/http-cache-middleware](https://github.com/methorz/http-cache-middleware)**HTTP caching with ETag support**[methorz/http-request-logger](https://github.com/methorz/http-request-logger)**Structured logging with request tracking**[methorz/openapi-generator](https://github.com/methorz/openapi-generator)**Automatic OpenAPI spec generation from DTOsThese packages work together seamlessly in PSR-15 applications.

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE) file for details.

Author
------

[](#author)

**Thorsten Merz**

---

**Made with ❤️ for clean, type-safe APIs**

###  Health Score

43

—

FairBetter than 91% of packages

Maintenance90

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity57

Maturing project, gaining track record

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

Every ~18 days

Total

7

Last Release

48d ago

Major Versions

v1.1.0 → v2.0.02025-12-22

### Community

Maintainers

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

---

Tags

composerdata-transfer-objectdtoframework-agnosticmiddlewarepackagistphpphp8psr-15request-handlingserializationsymfony-validatorvalidationhttpmiddlewarevalidationserializationpsr-15dto

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/methorz-http-dto/health.svg)

```
[![Health](https://phpackages.com/badges/methorz-http-dto/health.svg)](https://phpackages.com/packages/methorz-http-dto)
```

###  Alternatives

[cakephp/cakephp

The CakePHP framework

8.8k18.5M1.6k](/packages/cakephp-cakephp)[mezzio/mezzio

PSR-15 Middleware Microframework

3883.6M97](/packages/mezzio-mezzio)[mezzio/mezzio-authentication-oauth2

OAuth2 (server) authentication middleware for Mezzio and PSR-7 applications.

28483.0k2](/packages/mezzio-mezzio-authentication-oauth2)[mezzio/mezzio-authentication

Authentication middleware for Mezzio and PSR-7 applications

121.6M26](/packages/mezzio-mezzio-authentication)[middlewares/utils

Common utils for PSR-15 middleware packages

503.4M93](/packages/middlewares-utils)

PHPackages © 2026

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