Dancer2

 view release on metacpan or  search on metacpan

lib/Dancer2/Manual/Tutorial.pod  view on Meta::CPAN

    ./t/004_blog.t ......... ok
    All tests successful.
    Files=4, Tests=15,  3 wallclock secs ( 0.02 usr  0.00 sys +  2.30 cusr  0.32 csys =  2.64 CPU)
    Result: PASS

=head2 Deploying with a PSGI Server

C<plackup>, by defaults, runs a development server for developing your
application. It is not suitable for any public-facing deployment.

There are a number of great options available on the L<Plack|https://plackperl.org/>
website. For our example, we'll use L<Starman>, as it offers reasonable
performance and functions on nearly any server OS.

We'll pair Starman with L<Server::Starter>, which will give you a robust
way to manage server processes.

Install both of these modules:

    cpanm Starman Server::Starter

And add them to the blog's F<cpanfile>:

    requires "Starman";
    requires "Server::Starter";

Assuming we're deploying to a Debian server, the following can be used to
start the Danceyland blog in the background:

    sudo start_server \
        --daemonize \
        --dir=/path/to/DLBlog \
        --port=5000 \
        --log-file=/var/log/dlblog.log \
        --pid-file=/var/run/dlblog.pid \
        --status-file=/var/run/dlblog.status \
        -- plackup -s Starman--user www-data --group www-data -E production \
        bin/app.psgi

Once operational, the server can be restarted with:

    start_server --restart --pid-file=/var/run/dlblog.pid --status-file=/var/run/dlblog.status

Or stopped with:

    start_server --stop --pid-file=/var/run/dlblog.pid

=head2 Configuring Reverse Proxy with NGINX

Finally, let's put NGINX in front of our Dancer2 application. This will
improve the security and performance of our application:

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://127.0.0.1:5000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }

If this server is public facing, you should also configure it with HTTPS;
L<Let's Encrypt/https://letsencrypt.org> makes this free and easy.

More advanced setups are possible (like serving static content from NGINX
instead of Dancer2), but that is beyond the scope of this tutorial.

=head1 What You've Built

Congratulations! You have built a primitive but very functional blog
engine that protects maintenance functions behind a user login, using
L<Dancer2> and other plugins and modules in its ecosystem. You've learned
a number of important building blocks that will be crucial for building
other applications.

=head1 Where to Go Next

This application can be used as a springboard and reference for future
L<Dancer2> projects. There are still a number of improvements and additional
features that can be added to this blog. A few ideas include:

=over

=item Paginate the list of blog entries

After a while, the list of blog entries can get long. Using L<Data::Page>
or other Perl modules, break the list of entries into reasonable page
sizes to more easily and quickly navigate.

=item Add a search function to the blog

Add a search bar to the UI, then use the C<search()> method in
L<DBIx::Class> to find blog entries based on what the used input.

=item Improve application security by sanitizing input parameters

Using a regex or a validation framework (such as
L<Dancer2::Plugin::DataTransposeValidator>), scrub all input before using
it in a database operation.

=item Use database slugs in URLs instead of IDs

A database slug is a human-readable identifier (such as a URL encoding of
the entry title) that can be used to identify an entry rather than the
numerical ID. They are easier to remember than IDs, are better for SEO of
your content, and makes your application more secure by hiding some database
implementation details from an attacker (such as a row ID).

=item Move business logic to business layer; call business object from Dancer2

In larger applications, business logic (like creating a blog post) may
be put in an object (such as L<DBIx::Class::Result> or L<DBIx::Class::ResultSet>
objects, or other L<Moo> or L<Moose> objects), and that object gets
instantiated and called from Dancer2. This helps to decouple tasks in an
application, and allows for better testing of business logic.

=item Write more tests to catch error conditions for our blog



( run in 0.586 second using v1.01-cache-2.11-cpan-39bf76dae61 )