The contemporarily unique strengths of CGI as a deployment strategy are that CGI scripts ⓐ can just be dumped in the filesystem to deploy them and ⓑ do not have any of the issues of long-running processes: they tie up no resources when not in use and are extremely reliable because of the execution model, in which global state always starts from a blank slate when serving a request and there is no process that outlives the request and could wedge itself. Anyone who consciously chooses CGI over alternative deployment strategies nowadays probably has a fire-and-forget use case where the script will be seeing too little traffic to be worth any effort to tend to it regularly.

In his article about modern CGI, Grinnz suggested using Mojolicious rather than CGI.pm as a framework for writing CGI scripts. Mojolicious is explicitly intended for users who are willing to keep changing their own application code in order to enjoy a framework whose API design can be changed (hopefully for the better) without sacrificing the framework’s code quality. In the Venn diagram of the CGI-for-deployment and Mojolicious-for-framework audiences, there is no overlap. So I consider Mojolicious an oxymoronic alternative to CGI.pm.

A much better alternative to CGI.pm is simply raw Plack. It is lower-level than Mojolicious, so the code will be more verbose, but Plack’s stance on compatibility matches a fire-and-forget use case far better. CGI::Alternatives does not do a great job of selling that option, so let’s just see what it would look like in practice for the given example.

In the mentioned article, Grinnz presents a hypothetical uppercase.cgi :

#!/usr/bin/env perl use strict; use warnings; use CGI; use Encode::Simple; use JSON::MaybeXS; use Syntax::Keyword::Try; my $cgi = CGI->new; try { my $input = decode 'UTF-8', scalar $cgi->param('input'); print $cgi->header('application/json; charset=UTF-8'), encode_json {output => uc $input}; } catch { print $cgi->header('text/html; charset=UTF-8', '500 Internal Server Error'), '<h1>An error has occurred.</h1>'; die $@; }

In a real CGI scenario, with each request being processed with a blank slate of global state, eval ’s pitfalls do not really come into play, so we can leave out Syntax::Keyword::Try. In fact, the Mojolicious example derives most of its concision from the lack of any error handling, which we can do just as well even with raw CGI.pm:

#!/usr/bin/env perl use strict; use warnings; use CGI; use Encode::Simple; use JSON::MaybeXS; my $cgi = CGI->new; my $input = eval { decode 'UTF-8', scalar $cgi->param('input') }; print $cgi->header( 'application/json; charset=UTF-8' ), encode_json { output => uc $input };

A modernisation of this using Plack is straightforwardly equivalent:

#!/usr/bin/env perl use strict; use warnings; use Plack::Request; use Encode::Simple; use JSON::MaybeXS; my $app = sub { my $req = Plack::Request->new($_[0]); my $input = eval { decode 'UTF-8', $req->parameters->{'input'} }; [ 200, [ 'Content-Type', 'application/json; charset=UTF-8' ], [ encode_json { output => uc $input } ] ]; };

This is now a full PSGI app and as such gains all the same additional deployment options of the Mojolicious example. To deploy it as a CGI script you add this line at the bottom:

use Plack::Handler::CGI; Plack::Handler::CGI->new->run($app);

In all it takes about twice as long to load as the CGI.pm example, compared to the Mojolicious example taking about 7× as long. In exchange you get all the benefits of the PSGI ecosystem – without losing any of the strengths of deploying as a CGI script, unlike the Mojolicious example.