New Meteor app - lessons learned

I work for a small nonprofit and just finished a new Meteor app: the Seasonal Food Guide, a (free) website that tells you what fruits and vegetables are currently in season in near you (in the U.S.). Plus an iPhone and Android app via Cordova.

After 9 months of work I wanted to share a few lessons learned with you all. This post is a little long but hopefully new Meteor devs will find this helpful.

Coolest things about Meteor

1. Speed

It’s called Meteor for a reason, and wow is it fast. On my 100-300 MB/s connection our 1.9 MB homepage loads completely in 1-2 seconds. As a single-page application the transitions to other URLs is frictionless. Makes me wish I could build all my future projects in Node.js.

2. Vertical integration

I work at a small nonprofit (<20 people) and if I get hit by a bus, I don’t want to handoff a spaghetti stack with 15 different technologies to learn. I think it gives my co. comfort to know they could, worst-case, pay MDG for help if I disappeared off the face of the Earth.

When I started I was handed a website that stitched together Craft CMS + a Laravel App + Solr + Google Maps and other things. Just getting a local instance took a week. Honestly the marquee DDP “real-time” subscription update feature was less important to me than just having an end-to-end solution.

3. Cordova integration

There’s no way my small company could afford outside vendors to build and maintain native mobile apps, it just wouldn’t happen. But to have one codebase to rule all web, iOS, and Android? Awesome.

Be warned: setup of iOS and Android dev environments is an exercise in suffering, especially considering Meteor is incompatible with the latest version of Android dev tools. There’s got to be an easier way.

Also, this Meteor-Cordova icon bug in Meteor is real, and real annoying. Like the solution in that thread, I had to write my own script to solve this. Make sure to check your icons before submitting to Apple.


For now, I manually run tests, commit, build, upload to a Digital Ocean droplet, unzip, install, then use forever to start a new instance, then stop the old instance. That way I can deploy with no downtime. The plan is to automate this with a bash script, then explore more advanced solutions.

Also using Nginx as a reverse proxy, and loading static assets directly from Nginx, because it’s significantly faster.

If anyone has better deployment strategies, I’m all ears, but the Digital Ocean option not only was the cheapest it gives us the most control. I’d like to explore MDG’s Galaxy hosting, but honestly the setup and billing process are not super corporate-friendly, IMHO (e.g. multiple logins, inability to let finance set billing via PO, etc)

Biggest Mistakes I Made

1. Placing routing logic inside main jsx file instead of React Router
We have ~4,000 unique pages and I wanted to each have its own unique URL, but obviously I did not want to hardcode 4,000 webpages. I needed dynamic routing.

But I built all my routing logic inside my main.jsx file instead of React Router where it belongs, mainly because that’s where I hashed out all my content logic, and once I had it all built out it was too complex and too late to start over or rip out.

Even though this is a React Router issue it’d be great to see better out-of-the-box Meteor support for dynamic URLs.

2. Not game-planning iOS-specific features from the beginning

Apple rejected our first app submission because it was a “web app” and had no iOS-specific feature, basically a port of the mobile website. We added some native iOS functionality and got approved eventually but early in your Cordova development process ask yourself What iOS-only feature are we including in this app?

Also, getting a D-U-N-S number to enroll as a corporation in the Apple Developer program can take up to a month, so factor that into your schedules.

3. Writing tests at the end instead of at the start

While it’s common developer wisdom that tests should be written simultaneously (or even before) code is written, learning Meteor, React, Cordova for the first time while brushing up on Mongo and ES2015 was just a lot to take in on top of worrying about tests.

I have them in now but they’re probably not as good as they should be, it’s always hard to game out edge cases ex post facto.

Random Things Worth Knowing

1. New Relic does not support Meteor apps

At least not yet. We’re still investigating tools for monitoring, at the moment we’re just using Digital Ocean alerts.

2. If you use an EV Cert, CloudFlare will cost $200/month instead of $20/month

Using an EV Cert seemed like a good idea at the time, but CloudFlare has a 10x jump in cost if you want to use an EV Cert that you already purchased.

3. Google indexed the site fine without pre-rendering

I didn’t sign up for or any other service, Google accepted my sitemap and the React Helmet data fine (although it’d be nice if Meteor had a built-in tool to generate XML sitemaps).


That’s about it. If anyone has any suggestions on how I can improve the website or the mobile apps, I’m all ears. Thanks.


Congrats for the well-done app. And thanks for the hint about the icons, good to know.

Yeah, but you shouldn’t forget the other crawlers like Bing, Facebook or WA. They need a prerendered page, otherwise they will only see an empty page.

Just my two cents: 2 seconds on a 100-300 MB/s connection is really slow in 2017

Congrats on the app :).

Sorry, I should have been more descriptive.

It starts rendering the page at 45ms – the user sees content right away.

But no amount of hocus pocus will make large high-res images render faster than 1-2 seconds, at least none that I’m aware of.

I’m sure there are more optimizations I could make, though.

Good to know. We might add it in a few weeks, see how it goes.

Did you try pm2-meteor to deploy and host/manage your app? Didn’t find anything simpler. Plus you get a free web interface to monitor all the things on keymetrics.

Thanks for your feedback!

Use something like Chimp or Nightwatch to create and run functional UI tests against your app.

Then use a continuous integration service like CircleCI to setup a couple automated jobs that happen on code changes. These services integrate with things like GitHub so that when you merge a pull request or push a commit to a branch, it will automatically start running your automated jobs with that new code. You should have at least two jobs:

  1. build + test job which runs unit tests and functional UI tests
  2. a deploy job which will deploy your app, probably with either meteor-up or Galaxy

Ideally the sequence goes like this:

  1. you push a commit to master or merge a pull request
  2. CircleCI gets triggered to build your code, run unit tests, and run functional UI tests
  3. if all tests pass, Circle runs your deploy job and deploys the new code to a server
  4. if any tests fail, you get an email or notification with a link to the job so you can see the logs and fix what broke

So whenever you push a code change, if your tests all pass, it just ends up being deployed and you didn’t have to do anything at all.

I’ve posted an example of a CircleCI config I’m using for a build+test job to run unit tests and UI tests against a local meteor app.

I’ll check it out. Thanks!

This is super helpful, I’ll give it a try. Thanks!

1 Like