mojito 0.6.1

  • README.md
  • CHANGELOG.md
  • Installing
  • Versions
  • 51

A micro framework for modern web apps built from the ground up on the shelf framework

Build Status Pub Version

Introduction

A micro framework for modern web apps built from the ground up on the Shelf Framework.

Like its namesake, Mojito is mostly sugar and a blend of other ingredients. Mojito is deliberately a very thin layer over several shelf packages and focuses on the overall experience of building an application.

The focus of Mojito is on modern rich web apps that have a clean separation of ui from services. As such it doesn't bundle any server side templating packages although these can be easily added.

The core architecture of Mojito is shelf itself. All components are existing pub packages that are built from the ground up as shelf components. This makes it super easy to take advantage of any new shelf based packages that come along in the future

Usage

Getting Started

To create a web server and start it on port 9999 type the following in a file and run it.

import 'package:mojito/mojito.dart';

main() {
  var app = init();
  app.start();
}

You should see output like

2015-06-28 13:03:27.123 [INFO] mojito: Serving at http://:::9999

This won't do anything interesting though as we haven't added any routes.

Lets fix that now

main() {
  var app = init();

  app.router.get('/hi', () => 'hi');

  app.start();
}

This time when you start it up you should also see something like

2015-06-28 13:06:31.957 [INFO] mojito: GET	->	/hi

Try it out with curl

 curl http://localhost:9999/hi

and you should see the expected response of 'hi'

Development Mode

Mojito has a concept of a development mode that helps make for a quick dev loop. By default it will activate dev mode based on an environment variable MOJITO_IS_DEV_MODE. You can activate this in a shell prompt like

export MOJITO_IS_DEV_MODE=true

You can easily override how the dev mode is determined when you initialise mojito. For example

var app = init(isDevMode: () => true);

sets it to always on. Typically you don't want to do that though.

If you run on appengine then the following can be used to set the dev mode.

var app = init(isDevMode: () => Platform.environment['GAE_PARTITION'] == 'dev');

Routing

Mojito comes with a very feature rich router. You access the root router by calling app.router. It supports several styles for creating routes such as:

  • route annotations
@Get('{accountId}')
Account find(String accountId) => new Account.build(accountId: accountId);
  • using the fluent Router api
router.get('{accountId}', (String accountId) => new Account.build(accountId: accountId));
  • in built support for CRUD style methods and so on

All styles support:

  • adding middleware at any point in the route hierarchy
  • automatic conversion to / from JSON and your Dart classes

To get a good overview of the options you have, read the blog post Routing Options in Shelf.

The mojito router extends shelf_rest's router. As this is documented extensively in the shelf_rest documentation I won't repeat it here.

Additionally, mojito provides routing methods for the following tasks.

Static Asset Handling

Static assets such as html and css are a mainstay of most web applications.

In production these assets are served from the filesystem, but in development it is much more convenient to user pub serve.

Mojito makes this very easy, by allowing you to set up a static asset handler that uses pub serve in development mode and the filesystem in production (see section on Development Mode for details on activation).

The following example sets that up a route for all requests starting with /ui that uses the default settings for pub serve (port 8080) and file system path (build/web).

app.router.addStaticAssetHandler('/ui');

Pro Tip

Under the covers addStaticAssetHandler uses shelf_static and shelf_proxy to handle the static assets.

OAuth (1 and 2) Client

The Mojito router provides methods to set up the routes required to implement the 'client' part of the OAuth 2 Authorization Code Flow and similar routes for OAuth1

This allows developers to write web applications that interact with OAuth enabled services like:

  • google
  • github
  • bitbucket
  • hipchat
  • many many more

To simplify this even further, mojito supports several authorisation servers out of the box. The following example shows how to add a github client when deploying on Google Appengine using memcache to store the OAuth2 data.

final oauth = app.router.oauth;

oauth.gitHub().addClient(
    (_) => new ClientId('your clientId', 'your secret'),
    oauth.storage.memcache(() => context.services.memcache),
    new UriTemplate(
        'http://example.com/loginComplete{?type,token,context}'));

You access the route builders for oauth by calling router.oauth. From there you have access to out of the box oauth storage (such as memcache and in memory for development), plus customised route builders for common authorisation servers like github, google and bitbucket (PRs welcome for more servers).

For other (non out of the box) authorisation servers use the oauth.oauth2(...) or oauth.oauth1(...) methods.

When you start the above example you will see two routes created for the github flow

2015-06-29 08:44:51.503 [INFO] mojito: GET	->	/github/userGrant
2015-06-29 08:44:51.503 [INFO] mojito: GET	->	/github/authToken

The userGrant route is where you send the users browser to to initiate the flow. It will redirect to github for the user to grant access, upon which github will redirect the user back to the authToken route.

On successful completion of the auth flow, the users browser will be redirected back to the url you provided ('http://example.com/loginComplete' in this example) with the query params for type, token and context populated accordingly.

A good place to get started with oauth in mojito is to run oauth.dart in the example folder of mojito.

This sets up routes for the out of the box integrations. You can then try it out by opening a browser to the userGrant urls such as

http://localhost:9999/oauth/github/userGrant

Pro Tip

Mojito uses shelf_oauth to implement the oauth flows.

Context

Mojito makes some things, such as the currently logged in user, available via a context property. To access simply import mojito. For example

import 'package:mojito/mojito.dart';

somefunction() {
  print(context.auth);
}

Authentication

Mojito exposes helpers for setting up authentication via app.auth. If you want to apply it to all routes then use the global builder.

Global Authentication

For example the following sets the application to use basic authentication, allowing access over http (a bad idea other than in development) and allows anonymous access.

  app.auth.global
    .basic(_lookup)
    ..allowHttp=true
    ..allowAnonymousAccess=true;

Note the allowAnonymousAccess is actually a form of authorisation (rather than authentication) and is simply a convenience. See the Authorisation section below for more options.

Currently Authenticated User

The currently authenticated user (if there is one) is available via the mojito context.

It is defined as an Option which will be None if there is no currently authenticated user and Some if there is.

For example, the following gets the logged in user's username, if there is one, or sets it to 'guest' otherwise.

app.router..get('/hi', () {
  var username = context.auth.map((authContext) =>
      authContext.principal.name)
      .getOrElse(() => 'guest');

  return 'hello $username';
});

Route Specific Authentication

To apply a particular authentication to only some routes use the auth builder() and add it using the named parameter middleware on the desired route.

  var randomAuthenticator = (app.auth
      .builder()
      .authenticator(new RandomNameAuthenticator())..allowHttp = true).build();

  app.router
    ..get(
        '/randomness',
        () {
          String username = context.auth
              .map((authContext) => authContext.principal.name)
              .getOrElse(() => 'guest');

          return 'who are you today $username';
        },
        middleware: randomAuthenticator);

Here the '/randomness' route has middleware: randomAuthenticator which applies that authenticator to the route.

Pro Tips

  • If you add authentication middleware to a route defined with router.addAll then it will apply to all it's child routes.
  • See basic_example.dart in the examples folder to see how RandomNameAuthenticator is implemented
  • mojito uses shelf_auth for authentication support. Consult the shelf_auth docs for more details

Authorisation

Mojito exposes helpers for setting up authorisation via app.authorisation. Similarly to authentication, if you want to apply it to all routes then use the global builder, otherwise use the builder().

The following shows how to enforce that only authenticated users can access a particular route. This is useful, for example if you set up a global authenticator that allows anonymous access and you want to block anonymous access to some routes.

app.router.get('privates', () => 'this is only for the privileged few',
        middleware: app.authorisation.builder().authenticatedOnly().build())

Pro Tip

Other Middleware

Mojito exposes helpers for setting up other middleware via app.middleware. Similarly to authentication, if you want to apply it to all routes then use the global builder, otherwise use the builder().

Integrating with other Shelf Packages

It is also easy to use any shelf packages that are not bundled with mojito.

shelf packages will expose a shelf Handler. All the main mojito router methods take a handler argument, so it is largely a matter of plugging the handler from the shelf package you want to integrate, into the router method you want to use.

If the package will support more than one http method or if it serves more than a single set path then the add method should be used.

Note: if you can also use the @Add annotation if you prefer

Example - integrating shelf_rpc


import 'package:mojito/mojito.dart';
import 'package:shelf_rpc/shelf_rpc.dart' as shelf_rpc;
import 'package:rpc/rpc.dart';

main() {

  var app = init();

  var apiServer = <create rpc apiServer somehow>;
  // create a shelf handler from the api
  var handler = shelf_rpc.createRpcHandler(apiServer);

  // create a route for the handler. 
  app.router
    ..add('rpc', null, handler, exactMatch: false);

  app.start();
}

Note exactMatch: false is needed as shelf_rpc serves many sub routes. Also passing null as the value of the methods argument is used so that all methods are passed to the api.

Under the hood

Mojito bundles lots of existing shelf libraries and integrates them for easier use. These include:

0.6.1

  • fixed: had git dependency for 'config'

0.6.0

  • added cors
  • support for config

0.5.0

  • includes shelf_bind 0.9.0 which has many significant and some BREAKING changes. Consult the change logs for shelf_bind

0.4.0

  • Significantly improved Readme, dartdocs and examples
  • Revamped oauth client support
    • removed addOAuth2Provider and addOAuth1Provider from router
    • added oauth property to router providing access to new builders
    • out of the box support for creating oauth clients for:
      • github
      • bitbucket
      • google
  • Removed perRequestLogProcessor as wasn't viable
  • create a root Logger by default

0.3.0

  • Now extends the new shelf_rest router and inherits its shiny new features

0.2.0

  • upgraded version of shelf_oauth. Breaking if you relied on accessType in oauth2 being 'offline'

0.1.7

  • upgraded version of shelf_auth

0.1.6

  • upgraded version of shelf_auth

0.1.5

  • upgraded version of shelf_oauth

0.1.4

  • upgraded version of shelf_oauth

0.1.3

  • upgraded version of shelf_auth for exclusion support in authorisers

0.1.2

  • upgraded version of shelf_auth

0.1.1

  • added authorisation middleware

0.1.0+1

  • fixed bug where middleware was not passed to super ctr in router

0.1.0

  • added oauth2 support

0.0.3

  • support for same pub serve vs static trick that appengine uses

0.0.2

  • oauth support

0.0.1

  • First version

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  mojito: "^0.6.1"

2. Install it

You can install packages from the command line:

with pub:


$ pub get

Alternatively, your editor might support pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:mojito/mojito.dart';
        
Version Uploaded Documentation Archive
0.6.6 Oct 21, 2016 Go to the documentation of mojito 0.6.6 Download mojito 0.6.6 archive
0.6.5 Apr 10, 2016 Go to the documentation of mojito 0.6.5 Download mojito 0.6.5 archive
0.6.4 Jan 6, 2016 Go to the documentation of mojito 0.6.4 Download mojito 0.6.4 archive
0.6.3 Nov 18, 2015 Go to the documentation of mojito 0.6.3 Download mojito 0.6.3 archive
0.6.2 Aug 13, 2015 Go to the documentation of mojito 0.6.2 Download mojito 0.6.2 archive
0.6.1 Aug 13, 2015 Go to the documentation of mojito 0.6.1 Download mojito 0.6.1 archive
0.6.0 Aug 12, 2015 Go to the documentation of mojito 0.6.0 Download mojito 0.6.0 archive
0.5.2 Jul 14, 2015 Go to the documentation of mojito 0.5.2 Download mojito 0.5.2 archive
0.5.1 Jul 14, 2015 Go to the documentation of mojito 0.5.1 Download mojito 0.5.1 archive
0.5.0 Jul 12, 2015 Go to the documentation of mojito 0.5.0 Download mojito 0.5.0 archive

All 39 versions...

Analysis

This feature is new.
We welcome feedback.

We analyzed this package, and provided a score, details, and suggestions below.

  • tool failures on Dec 7, 2017
  • Dart: 2.0.0-dev.8.0
  • pana: 0.7.3+1

Scores

Popularity:
Describes how popular the package is relative to other packages. [more]
73
Health:
Code health derived from static analysis. [more]
48
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
0
Overall score:
Weighted score of the above. [more]
51

Platforms

Detected platforms:

Error(s) prevent platform classification.

Suggestions

  • Fix lib/src/config.dart.

    Strong-mode analysis of lib/src/config.dart failed with the following error:

    line: 13 col: 30
    Type parameter bound types must be instantiated.

  • Fix lib/src/auth_impl.dart.

    Strong-mode analysis of lib/src/auth_impl.dart failed with the following error:

    line: 12 col: 7
    Inconsistent declarations of 'global' are inherited from () → MiddlewareBuilder, () → AuthenticationBuilder.

  • Fix further 6 Dart files.

    Similar analysis of the following files failed:

    • lib/src/router_impl.dart
    • lib/src/authorisation_impl.dart
    • lib/src/mojito.dart
    • lib/src/oauth_impl.dart
    • lib/src/context.dart
    • lib/src/mojito_impl.dart

Dependencies

Package Constraint Resolved Available
Direct dependencies
config ^0.0.2 0.0.5
http_exception ^0.1.0 0.1.0
matcher >=0.10.0 <0.13.0 0.12.1+4
oauth >=0.3.0 <0.4.0 0.3.0 0.4.0
option >=0.3.0 <2.0.0 1.1.0 1.2.0
shelf ^0.6.2 0.6.8 0.7.1
shelf_auth >=0.7.0 <0.7.1 0.7.0 0.7.4
shelf_auth_session >=0.4.9 <0.4.10 0.4.9 0.4.12
shelf_bind ^0.9.1 0.9.1 0.9.4
shelf_exception_handler ^0.1.0 0.1.0
shelf_oauth >=0.8.0 <0.8.1 0.8.0 0.8.2
shelf_oauth_memcache ^0.3.5 0.3.5 0.3.7
shelf_path ^0.1.7 0.1.7 0.1.8
shelf_proxy ^0.1.0+2 0.1.0+4
shelf_rest >=0.3.2 <0.3.3 0.3.2 0.3.5
shelf_static ^0.2.2 0.2.6
Transitive dependencies
args 0.12.2+6 1.2.0
async 1.13.3 2.0.1
bignum 0.0.7 0.1.0
charcode 1.1.1
cipher 0.7.1
collection 1.14.3
concepts 0.2.0
constrain 0.2.8
convert 2.0.1
converters 0.0.3
crypto 0.9.1 2.0.2+1
dart_jwt 0.4.6 0.6.0
either 0.1.8
fixnum 0.9.1+2 0.10.6
hateoas_models 0.1.6 0.2.0
http 0.11.3+16
http_parser 3.1.1
logging 0.11.3+1
memcache 0.2.1+2
mime 0.9.4
path 1.5.1
petitparser 1.6.1
quiver 0.20.0 0.26.2
shelf_response_formatter 0.1.0
shelf_route 0.13.5 0.14.3
source_span 1.4.0
stack_trace 1.9.1
stream_channel 1.6.2
string_scanner 1.0.2
typed_data 1.1.5
uri 0.10.0 0.11.1
utf 0.9.0+3
uuid 0.4.1 0.5.3
xml 2.6.0
Dev dependencies
mockito >=0.8.1 <0.11.0
test ^0.12.0