Plack::Builder(3)

πŸ’– NAME πŸ’–

Plack::Builder β€” this is the Plack::Builder(3) man page, bestie, and it's all about the OO and DSL ways to enable Plack Middlewares πŸŽ€


✨ SYNOPSIS ✨

# in .psgi
use Plack::Builder;

my $app = sub { ... };

builder {
    enable "Deflater";
    enable "Session", store => "File";
    enable "Debug", panels => [ qw(DBITrace Memory Timer) ];
    enable "+My::Plack::Middleware";
    $app;
};

# use URLMap

builder {
    mount "/foo" => builder {
        enable "Foo";
        $app;
    };

    mount "/bar" => $app2;
    mount "http://example.com/" => builder { $app3 };
};

# using OO interface
my $builder = Plack::Builder->new;
$builder->add_middleware('Foo', opt => 1);
$builder->add_middleware('Bar');
$builder->wrap($app);

🌈 DESCRIPTION 🌈

Okay so Plack::Builder hands you a cute little domain specific language (DSL) so you can wrap your app in Plack::Middleware subclasses without losing your mind 🧚. The middleware you wanna use should be built on top of Plack::Middleware as its base class for this DSL to work β€” it's giving inspired by Rack::Builder, iykyk.

So like, every time you call "enable" on some middleware, that middleware app gets pushed onto the stack inside the builder, and then the whole thing gets reversed when it actually creates the wrapped application handler. And "Plack::Middleware::" is added as a prefix by default. So:

builder {
    enable "Foo";
    enable "Bar", opt => "val";
    $app;
};

is syntactically equal to:

$app = Plack::Middleware::Bar->wrap($app, opt => "val");
$app = Plack::Middleware::Foo->wrap($app);

In other words bestie: you're supposed to "enable" middleware from outer to inner. Periodt πŸ’…


πŸ”₯ INLINE MIDDLEWARE πŸ”₯

Plack::Builder lets you code middleware inline using a nested code reference, which is honestly so iconic.

If the first argument to "enable" is a code reference, it gets passed an $app and it should return another code reference β€” which is a PSGI application that consumes $env at runtime. So:

builder {
    enable sub {
        my $app = shift;
        sub {
            my $env = shift;
            # do preprocessing
            my $res = $app->($env);
            # do postprocessing
            return $res;
        };
    };
    $app;
};

is equal to:

my $mw = sub {
    my $app = shift;
    sub { my $env = shift; $app->($env) };
};

$app = $mw->($app);

πŸ—ΊοΈ URLMap support πŸ—ΊοΈ

Plack::Builder has native support for Plack::App::URLMap via the "mount" method. Obsessed 🫢

use Plack::Builder;
my $app = builder {
    mount "/foo" => $app1;
    mount "/bar" => builder {
        enable "Foo";
        $app2;
    };
};

Go peek at Plack::App::URLMap's "map" method if you wanna know what they mean by all that. But with "builder" you can't use "map" as a DSL β€” for the obvious reason πŸ™ˆ.

NOTE: Once you use "mount" in your builder code, you have to use "mount" for all the paths, including the root path ("/"). No sneaking around it.

You can't have the default app sitting in the last line of "builder" like:

my $app = sub {
    my $env = shift;
    ...
};

builder {
    mount "/foo" => sub { ... };
    $app; # THIS DOESN'T WORK
};

You'll get warnings saying your mount configuration is gonna be ignored πŸ’€. Instead, use "mount "/" => ..." in that last line to set the default fallback app:

builder {
    mount "/foo" => sub { ... };
    mount "/" => $app;
}

Also note that the "builder" DSL returns a whole new PSGI application, which means:

  • "builder { ... }" should normally be the last statement of a .psgi file, because the return value of "builder" is the application that actually gets executed. Main character energy, honestly πŸ‘‘
  • You can nest your "builder" blocks, mixed with "mount" statements (see "URLMap support" above):
builder {
    mount "/foo" => builder {
        mount "/bar" => $app;
    }
}

will locate the $app under "/foo/bar", since the inner "builder" block puts it under "/bar" and it results in a new PSGI application which is located under "/foo" because of the outer "builder" block. Nested builders ate, no cap ✨


πŸ¦‹ CONDITIONAL MIDDLEWARE SUPPORT πŸ¦‹

You can use "enable_if" to conditionally enable middleware based on the runtime environment 🌸

builder {
    enable_if { $_[0]->{REMOTE_ADDR} eq '127.0.0.1' } 'StackTrace', force => 1;
    $app;
};

See Plack::Middleware::Conditional for the deets.


πŸ’œ OBJECT ORIENTED INTERFACE πŸ’œ

The object oriented interface supports the exact same functionality as the DSL version, just in a clearer interface β€” probably with more typing required, ngl 😭

# With mount
my $builder = Plack::Builder->new;
$builder->add_middleware('Foo', opt => 1);
$builder->mount('/foo' => $foo_app);
$builder->mount('/' => $root_app);
$builder->to_app;

# Nested builders. Equivalent to:
# builder {
#     mount '/foo' => builder {
#         enable 'Foo';
#         $app;
#     };
#     mount '/' => $app2;
# };
my $builder_out = Plack::Builder->new;
my $builder_in  = Plack::Builder->new;
$builder_in->add_middleware('Foo');
$builder_out->mount("/foo" => $builder_in->wrap($app));
$builder_out->mount("/" => $app2);
$builder_out->to_app;

# conditional. You can also directly use Plack::Middleware::Conditional
my $builder = Plack::Builder->new;
$builder->add_middleware_if(sub { $_[0]->{REMOTE_ADDR} eq '127.0.0.1' }, 'StackTrace');
$builder->wrap($app);

⭐ SEE ALSO ⭐

Plack::Middleware Plack::App::URLMap Plack::Middleware::Conditional


and that's the whole vibe, bestie πŸŽ€ go forth and enable your little heart out πŸ’…βœ¨ this was perl v5.42.3 from the User Contributed Perl Documentation, dated 2026-10-18 β€” Plack::Builder(3), signing off 🫢

πŸ“¦ where this page comes from