Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/rawleyfowler/slick

A Plack toolkit for web apis, web apps, and everything in-between.
https://github.com/rawleyfowler/slick

microservice-framework perl perl5 plack plack-app restful-webservices

Last synced: about 1 month ago
JSON representation

A Plack toolkit for web apis, web apps, and everything in-between.

Awesome Lists containing this project

README

        

# [Slick](https://metacpan.org/pod/Slick)

Slick is an Object-Oriented Perl web-framework for building performant, and easy to refactor web applications.
Slick is built on top of [DBI](https://metacpan.org/pod/DBI), [Plack](https://metacpan.org/pod/Plack),
and [Moo](https://metacpan.org/pod/Moo) and fits somewhere in-between the realms of Dancer and Mojo.

Slick has everything you need to build a Database driven REST API, including built in support
for Database connections, Migrations, and Caching via Redis or Memcached. Since Slick is a Plack application,
you can also take advantage of swappable backends and Plack middlewares extremely simply.

Currently, Slick supports `MySQL`, `Redis`, `Memcached` and `Postgres` but there are plans to implement `Oracle` and `MS SQL Server`.

## Philosophy

Slick is aiming to become a "Batteries Included" framework for building REST API's and Micro-Services in
Perl. This will include tooling for all sorts of Micro-Service concerns like Databases, Caching, Queues,
User-Agents, and much more.

### Goals

- [x] Database management (auto-enabled)
- [x] Migrations (auto-enabled)
- [ ] CLI
- [x] Caching via Redis (optional)
- [x] Caching via Memcached (optional)
- [x] Sub-routine based caching for routes (optional)
- [ ] RabbitMQ built-ins (optional)
- [ ] AWS S3 support (optional)
- [ ] User-Agents, including Client API exports
- [ ] AWS SQS support (optional)

*Note*: All of these features excluding database stuff will be enabled optionally at run-time.

## Examples

### Single File App
```perl
use Slick;

my $s = Slick->new;

# Both MySQL and Postgres are supported databases
# Slick will create the correct DB object based on the connection URI
# [{mysql,postgres,postgresql}://][user[:[password]]@]host[:port][/schema]
$s->database(my_db => 'postgresql://user:[email protected]:5432/schema');
$s->database(corporate_db => 'mysql://corporate:[email protected]:3306/schema');

$s->database('my_db')->migration(
'create_user_table', # id
'CREATE TABLE user ( id SERIAL PRIMARY KEY AUTOINCREMENT, name TEXT, age INT );', #up
'DROP TABLE user;' # down
);

$s->database('my_db')->migrate_up; # Migrates all pending migrations

$s->get('/users/{id}' => sub {
my $app = shift;
my $context = shift;

# Queries follow SQL::Abstract's notations
my $user = $app->database('my_db')->select_one('user', { id => $context->param('id') });

# Render the user hashref as JSON.
$context->json($user);
});

$s->post('/users' => sub {
my $app = shift;
my $context = shift;

my $new_user = $context->content; # Will be decoded from JSON, YAML, or URL encoded (See JSON::Tiny, YAML::Tiny, and URL::Encode)

$app->database('my_db')->insert('user', $new_user);

$context->json($new_user);
});

$s->run; # Run the application.
```

See the examples directory for this example.

### Multi-file Router App

```perl
### INSIDE lib/MyApp/ItemRouter.pm

package MyApp::ItemRouter;

use base qw(Slick::Router);

my $router = __PACKAGE__->new(base => '/items');

$router->get('/{id}' => sub {
my ($app, $context) = @_;
my $item = $app->database('items')->select_one({ id => $context->param('id') });
$context->json($item);
});

$router->post('' => sub {
my ($app, $context) = @_;
my $new_item = $context->content;

# Do some sort of validation
if (not $app->helper('item_validator')->validate($new_item)) {
$context->status(400)->json({ error => 'Bad Request' });
}
else {
$app->database('items')->insert('items', $new_item);
$context->json($new_item);
}
});

sub router {
return $router;
}

1;

### INSIDE OF YOUR RUN SCRIPT

use 5.036;
use lib 'lib';

use Slick;
use MyApp::ItemRouter;

my $slick = Slick->new;

$slick->register(MyApp::ItemRouter->router);

$slick->run;
```

See the examples directory for this example.

### Running with `plackup`

If you wish to use `plackup` you can change the final call to `run` to a call to `app`

```perl
$s->app;
```

Then simply run with plackup (substitue `my_app.psgi` with whatever your app is called):

```bash
plackup -a my_app.psgi
```

### Changing PSGI backend

Will run on the default [`HTTP::Server::PSGI`](https://metacpan.org/pod/HTTP::Server::PSGI).
```perl
$s->run;
```

or

In this example, running Slick with a [`Gazelle`](https://metacpan.org/pod/Gazelle) backend on port `8888` and address `0.0.0.0`.
```perl
$s->run(server => 'Plack::Handler::Gazelle', port => 8888, addr => '0.0.0.0');
```

### Using Plack Middlewares

You can register more Plack middlewares with your application very easily!

```perl
my $s = Slick->new;

$s->middleware('Deflater')
->middleware('Session', store => 'file')
->middleware('Debug', panels => [ qw(DBITrace Memory) ]);

$s->run; # or $s->app depending on if you want to use plackup.
```

## Managing Your Database(s)

Slick allows you to easily connect databases to your applications.

### Creating a database
```perl
my $s = Slick->new;
$s->database(my_postgres => 'postgresql://username:[email protected]:5432/db_name');
```

### Migrations

Migrations are built using the `migration` method on `Slick::Database`. You provide 1, an ID for the migration,
2, the runnable/happy side of the migrations, and 3, the down or reverse of the migration.

```perl
$s->database('my_postgres')
->migration('create_users_table',
'CREATE TABLE users ( id INT PRIMARY KEY, name TEXT, age INT );',
'DROP TABLE user;')
->migration('create_pets_table',
'CREATE TABLE pets ( id INT PRIMARY KEY, name TEXT, owner INT FOREIGN KEY REFERENCES users (id) );',
'DROP TABLE pets;');
```

### Queries

Queries in Slick are built with [`SQL::Abstract`](https://metacpan.org/pod/SQL::Abstract), and most of the heavy lifting
is done for you already!

```perl
my $users = $s->database('my_postgres')
->select('users', [ 'id', 'name' ]); # SELECT id, name FROM users;

my $user = $s->database('my_postgres')
->select_one('users', [ 'id', 'name', 'age' ], { id => 1 }); # SELECT id, name, age FROM users WHERE id = 1;

$s->database('my_postgres')
->insert('users', { name => 'Bob', age => 23 }); # INSERT INTO users (name, age) VALUES ('Bob', 23);

$s->database('my_postgres')
->update('users', { name => 'John' }, { id => 2 }); # UPDATE users SET name = 'John' WHERE id = 2;
```

If you can't do what you want with `SQL::Abstract` helpers, you can certainly do it with DBI!

```perl
$s->database('my_postgres')->dbi->execute('DROP TABLE users;');
```

## Caching

Slick supports caching using [`Memcached`](https://memcached.org) or [`Redis`](https://redis.io).

```perl
use 5.036;

use Slick;
use Slick::Annotation qw(cacheable);

my $s = Slick->new;

# See Redis and Cache::Memcached on CPAN for arguments

# Create a Redis instance
$s->cache(
my_redis => type => 'redis', # Slick Arguments
server => '127.0.0.1:6379' # Cache::Memcached arguments
);

# Create a Memcached instance
$s->cache(
my_memcached => type => 'memcached', # Slick Arguments
servers => ['127.0.0.1'] => debug => 1 # Cache::Memcached arguments
);

$s->cache('my_redis')->set( something => 'awesome' );

$s->get(
'/foo' => sub {
my ( $app, $context ) = @_;
my $value = $app->cache('my_redis')->get('something'); # Use your cache
return $context->text($value);
}
);

# Use your cache to cache a route
$s->get(
'/foobar' => cacheable(
'my_redis',
sub {
my ( $app, $context ) = @_;
return $context->json( { foo => 'bar' } );
}
)
);

$s->run;
```

## Deployment

Please follow a standard `Plack` application deployment. Reverse-proxying your application behind
[`NGiNX`](https://nginx.org) or [`Caddy`](https://caddyserver.com) and using [`Docker`](https://www.docker.com) can
drastically improve your deployment.

An example `Dockerfile` can be found in the examples directory.

## Contributing

Slick is open to any and all contributions.

**Code Standards**:

* Always format with the provided `.perltidyrc`
* Always use `Perl::Critic` set to severity `3`
* Unpack subroutine arguments using array-destructuring when there are 2 or more elements

## License

Slick is provided under the Artistic 2.0 license.