Mojolicious and DB handles

By Abhijit Menon-Sen <ams@toroid.org>

2011-02-28

"But… what about the model?" is a very common question from newcomers to Mojolicious, and not everyone is happy with the simple answer: "You can do whatever you want, Mojolicious doesn't care". What this really means (especially for people coming from Catalyst) is that you can keep using your DBIx::Class model with "use My::Model", or use whatever other method you prefer to interact with your database.

I don't get along with DBIx::Class myself, but enough people ask about using straight DBI that I felt it worthwhile to share the way I do it. There's not much to it: my code boils down to calling DBI->connect and keeping the handle for later use. Mojo's package attributes with delayed initialisation make it easy to do this.

I put the following code in my startup function (i.e., sub startup in lib/MyApp.pm):

sub startup {
    my $app = shift;

    # …read configuration and do other stuff…

    (ref $app)->attr(
        db => sub {
            DBI->connect("dbi:Pg:database=$db", $user, $pass)
        }
    );
}

This adds a "db" attribute to my application class (represented here by the $app object), which can be accessed as "$app->db". The first time it is accessed, the function I provided is called, and its return value is cached and returned as the value of the attribute thereafter. In this case, my sub is a closure that uses the $db, $user, and $pass variables read from the configuration file, and connects to a Postgres database using DBI/DBD::Pg, but those are just details. You could do the same thing with DBIx::Connector or a similar module.

(Aside: don't just create a database handle in startup. It will break if you run your app under a preforking server like Hypnotoad. Your startup will be called once before forking, but the DBI handle can't be shared between the server processes. The delayed attribute initialisation is perfect in this case, because each server will have its own handle.)

With this code, the database connection is created only the first time it is needed, and it is kept around as long as the application is alive (how long that is depends on which server your application runs under). Controllers can just do "$self->app->db" to fetch a db handle at any time without worrying about any of this.

Marcus Ramberg pointed out on IRC that I could also use "$app->helper(db => sub {...})" to make the handle accessible from controllers (as "$self->db") and from epl templates (as "db"). I don't need to do that for database handles, but someone else might find it useful.

(This is an old post that I forgot to submit when it was written.)