Getting started with full-stack web development in Dartlang


Ultimate guide to get started with Dart
05-Jan-2017 | Anton Alex Nesterov

If you tried Dart, liked it, but couldn’t get trough a lot of small issues to get started. Probably you’ll find most of the solutions here.

I tried Dart couple of times and everytime I got back to Python and Javascript because my every attempt led to problems with whether Dart SDK bugs or lack of tools. I tried a lot of languages and tools and eventually, in couple of years, I got back to Dart and decided to solve all problems a had with previous attempts.

My goal is to have one language on every matter related to web development. You are probably thinking about NodeJs and how fast Javasript is growing. Yes, Javascript is good for most, but for me it never be best choice because I had experience with lots of disorganized spaghetti code and it left bad unforgettable impression.

Content

  1. Packaging and distributing
  2. Angular2 for Dart and ‘pub serve’
  3. Backend with Redstone.dart
  4. Hot Reloading and CORS issues
  5. Calling Javascript from Dart
  6. Angular2 Dart: Auth
  7. Redstone.dart: Authenticate user with JWT
  8. Redstone.dart: Database
  9. Web Sockets
  10. Send Emails
  11. Deployment

Setting Environment

If you don’t use any IDE I advise to use Sublime Text. Dart official site suggests some IDEs and editors support. I tried to go with IDEs and Atom Editor and every time dart interpreter consumed decent part of memory (600-1000Mb), it is pretty much for my laptop with 4Gb.

Install Dart SDK and modify path variable to your environment.

For fish:

set -gx PATH /usr/lib/dart/bin/ $PATH
set -gx PATH ~/.pub-cache/bin $PATH

For bash:

export PATH="/usr/lib/dart/bin/:$PATH"
export PATH="~/.pub-cache/bin:$PATH"

Now you will be able to run dart interpreter and package manager.


Packaging and Distributing

Dart package manager - pub uses pubspec.yaml file to resolve depedencies and the package metadata; this file has to be in your package root.

The your package code must be in the dirrectory called ‘lib’. Executables, scripts, utils, etc. has to be in the directory called ‘bin’. Package manager will create dirrectory called packages. Web frontend and static files has to be in the dirrectory called web; package manager will create the directory called build when you compile it to javascript.

Let’s create new dart package manually

Create directories:

 mkdir dart_project && cd dart_project
    mkdir bin lib web lib/client lib/server

Create ‘pubspec.yaml’ with following content:

    name: your_package_name
    description: Your Description
    version: 0.0.1
    environment:
      sdk: '>=1.13.0 <2.0.0'
    dependencies:
      angular2:
      browser:
      bootstrap_sass:
      http:
      sass:
      ng2_strap:
      font_awesome:
      dart_to_js_script_rewriter:
    transformers:
    - angular2:
        resolved_identifiers:
          BrowserClient: 'package:http/browser_client.dart'
        platform_directives:
        - 'package:angular2/common.dart#COMMON_DIRECTIVES'
        platform_pipes:
        - 'package:angular2/common.dart#COMMON_PIPES'
        entry_points: web/main.dart
    - dart_to_js_script_rewriter
    - sass

Now we got a project backbone. Let’s get all project depedencies by running pub get

~# pub get
Resolving dependencies... (16.6s)
 - bla...
 - bla...

Your project directory has to look like following:

├── bin
├── lib
│   ├── client
|   |    └── ...
│   └── server
|   |    └── ...
├── packages
│   └── ...
├── web
└── pubspec.yaml

Angular2 for dart and ‘pub serve’

‘index.html’ and related static files suppose to be in the web directory. dart2js and build scripts will use transformers defined in pubspec.yaml to create a build. We already included transformers for Angular2 and scss.

Let’s create index.html and main entry point for the client application

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <title></title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <base href="/">
  <link rel="stylesheet" href="packages/bootstrap_sass/sass/bootstrap.css">
  <link rel="stylesheet" href="packages/ng2_strap/styles/ng2_strap.css">
  <link rel="stylesheet" href="packages/font_awesome/css/font-awesome.min.css">
  <link rel="stylesheet" href="style.css">
  <script defer src="main.dart" type="application/dart"></script>
  <script defer src="packages/browser/dart.js"></script>
</head>
<body>
  <root></root>
</body>
</html>

Notice that ‘packages’ directory referred automatically.

main.dart

import 'package:http/browser_client.dart';
import 'package:angular2/platform/browser.dart';
import "package:angular2/core.dart";
import 'package:angular2/platform/common.dart' show HashLocationStrategy, LocationStrategy;
import 'package:angular2/router.dart' show ROUTER_PROVIDERS;

import 'package:your_package_name/client/application.dart';


void main() {
    // bootstrap angular2
    bootstrap(RootComponent, [
      ROUTER_PROVIDERS,
      provide(LocationStrategy, useClass: HashLocationStrategy, deps:[]),
      provide(BrowserClient, useFactory: () => (new BrowserClient()), deps: []),
    ]);
}

Notice that we import corresponding files using the package name import 'package:your_package_name/client/application.dart'; Other notable thing is that I applied HashLocationStrategy to enforce the browser navigate trough # sign; it is convenient because we have only index.html and no other routes presented on the frontend.

Create lib/client/application.dart with your root angular component.

Example:

import 'dart:html';
import 'dart:async';

import "package:angular2/core.dart";
import 'package:angular2/common.dart' show NgClass, NgStyle;
import 'package:angular2/router.dart';



@Component(
  selector: "root",
  templateUrl:'application.html',
  styleUrls: const ['application.css'],
  directives: const [ROUTER_DIRECTIVES, NgStyle, NgClass],
)
@RouteConfig(const [
    const Route(
        path:"/",
        name: "Dashboard",
        component: DashboardComponent,
        useAsDefault: true
     ),
])
class RootComponent implements OnInit {
  String title = '';

  ngOnInit(){
    //do init stuff
  }

}

Cool things is that attributes like templateUrl:"application.html" and styleUrls: const ['application.css'] read from the same place. So you just need to add this files in the same directory.

Project structure so far:

├── bin
|   └── ...
├── lib
│   ├── client
|   |    ├── application.dart
|   |    ├── application.html
|   |    └── application.css
│   ├── server
|   |    └── ...
├── packages
│   └── ...
├── web
|   ├── index.html
|   ├── style.css
|   └── main.dart
|
└──  pubspec.yaml

Add some content to the lib/client/application.html using angular2 template syntax and run pub serve on the project dir.

~# pub serve --port 4444

The server will autoreload on all changes made insde the package directory.


Backend with Redstone.dart

I was surprised when found something similar to Python’s Flask framework written in Dart. To be precise it’s not ‘like Flask’ but it is as convenient as Flask. For the history reference: Dart has its own core web server within standard library. There’s a library called shelf which is something like python’s Werkzeug or WebOb. And the Redstone.dart is built upon Shelf.

Quick Example:

import 'package:redstone/redstone.dart' as app;

@app.Route('/')
index(){
  return "Hello Glob!";
}

main(){
  app.start();
}

As you might suspected the server code will be located in the server directory. But before we do anything we have to add corresponding depedency to pubspec.yaml:

dependencies:
    redstone:

And the do pub get

~# pub get

Let’s create some dummy API.

Create file called main.dart in the server directory. Notice that I use library declaration to concatenate other files to the main one using part. But you can do it your way.

lib/server/main.dart

library server;
import "dart:io";
import "dart:async";
import 'package:redstone/redstone.dart' as app;
import "package:di/di.dart";

part './routes.dart';

start(){
  app.setupConsoleLog();
  app.start();
}

lib/server/routes.dart

part of server;

@app.Route('/api')
api(){
  return {};
}

Now we need to actually serve this API prototype. This is where we use ‘bin’ directory. Let’s create a file called server in the ‘bin’ directory with following content:

bin/server

import 'package:lead/server/main.dart';
main()=>start();

This file has main function which starts our API server. To run it use ‘dart bin/server’ command.

~# dart bin/server
INFO: 2016-07-31 10:29:10.088969: Configured error handler for /api  :server.api
INFO: 2016-07-31 10:29:10.144473: Running on 0.0.0.0:8080


Hot Reloading and CORS issues

So. When you get the frontend and the backend dev. servers running you actually got some issues with it. Everyone who worked fullstack knows about CORS. So bassically usually you need to allow cross origin requests using an http header, and then you need to allow some set of headers which client application can send. The second issue is that currently(2016) Redstone.dart and Dart itself doesn’t have autoreloding feature. Well it seems like a bummer but I have a solution.

Solution for everything:

Instead of spoiling server code let’s just serve frontend on the same origin. But remember that we need to support hot reloading. The thing is that ‘pub serve’ supports hot reloding, and will reload frontend server even when files in ‘lib/server’ are changed. There is additional handlers for Shelf (redstone is based on it. right?). What we need here is StaticHandler for production use and ProxyHandler for development use.

Exampe:

library server;
import "dart:io";
import "dart:async";
import 'package:redstone/redstone.dart' as app;
import "package:di/di.dart";

const PRODUCTION = false;

start(){
  if (PRODUCTION)
  {
    app.setShelfHandler(createStaticHandler("./build/web", defaultDocument: "index.html", serveFilesOutsidePath: true)); //serve from build
  }
  else
  {
    app.setShelfHandler(proxyHandler("http://localhost:4444")); //proxy to dev
  }

  app.setupConsoleLog();
  app.start();
}

The solution is great because you will serve files right from frontend dev server which will rebuid files on every change. The problems are that you need to run both server at once and the backend server do not support hot reloading.

Solution:

I dind’t find better way than write a python script that manages both server processes. It runs both processess and whenever the frontend server logs a message about successfull build it restarts the backend server.

bin/main.py

import subprocess, threading, os

killers = {}

def run(exe):
    p = subprocess.Popen(exe.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    print("Start "+exe)
    while(True):
      retcode = p.poll()
      line = p.stdout.readline()
      if killers.get(exe):
        p.kill()
        print("Kill "+exe)
        del killers[exe]
      yield line
      if(retcode is not None):
        break

if __name__=="__main__":

    def run_server():
        def serve():
            for line in run('dart bin/server'):
                if line.strip() and line.strip()!='\n': print(line)
        backend_t = threading.Thread(target=serve)
        backend_t.start()

        return backend_t

    backend_t = run_server()

    def static():
        global backend_t
        for line in run('pub serve --port=4444'):
            if line.strip() and line.strip()!='\n': print(line)
            if "Build completed successfully" in line:
                killers['dart bin/server'] = True
                #backend_t.stop()
                backend_t = run_server()


    static_t = threading.Thread(target=static)
    static_t.start()
    static_t.join()

After running this script plase wait while frontend server resolves all transformers.

As a result you have a development enviroment that provides hot reloaing and convenient to develop frontend.


Calling Javascript from Dart

Ok. When you have a server and can put some code you realize that you don’t want to rewrite every tool in Dart. Obviously javascript has bigger variety of tools that you want to reuse. Good news is that there wont be any problem to call javascript functions from Dart. It is harder to call Dart functions from javascript, but you wont need it at all.

Standard libraty “dart:js” provides javascript context. For example I will use humane.js to show a notification.

Example:

import 'dart:js';

final humane = new Humane();

class Humane {

    void log(String message){
        context['humane'].callMethod('log', [message]);
    }

}

main(){
    //somewhere in the client code
    humane.log("Oh my Glob! I can see it!");
}

Let’s add a ‘.bowerrc’ file to manage javascript libraries with bower.

{
  "directory": "./web/javascript",
  "registry": "http://bower.herokuapp.com",
  "strict-ssl": false
}

Do `bower install <js-lib>` and then add imports in 'index.html'.

Angular2 Dart: Auth

I killed a decent weekend day reading Angular2 documentation to find out that for most cases you can use examples for TypeScript, for everythig else there is dartdocs. Most of my time I spent figuring out authetication. The thing is that the angular2 api is still subject to change (2016), so I found a lot of deprecated docs. So currently (2016) to restrict access to some views you have to use an annotation called ‘CanActivate’. ‘CanActivate’ takes a function that suppose to return a boolean which determins if the component to be rendered.

AuthHook.dart

import 'dart:html';
import "package:angular2/core.dart";

AuthHook(Instruction instruction, param){
    var token = window.localStorage['auth-token'];
    if(token is String){
        return true;
    }
    else{
        window.location.hash = '/login';
        return false;
    }

}

component.dart

@Component(
  selector:"dash",
  templateUrl:'dashboard.html'
)
@CanActivate(AuthHook)
class DashboardComponent{

}

Redstone.dart: Authenticate user with JWT

Redstone has all needed authentication methods. When I found JWT plugin for Redstone it was only 5 days old but had tests and a decent example on board.

I made pubspec to resolve depedency from git repository:

   redstone_jwt_plugin:
        git: https://github.com/dartsome/redstone_jwt_plugin

So bassically this lib provides tools to issue and verify JWT:

  @app.Route("/login", methods: const [app.POST])
  Future<Map<String, dynamic>> login(@app.Body(app.JSON) Map body) async {
    String username = body["username"];
    String password = hashPassword(body["password"]);
    User user; // Find that user
    return {"token": issueToken("issuer", username, audience: user.rights)};
  }

Redstone.dart: Database

Like most great languages Dart has database drivers and ORMs. You can find ORM or write your own. Or you could just use MongoDB and something to validate your data.

Redstone has poweful tool to work with shemes called ‘redstone_mapper’. What it does is provides instruments for creating, validating and encoding your data from any datastorage. There are redstone mapper extentions for Postgress and MongoDB. I used redstone mapper mongo. It looks very similar to javascript ORMs except it makes use of annotations.

####Example main.dart

import 'package:redstone/server.dart' as app;
import 'package:redstone_mapper/plugin.dart';
import 'package:redstone_mapper_mongo/manager.dart';

main() {
  var dbManager = new MongoDbManager("mongodb://localhost/dbname", poolSize: 3);
  app.addPlugin(getMapperPlugin(dbManager));
  app.setupConsoleLog();
  app.start();

}

users.dart

import 'dart:async';
import 'package:redstone/server.dart' as app;
import 'package:redstone_mapper/mapper.dart';
import 'package:redstone_mapper/plugin.dart';
import 'package:redstone_mapper_mongo/manager.dart';
import 'package:redstone_mapper_mongo/metadata.dart';

class User {

  @Id()
  String id;

  @Field()
  String username;

  @Field()
  String password;
}


@app.Route("/services/users/list")
@Encode()
Future<List<User>> listUsers() =>
    mongoDb.find("users", User);

@app.Route("/services/users/add", methods: const[app.POST])
Future addUser(@Decode() User user) =>
    mongoDb.insert("users", user);


Web Sockets

Redstone.dart uses a Shelf websocket handler to create websockets. It is really as easy as on example:

import 'package:redstone/server.dart' as app;
import 'package:redstone_web_socket/redstone_web_socket.dart';

void main() {
  //install web socket handlers
  app.addPlugin(getWebSocketPlugin());
  app.setupConsoleLog();
  app.start();
}

@WebSocketHandler("/ws")
onConnection(websocket) {
  websocket.listen((message) {
    websocket.add("echo $message");
  });
}

Send Emails

There is Mailer which provides all needed tools to compose and send emails.

Example from official readme

import 'package:mailer/mailer.dart';

main() {
  var options = new GmailSmtpOptions()
    ..username = 'your gmail username'
    ..password = 'your gmail password';

  var emailTransport = new SmtpTransport(options);

  var envelope = new Envelope()
    ..from = 'foo@bar.com'
    ..recipients.add('someone@somewhere.com')
    ..bccRecipients.add('hidden@recipient.com')
    ..subject = 'Testing the Dart Mailer library 語'
    ..attachments.add(new Attachment(file: new File('path/to/file')))
    ..text = 'This is a cool email message. Whats up? 語'
    ..html = '<h1>Test</h1><p>Hey!</p>';

  emailTransport.send(envelope)
    .then((envelope) => print('Email sent!'))
    .catchError((e) => print('Error occurred: $e'));
}

Deployment

Well if you deployed anything with NginX it won’t be quite different. Here are some useful links:
1. Restone Docs 2. Grinder 3. Pubs 4. Heroku Build Pack 5. NginX Dart Config Example

To be continued…


Thank you and be back!