Componette

Componette

BrandEmbassy

BrandEmbassy / slim-nette-extension 4.2

Nette Extension for Slim API micro-framework using middlewares.

download-cloud-line composer require brandembassy/slim-nette-extension

CircleCI Total Downloads Latest Stable Version

Nette Extension for integration of SLIM for API

This extension brings the power of Slim for applications using Nette DI. It enables you to easily work with Slim middleware stack and develop your API easily.

This package now uses Slim Framework 4.x

The general idea has been discussed in this article. (Czech language)

Philosophy

Single Responsibility

The main idea is to delegate responsibilities of the code handling requests to separated middlewares. For example:

  • authentication
  • validation
  • business logic

How middlewares in Slim work is described here.

Easy configuration

Empowered by Nette DI and it's neon configuration syntax this package provides powerful and easy way to define your API.

Usage

So let's start!

composer require brandembassy/slim-nette-extension

Extension

Now register new extension by adding this code into your config.neon:

extensions:
    slimApi: BrandEmbassy\Slim\DI\SlimApiExtension # Register extension

slimApi: # Configure it
    slimConfiguration:
        settings:
            removeDefaultHandlers: true # It's recommended to disable original error handling 
                                        # and use your own error handlers suited for needs of your app. 

    apiDefinitionKey: api # Your API definition will be under this key in "parameters" section. 

First API endpoint

Now let's say you want to make a REST endpoint creating channels, [POST] /new-api/2.0/channels

You need to define in parameters.api section in config.neon.

Both services and middlewares must be registered services in DI Container.

slimApi:
    handlers:
        notFound: App\NotFoundHandler # Called when not route isn't matched by URL
        notAllowed: App\NotAllowedHandler # Called when route isn't matched by method
        error: App\ApiErrorHandler # Called when unhandled exception bubbles out

    routes:
        "2.0": # Version of your API
            "channels": # Matched URL will be "your-domain.org/2.0/channels"
                post:
                    # This is service will be invoked to handle the request
                    service: App\CreateChannelAction
                    
                    # Here middleware stack is defined. It's evaluated from bottom to top. 
                    middlewares:
                        - App\SomeOtherMiddleware # last in row
                        - App\UsuallyRequestDataValidationMiddleware # second in row
                        - App\SomeAuthMiddleware # this one is called first 

    beforeRouteMiddlewares:
        # this is called for each route, before route middlewares
        - App\SomeBeforeRouteMiddleware 
        
    afterRouteMiddlewares:
        # this is called for each route, after the route middlewares
        - App\SomeAfterRouteMiddleware

    beforeRequestMiddlewares:
        # this is called for each request, even when route does NOT exist (404 requests)
        - App\SomeBeforeRequestMiddleware

You can also reference the named service by its name.

Required permissions

A route can declare the permissions it requires. The extension only carries the list, checking it is up to your application.

slimApi:
    routes:
        "2.0":
            "channels":
                get:
                    service: App\ListChannelsAction
                    permissions: [channels:View, channels:Edit] # all of them are required
                post:
                    service: App\CreateChannelAction
                    permissions: [] # declared, nothing required

When the key is present, the list is set as the RouteDefinition::REQUIRED_PERMISSIONS_ATTRIBUTE request attribute before any middleware of the route runs (before route, version group, group and route middlewares), so each of them can read it:

$requiredPermissions = $request->getAttribute(RouteDefinition::REQUIRED_PERMISSIONS_ATTRIBUTE);

A route without the permissions key has no such attribute, so getAttribute() returns null. The three values let an application-wide permission middleware deny by default:

Attribute Meaning Suggested handling
null the route declares no permissions deny (a misconfiguration, e.g. 500 and an error log), so a forgotten key never opens an endpoint
[] the route is explicitly open allow any authenticated caller
['a', 'b'] the route requires these permissions allow only callers that have all of them

Allowed authorizations

A route can also declare which authorization types it accepts (token types, cookies, …). The names are up to your application, and so is the check.

slimApi:
    routes:
        "2.0":
            "channels":
                get:
                    service: App\ListChannelsAction
                    authorization: [userToken, serviceToken, sessionCookie]

The list is set as the RouteDefinition::ALLOWED_AUTHORIZATIONS_ATTRIBUTE request attribute, at the same point as the required permissions. As with permissions, a route without the authorization key gets null, and authorization: [] gets []. Your application decides what each means; treating null as a misconfiguration keeps a forgotten key from opening an endpoint.

See tests/SlimApplicationFactoryTest.php and tests/config.neon for more examples.

Execution

Now you can simply get SlimApplicationFactory class from your DI Container (or better autowire it), create app and run it.

$factory = $container->getByType(SlimApplicationFactory::class);
$app = $factory->create();
$app->run();

Migrating from Slim 3 to Slim 4

Version 4.x of this package uses Slim Framework 4 instead of Slim Framework 3. The migration should be mostly transparent for users as the package maintains backward compatibility where possible.

Key Changes

  1. Dependencies: Slim 4 uses PSR-7, PSR-15, and PSR-17 standards more strictly
  2. Container: Slim 4 no longer provides its own container, but the package provides a compatibility layer
  3. Middleware: The double-pass middleware style ($request, $response, $next) is still supported
  4. Routing: Routes are now registered on the App instance directly (handled internally by the package)

What You Need to Do

For most users, the upgrade should be seamless:

  1. Update your composer.json to require the new version
  2. Run composer update brandembassy/slim-nette-extension
  3. Clear your cache directories (temp/, tests/temp/)
  4. Test your application

Breaking Changes

  • Response and ResponseInterface wrappers have been removed — route handlers now receive a plain PSR-7 ResponseInterface directly
  • RequestInterface::getRoute() now returns ?Route (nullable)
  • RequestInterface gained new methods: getInnerRequest(), getRoutingResults()
  • If you were directly accessing Slim internals (like Slim\Container or Slim\Router), you'll need to update your code
  • Custom middleware that relied on Slim 3 specific features may need updates
  • $app->run() and $app->handle() now apply a Content-Type fix for empty responses

Backward Compatibility

The following are maintained for backward compatibility:

  • Middleware signature remains the same (double-pass style $request, $response, $next)
  • Route handler signature remains the same ($request, $response)
  • Route and handler registration via NEON configuration is unchanged
  • Container access via $app->getContainer() works as before

For more details on Slim 4 changes, see the official Slim 4 upgrade guide.

Componette Componette felix@nette.org