Apr 14, 2015 | laravel, lumen

Introducing Lumen from Laravel

Series

This is a series of posts on Lumen by Laravel.

!
Warning: This post is over a year old. I don't always update old posts with new information, so some of this information may be out of date.

Lumen is a new project from Laravel creator Taylor Otwell. It's a "micro-framework", meaning it's a smaller, faster, leaner version of a full web application framework. PHP has two other popular micro-frameworks, Slim and Silex.

Lumen

Lumen has the same foundation as Laravel, and many of the same components. But Lumen is built for microservices, not so much for user-facing applications (although it can be used for anything.) As such, frontend niceties like Bootstrap and Elixir and the authentication bootstrap and sessions don't come enabled out of the box, and there's less flexibility for extending and changing the bootstrap files.

What's it for?

Lumen is for projects and components that can benefit from the convenience and power of Laravel but can afford to sacrifice some configurability and flexibility in exchange for a speed boost.

Lumen is targeted at microservices--small, loosely-coupled components that usually support and enhance a core project. Microservices are separated components with bounded contexts (meaning they have well-defined interfaces between each other), so in a microservice architecture you might have several small Lumen apps that support another, possibly Laravel-powered, app.

Show me.

So as not to talk too much about microservices and Lumen at the same time, let's just start by providing a simple caching layer in front of a slow or unreliable external service. I often work with external data sources--APIs, for example--that need transformation and/or caching. As a result, I often build small single-purpose applications that sit between one source of data and my consuming code.

I'll often use Laravel for these applications, which is fine, but there is a lot of extra code that comes with stock Laravel that I don't need for a microservice, let alone one of these little single-purpose caches. So, let's build one of these using Lumen.

Caching

One simple way I've provided a cache layer in the past is just to route all of my calls through this layer, cache the results, and serve from the cache. Let's try that out.

Installing Lumen

Lumen has a simple installer just like Laravel's. You can pull it in globally:

composer global require "laravel/lumen-installer=~1.0"

Now, you can run lumen new MyProject and it'll create that folder and create a lumen project in there for you.

cd Sites
lumen new my-cache
cd my-cache

OK, so now we're in our Lumen install. You can check out php artisan to see what commands we have available, or php artisan serve to spin up a web server at localhost:8000 that's serving your site.

Now, I just want to pass through all of my calls directly. Let's get this app running.

Turning on DotENV and façades

In Laravel, everything just works out of the box. That's pretty true in Lumen, too, but you're going to want to take a first glance at bootstrap/app.php. There are a few options you can enable in here--they'll look like a commented-out line of code that you can turn on by un-commenting it.

Because I'll be wanting to use Laravel's Façades and .env environment variables, I'll un-comment those lines, which look like this:

// Dotenv::load(__DIR__.'/../');

and this:

// $app->withFacades();

You can scroll through this file and see places you can enable Eloquent, route and global middleware, and service providers.

Capturing all routes

Next, let's go to app/Http/routes.php. Note that routes in Lumen use nikic/FastRoute instead of the Illuminate Router, so things will look a little different.

Let's create a route to capture every route that's passed through.

$app->get('{path:.*}', function($path)
{
    echo 'You just visited my site dot com slash ' . $path;
});

If you're familiar with Laravel, you may notice that that above route in Laravel would be:

$router->get('{path?}', function($path)
{
    echo 'You just visited my site dot com slash ' . $path;
})->where('path', '.*');

But essentially, we're capturing every path and passing it in as the $path variable.

Passing through

Now, we can set up our API caller. Skip the next two paragraphs if you don't care about the API caller--it's not entirely necessary to understand this example.

Note that I'm using a generic PassThrough class I wrote that is constructed with a base URL (e.g. http://api.mysite.com/v1/), and has a getResultsForPath method which takes a path (e.g. people/145), and returns a result that's an array with headers, body, and status. It operates the same as the fakeApiCaller class I described in this blog post.

So, we're defining which headers we do and don't want to return; the root URL for our API calling; and then we're passing the path to the caller, getting a response, and creating an Illuminate response using Laravel's response() helper, which takes the parameters of body, status, headers.

$app->get('{path:.*}', function ($path) use ($app)
{
    // Configure
    $headersToPass = ['Content-Type', 'X-Pagination'];
    $rootUrl = 'http://www.google.com/';

    // Run
    $passThrough = $app->make('App\PassThrough', [$rootUrl]);
    $result = $passThrough->getResultsForPath($path);

    // Return
    return response(
        $result['body'],
        $result['status'],
        array_only(
            $result['headers'],
            $headersToPass
        )
    );
});

Notice that we're passing the $app instance around, which we can use to resolve objects out of the IOC container or whatever else.

Caching

Finally, let's, cache the results, and we're good to go.

$app->get('{path:.*}', function ($path) use ($app)
{
    // Configure
    $cacheTtl = 60;
    $headersToPass = ['Content-Type', 'X-Pagination'];
    $rootUrl = 'http://www.google.com/';

    // Run
    $result = Cache::remember(
        $path,
        $cacheTtl,
        function() use ($path, $app, $rootUrl) 
        {
            $passThrough = $app->make('App\PassThrough', [$rootUrl]);
            return $passThrough->getResultsForPath($path);
        }
    );

    // Return
    return response(
        $result['body'],
        $result['status'],
        array_only(
            $result['headers'], $headersToPass
        )
    );
});

That's it! We now have a blazing fast caching mechanism in front of any site. Create your PassThrough class, use Guzzle to construct and call the path, and then split out the Guzzle response to the expected shape, and you're good to go.

Other ideas

Clearly this is a very simple use-case. Lumen is targeted at microservices, so it's more likely to be used when you're separating out a single high-use piece of your application. It might become your API server, or push (or pull) from your queues. It might collect data from multiple places and then serve it out in a normalized manner. If it's a single component, especially if it's high traffic, it may be worth trying it out with Lumen.

Conclusion

Lumen is Laravel, stripped down for speed. It doesn't bother with views and sessions and other consumer-facing conveniences--it's optimized for speedy, trim, microservices. Try it out. And check out the docs, too, for a ton of more information about how to use Lumen.

ArtisanGoose has also written a blog post about how he's using Lumen as a submodule within his Laravel application.


Comments? I'm @stauffermatt on Twitter


Tags: laravel  •  lumen


This is part of a series of posts on Lumen by Laravel:

  1. Apr 14, 2015 | laravel, lumen

Subscribe

For quick links to fresh content, and for more thoughts that don't make it to the blog.