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.
- 832