Beyond single Page apps

An Introduction to iron:router

 

 

Meteor Meetup,

Hamburg, Dec 2014.

@steffoweber

Problem, iron:router solves
Client Side routing

Server side routing
anatomy of iron:router

Overview

Client Side

  • One URL to serve all content
  • Client side HTML generation
  • DOM manipulation in browser

SERVER Side

  • One URL to serve content
  • Send all *.html, *.css, *.js files

Possible issues

  • Browser Back/Forward navigation
  • Bookmarks (sshht – don't mention '#')
  • Search engines
  • Code|Project Complexity

routing (Client side)

iron:router

  • THE (de facto) routing package for Meteor
  • Not part of the core packages
  • Client-side & server-side routing

Demo (1)

gott-2:Meteor steffo$ meteor create rdemo

rdemo: created.                               

 

To run your new app:                          

   cd rdemo

   meteor

gott-2:Meteor steffo$ cd rdemo/               

gott-2:rdemo steffo$ meteor add iron:router

  added iron:location at version 1.0.3        

  added iron:dynamic-template at version 1.0.3

  added iron:router at version 1.0.3          

  added iron:layout at version 1.0.3          

  added iron:middleware-stack at version 1.0.3

  added iron:url at version 1.0.3             

  added iron:controller at version 1.0.3      

  added iron:core at version 1.0.3            

                                              

iron:router: Routing specifically designed for Meteor

gott-2:rdemo steffo$ meteor                   

warning: still trying to confirm logout with www.meteor.com

[[[[[ ~/CodeWarrior/Meteor/rdemo ]]]]]        

 

=> Started proxy.                             

=> Started MongoDB.                           

=> Started your app.                          

 

=> App running at: http://localhost:3000/

Create Meteor app

Add router package

Start Meteor server

Demo (2)

Demo (3)

  • Configuration objects must be accessible from both, client AND server
  • Objects don't need to be in the same file

A very simple route

In 'ironRoutes.js'

Router.route('signup', {
        path: '/signup'
  });

In 'rdemo.html'

<template name="signup">
    <h1>Signup</h1>
</template>

caveat

provide a catchall

Router.configure({
         notFoundTemplate: "404"
});

Hooks

  • If user is logged on,
    • display user's profile page
    • set navbar icon to "profile page"
  • Else,
    • redirect to logon page.
Router.route('ShowProfile', {
      path: '/ShowProfile',
      onBeforeAction: function () {
          if (!Meteor.user()) {
              this.redirect('/');
          }
      },
      onAfterAction: function () {
          hightlightNavbarItem('#ShowProfileNavBtn');

      }
  });

Data context (1)

<host:port>/read/{Music|Culture|Politcs}

Route

with only one routing rule

Data Context (2)

Text

Router.route('read', {
    path: '/read/:section',
    data: function () {
        var acceptableSections = ["Music", "Politics", "Culture"],
            section = this.params.section;
        if (_.contains(acceptableSections, section)) 
           return {section: section}
        else this.render('404');
    }
});​
<template name="read">
 <h1>The Meteor Post Journal</h1>
    <h2>{{section}}</h2>
</template>​

other Cool client stuff

 
  • Wait until subscription data is available
    • waitOn: function() {

          return MyCollection.subscribe('myCollection');

        }

  • Data inside routing function is reactive (e.g. Meteor.user() or Roles).

server side routing

building a REST API with meteor

Server routes

  • A client sends an HTTP request to the server
    • Client: Browser, Java, .Net, cURL
    • Server: Meteor server
    • Request: text/plain, application/json, image/jpeg
  • Server replies with a document
    • text/plain
    • application/json

API sample

Router.route('api/:func/:param', {
    where: 'server'
})
    .get(function () {
        var f = this.params.func;
        var p = this.params.param;

        this.response.statusCode = 200;
        this.response.setHeader = 'Content-Type:   application/json';
        var result = Meteor.call(f,p);
        this.response.end(JSON.stringify({"return":result,"status":"OK"}));
    })
Meteor.methods({
  get: function(param) {
        return "Hello " + param;
    }     
})​
$ curl -v http://localhost:3000\
          /api/get/steffo 

asynC code

Router.route('api/:func/:param', {
    where: 'server'
})
    .get(function () {
        var f = this.params.func;
        var p = this.params.param;
        var res = this.response;

        Meteor.call(f, p, function (error, result) {
            if (error) {
                res.statusCode = 500;
                res.setHeader = 'Content-Type: application/json';
                res.end(JSON.stringify(error));
            } else {
                res.statusCode = 200;
                res.setHeader = 'Content-Type: application/json';
                res.end(JSON.stringify({
                    "return": result,
                    "status": "OK"
                }));
            }
        })
    })
Meteor.methods({
  get: function(param) {
        return "Hello " + param;
    }     
})​
$ curl -v http://localhost:3000\
          /api/get/steffo 

Anatomy of iron:router

Pooh – lot's of deps

gott-2:rdemo steffo$ meteor add iron:router
  added iron:location at version 1.0.3        
  added iron:dynamic-template at version 1.0.3
  added iron:router at version 1.0.3          
  added iron:layout at version 1.0.3          
  added iron:middleware-stack at version 1.0.3
  added iron:url at version 1.0.3             
  added iron:controller at version 1.0.3      
  added iron:core at version 1.0.3            
                                              
iron:router: Routing specifically designed for Meteor

Why should i care about these packages?

Example. file upload to 3rd party dropbox

Browser

  • upload local file

Meteor

  • has OAuth token

Dropbox

  • needs OAuth token

uploading 100MB via http.put

Router.route('/api/upload', {
    where: 'server'
})
    .put(function () {
        console.log("Request: " +      
JSON.stringify(this.request.body));
      }

 

 curl -v -X PUT -H "content-type: image/jpeg" \  --data-binary "@hundertmb.jpeg"\  http://localhost:3000/api/upload/

Output. 

input. 

compute. 

=> Meteor server restarted                    

I20141203-15:04:12.589(1)? Request: undefined

chunked upload

> PUT /api/upload/ HTTP/1.1
> User-Agent: curl/7.37.1
> Host: localhost:3000
> Accept: */*
> content-type: image/jpeg
> Content-Length: 87388
> Expect: 100-continue
> 
< HTTP/1.1 100 Continue
< HTTP/1.1 200 OK

Curl -verbose 

data & end event handler

Router.route('/api/upload', {
    where: 'server'
})
    .put(function () {
        var buffers = [];
        var rlen = 0;
        var resp = this.response;
        // check here to learn about event processing: http://nodejs.org/api/events.html

        this.request.on('data', function (chunk) {
            console.log("***");
            buffers.push(chunk);
            rlen = rlen + chunk.length;
            console.log("Request length: " + rlen);
        })

        this.request.on('end', function () {
            var data = Buffer.concat(buffers);

            console.log("--> : " + data.length);
            var httpreq = Meteor.npmRequire('httpreq');
            httpreq.put('https://api-content.dropbox.com/1/files_put/auto/zcfg/test.jpg', {
                    body: data,
                    headers: {
                        'Authorization': "Bearer your-dropbox-aouth-bearer-atoken"
                    }
                },
            );
        });
    })

Summary

  • extremely useful package
  • things I didn't cover
    • middleware tweaks (like max file size, bodyparsers)
    • best practice for storing ironRoutes.js
    • controllers (you can write your own)
    • wildcards | regex in URLs
    • templates and yields
  • things I like to learn about
Made with Slides.com