Deploying Ember CLI Apps to Parse Cloud Code
2 September 2015
I’m a big fan of Parse: their backend-as-a-service works great for the type of apps I build, which generally have little to no “server” and communicate primarily through REST endpoints.
Just the other day, I discovered that their back-end can host static files, meaning I could move my deployed Ember apps away from services like S3 or Heroku, thus simplifying (and cheapening) my deployments. Getting an Ember CLI app to run on Parse was a little less than frictionless, though, and, in this post, we’ll look at how to manage it.
To get started, you’ll need:
- A Parse account (there’s a free option) and Account Key,
- A Parse application, and
- An Ember CLI app.
But First, a Caveat
To be fair, deploying static files to Parse (such as those generated by Ember CLI) is quite simple. The Parse team provides a command line tool to deploy your code and assets, and it works well. What makes deploying Ember apps tricky has to do with Ember’s URLs.
By default, Ember CLI apps use a browser’s history API, which gives you URLs like your-app.com/photos/1
. Here’s the issue: if a user navigates directly to that URL, most web servers will look for either static files or a defined route to serve, neither of which we’ll have after deploying this app.
$ curl -s -w "%{http_code}" http://your-app.com/photos/1 -o /dev/null |
To fix this, we can tell Ember CLI to use “hash”-based URLs instead (like your-app.com/#/photos/1
), which we’ll see how to do momentarily. This tells the server to load the root document, and Ember can then step in to figure out what route you’re looking at based on what’s past the “hash” symbol.
Now that’s out of the way, let’s get started deploying our Ember CLI app to Parse.
The Easy Way
Update Ember CLI’s location type. We now know that history-based URLs will break when Parse serves the app from static files, so let’s change our app to use hashes instead: in config/environment.js
, simply change the locationType
property from 'auto'
to 'hash'
.
Get your project Parse-ready. The fastest way to do this is to create a new Parse app using their command line tool. From there, you can either copy the files you need into an existing project, or create a new Ember CLI project inline. Using the Parse CLI, run parse generate
and answer its questions. You’ll see a few files in there that you’ll need in your Ember app:
- The
/cloud
folder contains any custom server code your Parse app needs. The Parse CLI generates some default code inmain.js
…you can take it or leave it, but you’ll need thecloud
directory either way. - The
/public
folder is where your compiled Ember app will belong. You don’t need to copy this over; we’ll make our own. .parse.local
contains some properties that tell Parse to which app this code will belong. Warning: since this file contains your application’s unique key, make sure you don’t check it in to public source control. Theember-cli-dotenv
library can help with this..parse.project
tells Parse what version of its SDK to use. This one’s safe to stick on GitHub. :)
For more details, check out the Parse documentation on the subject.
Deploy! This step requires a bit more prep. What we need is to compile the Ember app into a public
directory, then copy the Parse files from the previous step, then tell the Parse CLI to perform the deploy. I use a bash script for this, but be warned: I’m not a bash expert by any means, so feedback is definitely appreciated.
# Compile the Ember app. |
Just run the bash script with sh your-file-name.sh
, or maybe set it as an npm package script.
The Hard(-er) Way
At this point, you should have a fully-operational Ember app running on Parse…but we can do better. Let’s change our deploy process so we can use those history-based URLs.
Reset things. To start, change your app’s locationType
parameter (in config/environment.js
) back to 'auto'
. No use in sticking with hash-based URLs anymore!
Make a mini node app. We’ll need to tell Parse to always serve up the root document (located at public/index.html
) to any and all requests so that Ember can take over routing. Fortunately, this is easily accomplished by giving Parse a simple node app that it will run alongside serving static files. The following code is from Erik Hanchett‘s great blog post on serving Ember apps from Express:
var express = require('express'); |
Parse starts by looking for files in the public
directory before it will run this simple server, so we won’t need to worry about not handling routes for static assets like CSS and JavaScript. From the Parse documentation:
When a request goes to a URL of your subdomain, Parse will first look for a matching file in the
public
directory. If there is no match, then Parse will invoke any Express request handlers that you have registered in Cloud Code.
To tell Parse about this node server, have your app’s main.js
file import it with require('cloud/app.js');
on the first line.
Send down the root file. Unfortunately, Parse’s node implementation lacks certain features (like Express’s Response
object’s sendFile()
function) that would make sending down a static file trivial. Instead, I’m using a bit of a hack: in the deploy script, make the root file an EJS template, then tell the node app to render it (something the Parse node binary can do):
var express = require('express'); |
Here’s the new part of the bash script that does that:
# Create the 'views' directory. |
Deploy! Again! With the build script modified, we’re ready to deploy again! This time, our script will also copy the index.html
file into the cloud/views
folder so our mini-node app can serve it. All that’s left is to visit your Parse app and see if everything works correctly. If not…well, Parse has some great logging tools!
Even with the workaround for Ember’s history-based URLs, I’m thrilled with how easy it is to deploy an Ember CLI app to Parse. If you’re already a Parse user, or have been looking for an excuse to give it a try, why not try your hand at deploying an Ember app? If you run into any issues, or have suggestions for ways to streamline this process, share them in the comments!