Meteor is a name I’ve seen around for a while, often surrounded by talk of how different and innovative it is. I’ve been meaning to have a proper play with it since I first heard about it in a write up of the Throne of JS conference from 2012. With version 1.0 being released in October last year, the first version with an official Windows installer (1.1) following in March, and the announcement this month that version 1.2 will (almost) fully support ECMAScript 6 out of the box, I decided that I was out of excuses and needed to install it and have a look. Whether or not it lives up to some of the hype floating around the web is probably too subjective a question to answer, but it was certainly a really interesting and different experience to write an app with Meteor.
I created a simple single page stock manager app because that’s the sort of rock ‘n’ roll lifestyle I lead. You can see all the code at GitHub.
So What Is It?
If you have the time, I recommend working through the getting started tutorial to get a decent idea of what Meteor is about.
To create Meteor Stocks, I’ll use the create command:
meteor create meteor-stocks
This gives us a basic project template as well as a load of standard packages and all the important gubbins to be able to run the app at
cd meteor-stocks meteor
One thing lacking from the main tutorial is any discussion of how to organise your code to keep it neat and maintainable. However, there is a good section in the docs, which is worth a read, and an even better section on the Unofficial Meteor FAQ. One important take-away is that anything you put in a folder called ‘server’ will only go to the server, anything in a folder called ‘client’ will only go to the client, and everything else will go to both.
Anyway, based on the recommended layouts, I’ll create the following structure:
/.meteor/ /client/ - meteor-stocks.html - meteor-stocks.css - meteor-stocks.js /lib/ /server/
where lib is for any code common to client and server. I won’t actually use meteor-stocks.js so I’ll delete that and create more modular and specialised JS files as I go along.
Out of the box, Meteor contains some functionality to manipulate and query your database directly from the client. This is great for quick prototyping, and for showing off at nerdy parties, but as the tutorial points out, it would be a dangerous thing to keep in production, so I’ll remove these packages early to avoid temptation:
meteor remove insecure meteor remove autopublish
The idea of this app is to let users build up and save their own list of stocks to keep an eye on, and for that to work users will need to be able to set up accounts. Luckily, this is blindingly easy using the method described in the getting started tutorial.
Now that a user can sign-up and log-in, they need to be able to add stocks. So first we need to create a collection. I’ll create lib/collections.js and add:
This creates a new collection on the server backed by an integrated MongoDB database. It tells the client to create a cached version of the server collection so that it can do clever things like predict the outcomes of server calls without having to wait for a response, meaning the UI can respond to input extremely quickly. This collection is also reactive, so changes to the collection will automatically be reflected in templates that use it.
Because I removed the autopublish package, I have to manually set up publishing and subscribing for this collection, so I’ll create server/publish.js:
Then I can create a client function to query the collection via that subscription and make it available in the
<body> section of my view. I’ll create client/body.js and add:
and then in meteor-stocks.html within my
This uses the Spacebars templating engine and means that each stock returned by the stocks function will have its contents output to a template called ‘stock’. So I’ll add that template by creating client/stock.html:
That’s great but we don’t have any entries in the database yet, so there’s not much to show. Let’s create a way to add stocks in the UI. In meteor-stocks.html:
and in body.js:
If I hadn’t removed the insecure package earlier I could have inserted a record straight into the database from this event listener, but because I’m trying to be responsible, there’s one more step.
Notice the line
Meteor.call("addStock", input.value) - that’s calling a Meteor method,
addStock(). Both client and server can respond to meteor methods: if there’s one common method, it will be run on the server while the client will attempt to predict the outcome so that any changes can be reflected on the UI immediately. Any discrepancies get sorted out once the server responds. Alternatively, you can specify what you want each to do by writing separate methods with the same name in client and server-specific files.
I’ll start off doing it the first way. So I’ll create lib/methods.js and add:
The call to
Stocks.insert() will be run on both the actual stocks collection in the database and the client’s cached version so adding a new entry should be reflected instantly on the UI. If you open up two browsers side by side and add a stock, the new entry should appear on both screens quickly, but slightly more quickly on the screen where you actually added it.
This is all very nice, but we’re not really adding stocks, we’re just adding whatever someone wants to type into the input. I’ll use Yahoo Finance to provide stock information because it’s free and has a simple API. I could write some code to handle calls to the API but a quick Google shows it already exists. Meteor and Atmosphere are fairly mature, despite still being on quite low version numbers, and there seems to be a good vibrant community and a lot of existing packages. That’s good because I’m allergic to doing any more work than I have to. So I’ll add the Yahoo Finance package:
meteor add ajbarry:yahoo-finance
The description shows that this is a server-only interface, that means it can only be called from server-based code. First I’ll create server/methods.js and add:
This method will take a financial symbol and return its name if it exists or the string “N/A” if it doesn’t. So I’ll update my
addStock method to only add stocks that are actually stocks:
This means we’ve lost the benefit of the client-side collection stubbing I described earlier because now we’re only doing the insert on the server. To get it back, we could add something like:
Now the stock would be added to the cached collection, and therefore the list in the UI, before the response from the server telling us whether it exists. In some cases that would be a good route to take, but here it probably isn’t. There are a lot more strings that aren’t financial symbols than ones that are, so it wouldn’t be a good idea to predict that the insert will succeed. If we entered a random string, it would flash up in the list and then disappear again when the server comes back to tell us it doesn’t exist.
To add a bit more functionality to the stock list, I’ll let the user delete stocks and make favourites. I already added the necessary html in the stock template, so I’ll add client/stock.js:
and to the Meteor.methods object in lib/methods.js I’ll add:
I’ll also add a count of how many users are watching each stock so you can see if you’re watching popular stocks or if you’re out there on the wild west of the financial universe. In stock.js:
Now if you get all your friends and family to sign up and add stocks too (or create some new users in another browser window; whichever takes less awkward conversation) you should see the count numbers go up and down to reflect what other users are doing.
Now that we’ve got a list that we can add to, we want to be able to select from that list and view some actual data. To do this I’ll set up a reactive variable that I can update with the details of the selected stock so that anything that depends on it will automatically update whenever those details change. For this I’ll add the reactive-vars package:
meteor add reactive-vars
and then add one in my collections.js file:
Now I’ll add a method to server/methods.js to get all the data for a given stock for the last year:
and then add another listener to the events object in stock.js:
This listens for clicks on any of the stock listing
<li> elements and calls my server-side
getData() method then updates the
SelectedStock reactive var with an object literal containing the results. All that’s left to do is create a template to show the contents of
SelectedStock. So in meteor-stocks.html I’ll add:
and then I’ll create client/stockDetail.html:
Now clicking a stock in the list will give us more information: the stock’s name and its most recent close price.
It would be nice to show a bit more than that, so I’ll use Highstock to output the data in a chart. Another quick Google shows there’s a handy Meteor wrapper around Highstock already so I’ll add that:
meteor add jhuenges:highstock
I’ll stick a div for my chart in stockDetail.html:
and then create client/stockDetail.js to deal with the chart set-up:
Tracker.autorun() method automatically tracks any reactive data sources that it detects as dependencies. That means that because
SelectedStock.get(), it will be run any time
SelectedStock changes, and the chart will be recreated with new data.
Meteor also gives easy access to session variables, so I’ll try that out by giving the user an option to customise what they see. I added the ability to set stocks as favourites earlier, so now I’ll let users filter their stock list to show only their favourites.
First I’ll add something to click, so in meteor-stocks.html:
and a corresponding event listener in the events object in body.js:
Session.set() takes a string as a key and then whatever arbitrary value you want to set it to; in this case a boolean. Like the collections and the
Session is reactive, so templates and other objects can respond to changes automatically. Now I’ll just update the
stocks function in my body helpers to reflect this filter state:
Now we can update the filter session variable by clicking in the UI and immediately see that change reflected in the stock list.
In the finished app, I used the same method to add a button to toggle the chart between a line series and an OHLC chart.
Meteor seems like a fresh way to work. It makes a lot of things, like CRUD operations and keeping your UI in line with data changes, extremely easy and it adds a lot of nice, clever tricks, like the client-side stubbing of server-side methods.
There are a few things that struck me as awkward, like the reliance on passing strings to call Meteor Methods, which makes me worry that I’m always only one ham-fisted typo away from a nasty and confusing bug that an IDE won’t help me with. I’m also a little uneasy about how global everything feels: there’s no need to import specific modules, as you would with Require, everything’s just sort of there. That makes development very quick and easy, but I wonder what it would mean for maintainability on a larger project.
Still it’s very impressive how much you can achieve with relatively few lines of code and Meteor gluing everything together behind the scenes, so I think this is definitely a framework to try.