Skip to content
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
175 lines (130 sloc) 4.33 KB


Embla is a powerful but simple server side application framework for Dart.


Install like so:

# pubspec.yaml
  embla: any
> pub get

Embla scripts can be run directly with dart my_script.dart, but for development we can use the Embla CLI:

> pub global activate embla
# Add pub's binaries to PATH, to be able to omit "pub run" (Also:
> PATH=$PATH:~/.pub-cache/bin
> embla start

Currently, embla start will look for a bin/server.dart file and start the app. If you make changes to your project files, the app will automatically restart.


Here's an example of a super simple Embla app.

export 'package:embla/bootstrap.dart';
import 'package:embla/http.dart';

get embla => [
  new HttpBootstrapper(
    pipeline: pipe(() => 'Hello world!')

This application starts a server, and responds with "Hello world!" on every request. Looks weird? Let's figure out what's going on.


Instead of the good old main function, Embla requires a getter called embla in the main entry point script. The actual main function will be provided by bootstrap.dart.

export 'package:embla/bootstrap.dart';

get embla => [];

If we were to run the above script, we would get an empty Dart process that did nothing, and would close on Ctrl+C.

To hook into the application, we can add Bootstrappers to the embla function. HttpBootstrapper comes out of the box if we just import 'package:embla/http.dart'. Each bootstrapper should be instantiated in the embla function, and any configuration needed is passed through the constructor.

HTTP Pipeline

It just so happens the HttpBootstrapper takes a named pipeline parameter, that represents the request/response pipeline for the server.

To create a pipeline, we use the pipe helper provided by embla/http.dart. A pipeline consists of a series of Middleware. Embla wraps Shelf for this.

import 'dart:async';

export 'package:embla/bootstrap.dart';
import 'package:embla/http.dart';

get embla => [
  new HttpBootstrapper(
    pipeline: pipe(

class MyMiddleware extends Middleware {
  Future<Response> handle(Request request) {
    // Pass along to the next middleware
    return super.handle(request);

The pipe allows for different formats for Middleware. You can pass in a Shelf Middleware directly, or the Type of a middleware class. It also supports passing in a Function, which will be converted to a route handler.


Routes are nothing more than conditional paths in the pipeline. Here's an example:

pipeline: pipe(


  Route.get('/', () => 'Hello world'),


    Route.get('', () => 'Will be reached by GET /subroutes'),

    Route.put('action', () => 'Will be reached by PUT /subroutes/action'),'another',
      () => 'Will be reached by POST /subroutes/another'

      ({String wildcard}) => 'GET /subroutes/deeper/$wildcard'

  () => 'This will be reached by request not matching the routes above'


In Embla, controllers are also middleware. They are collections of routes, after all. The controllers use annotations to declare routes.

export 'package:embla/bootstrap.dart';
import 'package:embla/http.dart';
import 'package:embla/http_annotations.dart';

get embla => [new HttpBootstrapper(pipeline: pipe(MyController))];

class MyController extends Controller {
  /// GET /action  ->  'Response'
  @Get() action() {
    return 'Response';

  /// POST /endpoint  ->  302 /
  @Post('endpoint') methodName() {
    return redirect('/action');

Since controllers are middleware too, we can easily route our controllers to endpoints like this:

Route.all('pages/*', PagesController)

Custom Bootstrappers

Bootstrappers hook into the initialization and deinitialization of the application. Creating one is super simple.

export 'package:embla/bootstrap.dart';
import 'package:embla/application.dart';

get embla => [new MyBootstrapper()];

class MyBootstrapper extends Bootstrapper {
  init() {
    print('Initializing the application!');
You can’t perform that action at this time.