# NAME CGI::Alternatives - Documentation for alternative solutions to CGI.pm # VERSION 0.20 # DESCRIPTION This module doesn't do anything, it exists solely to document alternatives to the [CGI](https://metacpan.org/pod/CGI).pm module. # BUT WHY? CGI.pm hasn't been considered good practice for many years, and there have been alternatives available for web development in perl for a long time. Despite this there are still some perl developers that will recommend the use of CGI.pm for web development and prototyping. The two main arguments for the use of CGI.pm, often given by those developers, are no longer true: 1) "CGI.pm is a core module so you don't have to install anything extra." This has been incorrect since 2014 when perl 5.22 was released and no longer ships with it: http://perl5.git.perl.org/perl.git/commitdiff/e9fa5a80 If you are doing any serious web development you are going to have to use external dependencies, DBI is not in the core for example. 2) "CGI.pm scripts are shorter and simpler than alternative implementations." Again, not true and the following examples will show that. # NOTE ABOUT THE EXAMPLES All of the following are functionally identical. They display a very simple form with one text input box. When the form is submit it is redisplayed with the original input displayed below the input box. This example may be trivial, but that is the point. The frameworks shown here feature a great deal of functionality for dealing with other parts of your application and dealing with that in a maintainable way, with full separation of concerns and easy testing. All the examples are commented, where i feel it is necessary to highlight the differences between the implementations, however i do not explain the details of the frameworks - i would be duplicating the framework's docs if i did that, so have a look at the links provided and investigate further. All of the examples in this documentation can be found within the examples/ directory within this distribution. If you want to run them you will need to install the necessary CPAN modules, these are not included as dependencies in this distribution. # RAW CGI.pm EXAMPLES This is the base script that will be re-implemented using the other frameworks There are two versions - one that uses the HTML generation functions of CGI.pm and one that uses Template Toolkit. This is where we get into the first issue with CGI.pm - poor separation of concerns. CGI.pm (and cgi-lib.pl) existed years before template engines were available in perl. As a consequence, to make the generation of html easier, functions were added to output HTML direct from scripts themselves. In doing this you immediately increase the maintenance burden as any changes required to the HTML need to be done within the scripts. You can't just hand a template to the web-designers and allow them to work their magic. Don't mix the business logic and the presentation layer. Just don't. ## CGI.pm With Inline HTML Functions A simple example with form using the html generation functions of CGI.pm. Please don't use these functions, i am merely showing them here for comparison reasons. #!/usr/bin/env perl # most CGI.pm scripts i encounter don't use strict or warnings. # please don't omit these, you are asking for a world of pain # somewhere down the line if you choose to develop sans strict use strict; use warnings; use CGI qw/ -utf8 /; my $cgi = CGI->new; my $res = $cgi->param( 'user_input' ); my $out = $cgi->header( -type => 'text/html', -charset => 'utf-8', ); # html output functions. at best this is a lesson in obfuscation # at worst it is an unmaintainable nightmare (and i'm using # relatively clean perl code and a very very simple example here) $out .= $cgi->start_html( "An Example Form" ); $out .= $cgi->start_form( -method => "post", -action => "/example_form", ); $out .= $cgi->p( "Say something: ", $cgi->textfield( -name => 'user_input' ), $cgi->br, ( $res ? ( $cgi->br, "You wrote: $res" ) : () ), $cgi->br, $cgi->br, $cgi->submit, ); $out .= $cgi->end_form; $out .= $cgi->end_html; print $out; If you really want to continue using the HTML generation functionality of CGI.pm then you should take a look at [HTML::Tiny](https://metacpan.org/pod/HTML%3A%3ATiny) instead, which may give you a migration path away from CGI.pm's html generation functions; i strongly encourage you to move towards template driven page generation for anything involving markup as it will make porting your app to other frameworks much easier in the long run. ## CGI.pm Using Template Toolkit I'm including this example to show that it is easy to move the html generation out of the raw CGI.pm script and into a template for better separation of concerns. #!/usr/bin/env perl # most CGI.pm scripts i encounter don't use strict or warnings. # please don't omit these, you are asking for a world of pain # somewhere down the line if you choose to develop sans strict use strict; use warnings; use FindBin qw/ $Script $Bin /; use Template; use CGI qw/ -utf8 /; # necessary objects my $cgi = CGI->new; my $tt = Template->new({ INCLUDE_PATH => "$Bin/templates", }); # the user input my $res = $cgi->param( 'user_input' ); # we're using TT but we *still* need to print the Content-Type header # we can't put that in the template because we need it to be reusable # by the various other frameworks my $out = $cgi->header( -type => 'text/html', -charset => 'utf-8', ); # TT will append the output to the passed referenced SCALAR $tt->process( "example_form.html.tt", { result => $res, }, \$out, ) or die $tt->error; print $out; ## The Template File Here's a key point - this template file will be re-used by **all** the following framework examples with absolutely no modifications. We can move between the frameworks without having to do any porting of the HTML because it has been divorced from the controller code. What did i say? Separation of concerns: win.
An Example Form } . ($input ? "You wrote: $input
" : '') . q{ }); }; For more information: [https://blogs.perl.org/users/grinnz/2025/02/cgitiny---perl-cgi-but-modern.html](https://blogs.perl.org/users/grinnz/2025/02/cgitiny---perl-cgi-but-modern.html) # MODERN DEPLOYMENT PRACTICES ## PSGI Servers All modern Perl web frameworks support PSGI. Here are the recommended PSGI servers for production: - [Starman](https://metacpan.org/pod/Starman) - High-performance preforking PSGI server (recommended for most uses) - [Gazelle](https://metacpan.org/pod/Gazelle) - Preforking Plack handler for performance freaks - [Twiggy](https://metacpan.org/pod/Twiggy) - AnyEvent-based PSGI server for real-time applications Example production deployment with Starman: plackup -s Starman \ --workers=10 \ --port=5000 \ --daemonize \ --pid=/var/run/myapp.pid \ bin/app.psgi ## Reverse Proxy In production, run your PSGI app behind a reverse proxy like nginx or Apache: nginx example: upstream myapp { server 127.0.0.1:5000; } server { listen 80; server_name myapp.example.com; location / { proxy_pass http://myapp; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } # Dependency Management This is a whole other topic, but given CGI.pm is no longer in the perl core you would have to install it anyway. It would be a good idea to do this the right way from beginning. I'm not going to cover this in detail here, there are many many good sources of information on the web. Here are some links to get you started: ## Managing Perl Versions - [https://github.com/tokuhirom/plenv](https://github.com/tokuhirom/plenv) - plenv: Perl binary manager - [https://perlbrew.pl/](https://perlbrew.pl/) - perlbrew: Manage multiple perl installations ## Managing Perl Modules - [https://metacpan.org/release/App-cpanminus](https://metacpan.org/release/App-cpanminus) - cpanm: Fast, lightweight CPAN client - [https://metacpan.org/release/App-cpm](https://metacpan.org/release/App-cpm) - cpm: Fast, parallel CPAN module installer (3x faster than cpanm) - [https://metacpan.org/release/Carton](https://metacpan.org/release/Carton) - Carton: Perl module dependency manager (like bundler for Ruby) - [https://metacpan.org/release/local-lib](https://metacpan.org/release/local-lib) - local::lib: Use a local lib/ directory for modules Modern approach using cpanfile and cpm: Create a cpanfile: requires 'Dancer2', '0.400000'; requires 'Template', '2.26'; requires 'Starman', '0.4015'; Install dependencies: # Fast parallel installation cpm install # Or with cpanm cpanm --installdeps . Lock dependencies with Carton: carton install carton exec plackup bin/app.psgi # TESTING One major advantage of modern frameworks is testability. All the frameworks mentioned support easy testing: use Test::More; use Plack::Test; use HTTP::Request::Common; my $app = MyApp->to_app; test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/example_form"); is $res->code, 200; like $res->content, qr/Say something/; }; done_testing; # MIGRATION STRATEGIES ## Gradual Migration You don't have to migrate everything at once: - 1. Start by wrapping your CGI scripts with Mojolicious::Plugin::CGI - 2. Add new features using the framework's native routing - 3. Gradually port old scripts to framework controllers - 4. Extract common code into libraries ## Quick Wins Start with these easy improvements: - Move to PSGI for deployment flexibility - Separate HTML into templates - Use a proper PSGI server instead of CGI - Add proper logging with [Log::Log4perl](https://metacpan.org/pod/Log%3A%3ALog4perl) or [Log::Dispatch](https://metacpan.org/pod/Log%3A%3ADispatch) - Write tests for your routes # ADDITIONAL RESOURCES - [Task::Kensho](https://metacpan.org/pod/Task%3A%3AKensho) - A Glimpse at an Enlightened Perl - [https://perlmaven.com/](https://perlmaven.com/) - Perl Maven tutorials - [https://www.perl.com/](https://www.perl.com/) - Perl.com articles - [https://blogs.perl.org/](https://blogs.perl.org/) - Perl community blogs # SEE ALSO [Task::Kensho](https://metacpan.org/pod/Task%3A%3AKensho) - A Glimpse at an Enlightened Perl [PSGI](https://metacpan.org/pod/PSGI) - Perl Web Server Gateway Interface Specification [Plack](https://metacpan.org/pod/Plack) - PSGI toolkit and servers # AUTHOR INFORMATION Lee Johnson - `leejo@cpan.org` (LEEJO) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. If you would like to contribute documentation please raise an issue / pull request: https://github.com/leejo/cgi-alternatives