Weave Code
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

Rest Bundle Laravel Package

dmp/rest-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation Add the bundle to config/bundles.php:

    DMP\RestBundle\RestBundle::class => ['all' => true],
    

    Install via Composer:

    composer require dmp/rest-bundle
    
  2. First Controller Create a DTO for request/response (e.g., src/DTO/TestDTO.php):

    namespace App\DTO;
    
    use Symfony\Component\Validator\Constraints as Assert;
    
    class TestDTO {
        #[Assert\NotBlank]
        public string $name;
    }
    
  3. Route & Annotations Define a controller (src/Controller/TestController.php):

    use DMP\RestBundle\Annotation as Rest;
    use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
    use Symfony\Component\Routing\Attribute\Route;
    
    class TestController {
        #[Route('/api/test', methods: ['POST'])]
        #[Rest\Serializable(statusCode: 201)]
        public function test(#[MapRequestPayload] TestDTO $request): TestDTO {
            return $request; // Auto-serialized
        }
    }
    
  4. Test It Send a POST to /api/test with JSON body:

    {"name": "Test"}
    

    Expected response:

    {"name": "Test"}
    

Implementation Patterns

1. DTO-Driven Workflow

  • Request/Response Separation: Always use DTOs for both input and output. Example:
    #[Route('/api/user', methods: ['POST'])]
    #[Rest\Serializable]
    public function createUser(#[MapRequestPayload] UserRequestDTO $request): UserResponseDTO {
        $user = $this->userService->create($request);
        return new UserResponseDTO($user);
    }
    
  • Nested DTOs: Support complex payloads via nested DTOs with #[MapRequestPayload] on parent objects.

2. Validation & Error Handling

  • Automatic Validation: The bundle validates DTOs using Symfony’s validator. Errors return as:
    {
      "errors": [
        {"property": "name", "message": "This value should not be blank."}
      ]
    }
    
  • Custom Error Responses: Override default behavior by throwing exceptions with custom messages or using #[Rest\Serializable] with a custom status code.

3. Query Parameters

  • Use #[MapQueryString] for GET requests:
    #[Route('/api/search', methods: ['GET'])]
    #[Rest\Serializable]
    public function search(#[MapQueryString] SearchQueryDTO $query): SearchResultDTO {
        return $this->searchService->execute($query);
    }
    

4. Pagination & Metadata

  • Extend Serializable to include metadata:
    #[Rest\Serializable(statusCode: 200, metadata: ['pagination' => true])]
    public function listUsers(): UserCollectionDTO { ... }
    
    Response:
    {
      "data": [...],
      "meta": {"pagination": {"total": 100}}
    }
    

5. Service Integration

  • Dependency Injection: Inject services into controllers as usual:
    public function __construct(private UserService $userService) {}
    
  • Transaction Handling: Use Symfony’s Transactional trait for atomic operations.

6. Testing

  • Unit Tests: Mock DTOs and validate serialization:
    $dto = new TestDTO();
    $dto->name = "Test";
    $serializer = $this->container->get('serializer');
    $json = $serializer->serialize($dto, 'json');
    $this->assertEquals('{"name":"Test"}', $json);
    
  • Integration Tests: Use client->request() with JSON payloads and assert responses.

Gotchas and Tips

Pitfalls

  1. Hardcoded /api/ Prefix

    • The README mentions a TODO to remove this requirement. Until then, ensure all routes start with /api/ or override the prefix in the bundle config (if exposed).
  2. DTO Serialization Issues

    • Circular References: Use #[Groups({"api"})] with Symfony Serializer to avoid circular reference errors.
    • Non-Serializable Properties: Exclude properties with #[SerializedName(ignore: true)] or use #[Groups({"api"})] selectively.
  3. Validation Overrides

    • Custom validators may conflict with the bundle’s ExceptionListener. Ensure your validator throws ValidationFailedException or extends ConstraintViolationListException.
  4. Exception Handling

    • Non-validated exceptions (e.g., RuntimeException) will return as:
      {"errors": [{"message": "Exception message"}]}
      
      To customize, create an event subscriber for kernel.exception.
  5. Query Parameter Conflicts

    • If a DTO property name matches a Symfony route parameter (e.g., {id}), the query parameter will override the route parameter. Rename properties or use #[MapQueryString(alias: "custom_name")].

Debugging Tips

  1. Enable API Debugging Add this to config/packages/dev/rest_bundle.yaml (if supported):

    debug: true
    

    This may expose detailed error traces in responses.

  2. Log Validation Errors Configure Symfony’s validator to log errors:

    # config/packages/validator.yaml
    Symfony\Component\Validator\Validator\ValidatorInterface:
        arguments:
            $constraintValidatorFactory: '@validator.constraint_validator_factory'
            $constraintValidatorFactories: []
            $mapping: '@validator.mapping'
            $listeners: ['@validator.listener.error_listener']
    
  3. Check Event Dispatchers The bundle may dispatch events (e.g., rest.exception). Subscribe to them for custom logic:

    public static function getSubscribedEvents(): array {
        return [
            'rest.exception' => 'onRestException',
        ];
    }
    

Extension Points

  1. Custom Serialization Groups Extend the bundle’s serializer by adding custom groups to your DTOs:

    #[Groups({"api", "admin"})]
    public string $secretData;
    
  2. Response Transformers Override the default response transformer by binding a custom service:

    # config/services.yaml
    services:
        App\Rest\ResponseTransformer:
            tags: ['rest.response_transformer']
    
  3. Middleware Integration Add middleware to preprocess requests/responses:

    public function onKernelRequest(RequestEvent $event) {
        $request = $event->getRequest();
        if ($request->isMethod('POST') && $request->getPathInfo() === '/api/test') {
            $request->attributes->set('custom_flag', true);
        }
    }
    
  4. Dynamic Route Prefixes If the /api/ prefix is problematic, fork the bundle and modify DMP\RestBundle\Routing\Loader\RestRouteLoader to accept a configurable prefix.

Performance Tips

  1. DTO Caching Cache DTO classes if they’re used frequently (e.g., with Symfony\Component\Cache\Adapter\AdapterInterface).

  2. Batch Validation For bulk operations, validate DTOs in batches to reduce overhead:

    $validator = $this->validator;
    $errors = $validator->validate($dto1);
    $errors = $validator->validate($dto2);
    
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.
comsave/common
alecsammon/php-raml-parser
chrome-php/wrench
lendable/composer-license-checker
typhoon/reflection
mesilov/moneyphp-percentage
mike42/gfx-php
bookdown/themes
aura/view
aura/html
aura/cli
povils/phpmnd
nayjest/manipulator
omnipay/tests
psr-mock/http-message-implementation
psr-mock/http-factory-implementation
psr-mock/http-client-implementation
voku/email-check
voku/urlify
rtheunissen/guzzle-log-middleware