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
Beyond Single Page Apps
By Steffo Weber
Beyond Single Page Apps
A short introduction to Meteor's iron:router package.
- 869