FastBoot brings server-side rendering to Ember.js applications.
By performing initial renders on the server, your Ember app is accessible to search engines, curl
, and other scrapers.
Users see content faster, and can view your app even if they have JavaScript disabled or the JavaScript files fail to load.
FastBoot works by creating a Node.js process and executing your Ember application within it.
Most Ember applications should work out of the box. However, there are some patterns that you should be sure to follow to guarantee that your application is fully FastBoot compatible. See Tips and Tricks below for a full list.
FastBoot is an addon you can add to existing Ember CLI apps. To FastBoot-enable your application, run:
ember install ember-cli-fastboot
This will install the ember-cli-fastboot
addon via npm and save it to your application's package.json
.
You can start a FastBoot server on your development machine by running:
ember serve
This starts a FastBoot server listening on port 4200.
You can verify it's working by curling from localhost
:
curl 'http://localhost:4200/' -H 'Accept: text/html'
You should see the content of your application's index template rendered in the output, instead of the empty HTML most client-side rendered apps show.
To stop the server, press Ctrl-C
on your keyboard to kill the process.
You can run the development server on a different port by passing the --port
argument or use any of ember-cli
options:
ember serve --port 4567
You can also turn off the server side rendering on a per request basis using fastboot
query parameter. To disable FastBoot rendered content, visit localhost:4200/?fastboot=false. You can enable FastBoot rendered content again by visiting localhost:4200/?fastboot=true.
Once you've installed the FastBoot addon, a Node-compatible build of your app is automatically included when you run ember build
.
As with any other Ember build, you'll want to build for the production environment when deploying for your users to use:
ember build --environment production
You are strongly encouraged to automate deploys using [Ember CLI Deploy][ember-cli-deploy]. There are a number of FastBoot-compatible deploy plugins, with more being authored every day.
Manual deployment is slow and error-prone, and even if you have a custom deployment process, writing an Ember CLI Deploy plugin is straightforward and will save you time and energy in the future.
As with a standard Ember build for the browser, compiled assets by default go in your application's dist
directory.
FastBoot adds two additions:
dist/package.json
, which contains metadata about your app for consumption by the FastBoot server.dist/assets/
, which contains your app's compiled JavaScript that is evaluated and run by the FastBoot server (vendor-d364124232469f73db00693819bb2575.js
, app.js
, app-fastboot.js
).For more information, see the Architecture section.
FastBoot registers the fastboot
service which you can inject into your application:
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class Application extends Route {
@service fastboot;
get isFastBoot() {
return this.fastboot.isFastBoot;
}
// ... Application code
}
This service contains several useful objects and methods you can use to manage an application's state when it is being FastBooted.
Property | Description |
---|---|
isFastBoot |
Equals true if your application is running in FastBoot |
response |
The FastBoot server's response |
request |
The request sent to the FastBoot server |
shoebox |
A key/value store for passing data acquired server-side to the client |
deferRendering |
A function that takes a Promise that you can use to defer sending the response to allow asynchronous rerendering |
By default, FastBoot waits for the beforeModel
, model
, and afterModel
hooks to resolve before sending a response back to the client.
You can use these hooks to defer the initial rendering of your application.
See Use Model Hooks to Defer Rendering.
If you have asynchronous code that runs outside of these lifecycle hooks, you will want to use deferRendering
to block the response. deferRendering
function accepts a Promise
and will chain all promises passed to it.
FastBoot will wait for these promises to resolve, allowing the app to rerender itself based on new data coming in asynchronously before sending the response to the client.
You must call deferRendering
before these model hooks complete.
For example, if you made an asynchronous call in a Component, you would use deferRendering
in the init
lifecycle hook.
The fastboot.request
key allows you access to the request sent to the FastBoot server.
You can access the current request headers via fastboot.request
.
The headers
object implements part of the Fetch API's Headers class, the functions available are has
, get
, and getAll
.
For more information about HTTP headers see here.
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class Application extends Route {
@service fastboot;
model() {
let headers = this.fastboot.request?.headers;
let xRequestHeader = headers.get('X-Request');
// ...
}
}
You can access cookies for the current request via fastboot.request
in the fastboot
service.
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class Application extends Route {
@service fastboot;
model() {
let authToken = this.fastboot.request?.cookies.auth;
// ...
}
}
The FastBoot service's cookies
property is an object containing the request's cookies as key/value pairs.
You can access the host of the request that the current FastBoot server is responding to via fastboot.request
in the fastboot
service.
The host
property will return the full hostname
and port
(example.com
or localhost:3000
).
For example, when requesting http://myapp.example.com/photos
from your browser, fastboot.request.host
would equal myapp.example.com
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class Application extends Route {
@service fastboot;
model() {
let host = this.fastboot.request?.host;
// ...
}
}
Retrieving host
will error on 2 conditions:
hostWhitelist
defined.Host
header does not match an entry in your hostWhitelist
.For security, you must specify a hostWhitelist
of expected hosts in your application's config/environment.js
:
module.exports = function(environment) {
var ENV = {
modulePrefix: 'host',
environment: environment,
baseURL: '/',
locationType: 'auto',
EmberENV: {
// ...
},
APP: {
// ...
},
fastboot: {
hostWhitelist: ['example.com', 'subdomain.example.com', /^localhost:\d+$/]
}
};
// ...
};
hostWhitelist
entries can be a String
or RegExp
to match multiple hosts.
Be careful with RegExp
entries because host names are checked against the Host
HTTP header, which can be forged.
An improperly constructed RegExp
could open your FastBoot servers and any backend they use to malicious requests.
You can access query parameters for the current request via fastboot.request
in the fastboot
service.
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class Application extends Route {
@service fastboot;
model() {
let authToken = this.fastboot.request?.queryParams.auth;
// ...
}
}
The service's queryParams
property is an object containing the request's query parameters as key/value pairs.
You can access the path (/
or /some-path
) of the request that the current FastBoot server is responding to via fastboot.request
in the fastboot
service.
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class Application extends Route {
@service fastboot;
model() {
let path = this.fastboot.request.path;
// ...
}
}
You can access the protocol (http:
or https:
) of the request that the current FastBoot server is responding to via fastboot.request
in the fastboot
service.
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class Application extends Route {
@service fastboot;
model() {
let protocol = this.fastboot.request.protocol;
// ...
}
}
You can also access the method of the request via fastboot.request
in the fastboot
service.
The method
property will return the method name (GET
, POST
, PATCH
...) of the request.
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class Application extends Route {
@service fastboot;
model() {
let method = this.fastboot.request.method;
// ...
}
}
On POST
, PUT
and PATCH
request you will probably be interested in the body of the request.
You can access the body
of the request via fastboot.request
in the fastboot
service, but
only if you setup a middleware to extract the body of the request in your express server.
You can use body-parser
and fastboot-express-middleware
and to create an in-repo addon that
contains a middleware.
// your-app/lib/fastboot-middleware/index.js
var bodyParser = require('body-parser');
var FastBootExpressMiddleware = require('fastboot-express-middleware');
module.exports = {
name: 'fastboot-middleware',
serverMiddleware(options) {
var app = options.app;
app.use(bodyParser.text()); // or bodyParser.json()
app.use(function(req, resp, next) {
var outputPath = process.env['EMBER_DIST_FOLDER'];
if (req.serveUrl) {
var fastbootMiddleware = FastBootExpressMiddleware({
distPath: outputPath
});
fastbootMiddleware(req, resp, next);
} else {
next();
}
});
}
};
// your-app/lib/fastboot-middleware/package.json
{
"name": "post-middlware",
"keywords": [
"ember-addon"
]
}
Then on the package.json
of your app you can the folder of this in-repo addon to the autodiscovery
paths:
// your-app/package.json
{
// ...
"ember-addon": {
"paths": [
"lib/fastboot-middleware"
]
}
}
Now that this middleware is parsing the body of request, you can access it on the fastboot
service.
Depending on what method on body-parser
you used, the body can be a parse JSON object, a string
or even a raw Buffer.
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class Application extends Route {
@service fastboot;
model() {
let method = this.fastboot.request.body;
// ...
}
}
FastBoot Response gives you access to the response metadata that FastBoot will send back the client.
You can access the current response headers via fastboot.response.headers
.
The headers
object implements part of the Fetch API's Headers class, the functions available are has
, get
, and getAll
.
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class Application extends Route {
@service fastboot;
model() {
let isFastBoot = this.fastboot.isFastBoot;
if (isFastBoot) {
let resHeaders = this.fastboot.response.headers;
resHeaders.set('X-Debug-Response-Type', 'fastboot');
}
// ...
}
}
You can access the status code of the current response via fastboot.response.statusCode
.
This is useful if you want your application to return a non-default (200
) status code to the client.
For example if you want a route of your application to be 401 - Unauthorized
if it accessed without OAuth credentials, you could use statusCode
to do that.
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class Application extends Route {
@service fastboot;
beforeModel() {
let isFastBoot = this.fastboot.isFastBoot;
if (!isFastBoot) {
return;
}
let reqHeaders = this.fastboot.request.headers;
let authHeaders = reqHeaders.Authorization;
if (authHeaders === null) {
this.set('fastboot.response.statusCode', 401);
}
// ...
}
}
FastBoot handles 200
, 204
and 3xx
by default.
For other custom responses you will want to modify your application and FastBoot server implementation to act accordingly.
FastBoot's default behavior is to inject HTML that your application has rendered into the <head>
and <body>
sections of the application's index.html
file.
In some cases you may want to use different HTML boilerplate for your application. For instance, AMP pages have a rigid structure
that they must adhere to, so if you are using Ember to generate a valid AMP page you will need to change the index.html
file that FastBoot uses.
To do so, specify an htmlFile
in your application's config/environment.js
:
module.exports = function(environment) {
var ENV = {
// ...
fastboot: {
htmlFile: 'custom-index.html'
}
};
};
FastBoot will look for this file in your dist/
directory when serving your application.
You can put your custom html file in your application's public/
directory and ember-cli
will
copy it to the correct location in the dist/
directory when building. This should be a standard HTML file
(no handlebars) with comment placeholders <!-- EMBER_CLI_FASTBOOT_HEAD -->
and
<!-- EMBER_CLI_FASTBOOT_BODY -->
that FastBoot will replace with your application's <head>
and <body>
content, respectively.
The Shoebox lets you pass application state from your FastBoot rendered application to the browser for client-side rendering. For example if your FastBoot server makes an API request, you can use the Shoebox to pass data to the client's browser. When the application resumes rendering on the client-side, it will be able to use that data, eliminating the need for it to make an API request of its own.
The contents of the Shoebox are written to the HTML as strings within <script>
tags by the server rendered application, which are then consumed by the browser rendered application.
This looks like:
.
.
<script type="fastboot/shoebox" id="shoebox-main-store">
{"data":[{"attributes":{"name":"AEC Professionals"},"id":106,"type":"audience"},
{"attributes":{"name":"Components"},"id":111,"type":"audience"},
{"attributes":{"name":"Emerging Professionals"},"id":116,"type":"audience"},
{"attributes":{"name":"Independent Voters"},"id":2801,"type":"audience"},
{"attributes":{"name":"Staff"},"id":141,"type":"audience"},
{"attributes":{"name":"Students"},"id":146,"type":"audience"}]}
</script>
.
.
shoebox.put
lets you add items to the Shoebox.
shoebox.retrieve
lets you remove items from the Shoebox.
In the example below, we find and store our data in a shoeboxStore
object, when the application is rendered in FastBoot.
When the same code is then executed by the client browser, we retrieve the items from the shoeboxStore
rather than redoing the find (and triggering a network request).
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class Application extends Route {
@service fastboot;
@service store;
model(params) {
let shoebox = fastboot.shoebox;
let shoeboxStore = shoebox.retrieve('my-store');
let isFastBoot = fastboot.isFastBoot;
if (isFastBoot) {
return this.store.findRecord('post', params.post_id).then(post => {
if (!shoeboxStore) {
shoeboxStore = {};
shoebox.put('my-store', shoeboxStore);
}
shoeboxStore[post.id] = post.toJSON();
});
}
return shoeboxStore && shoeboxStore[params.post_id];
}
}
JavaScript running in the browser relies on the XMLHttpRequest
interface to retrieve resources, while Node offers the http
module.
What do we do if we want to write a single app that can fetch data from our API server when running in both environments?
One option is to use the ember-fetch addon, which provides an implementation of [Fetch API][fetch-api] standard that works seamlessly in both environments.
To use ember-fetch
, install it as you would any addon:
ember install ember-fetch
Once installed, you can import it using the JavaScript module syntax, wherever you need to make a network request:
import fetch from 'ember-fetch/ajax';
The fetch()
method returns a promise and is very similar to jQuery's getJSON()
method that you are likely already using.
For example, here's an Ember route that uses fetch()
to access the GitHub JSON API and use it as the route's model:
import Route from '@ember/routing/route';
import fetch from 'ember-fetch/ajax';
export default class Application extends Route {
model() {
return fetch('https://api.github.com/users/tomdale/events')
.then(function(response) {
return response;
});
}
}
For more information, see ember-fetch and the [Fetch documentation on MDN][fetch-api].
To control the title that gets displayed for a route (i.e., the <title>
tag), install the FastBoot-compatible ember-cli-document-title
addon:
ember install ember-cli-document-title
This addon allows you to specify the page title by adding a title
property to your application's routes.
// routes/post.js
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class Application extends Route {
titleToken(model) {
return model.name;
}
}
See ember-cli-document-title for more information.
<head>
TagsFastBoot makes your Ember application accessible to services that embed content, such as Twitter's cards and Facebook posts.
You can customize how your site appears in these contexts with <meta>
tags driven dynamically by your Ember app.
To get started, install the ember-cli-head
addon:
ember install ember-cli-head
The ember-cli-head
addon works by giving you a Handlebars template that is rendered into your application's <head>
tag.
To configure what gets rendered, edit the newly-created app/templates/head.hbs
file.
For example, to set the title used when embedding a route in Facebook (or any other Open Graph-compatible app), add the appropriate <meta>
tag to head.hbs
:
{{!-- app/templates/head.hbs --}}
<meta property="og:title" content={{model.title}} />
This creates a <meta>
tag whose property
attribute is og:title
.
Now we just need to tell it what the title is, which is typically determined dynamically by the current route's model:
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class Application extends Route {
// This service is the `model` in the head.hbs template,
// so any properties you set on it are accessible via
// `model.whatever`.
headData: service(),
afterModel(model) {
let title = model.title || "Untitled";
this.set('headData.title', title);
}
}
For more information, see ember-cli-head.
The browser and Node are two different environments, and functionality in one is not always available in the other. As you develop your app, you may need to pull in additional dependencies to provide for functionality not available by default in Node.
For example, localStorage
is not available in Node and you may instead want to save information to a Redis server by using the radredis
npm packge.
For security reasons, your Ember app running in FastBoot can only access packages (both built-in and from npm) that you have explicitly whitelisted.
To allow your app to require a package, add it to the fastbootDependencies
array in your app's package.json
:
{
"name": "my-sweet-app",
"version": "0.4.2",
"devDependencies": {
// ...
},
"dependencies": {
"radredis": "0.1.1"
},
"fastbootDependencies": [
"radredis",
"path"
]
}
The fastbootDependencies
in the above example means the only modules your Ember app can use are radredis
(provided from npm) and path
(a built-in Node module).
If the package you are using is not built in to Node, you must also specify the package and a version in the package.json
dependencies
hash. Built-in modules (path
, fs
, etc.) only need to be added to fastbootDependencies
.
From your Ember app, you can run FastBoot.require()
to require a package.
This is identical to the CommonJS require
except it checks all requests against the whitelist first.
if (typeof FastBoot !== 'undefined') {
let path = FastBoot.require('path');
let filePath = path.join('tmp', session.getID());
}
If you attempt to require a package that is not in the whitelist, FastBoot will raise an exception.
Note that the FastBoot
global is only available when running in FastBoot mode.
You should either guard against its presence or only use it in FastBoot-only initializers.
An Ember app running in the browser is long-lived: as soon as the page loads, you can start rendering parts of the UI as soon as the data driving that UI becomes available.
Ember starts by collecting the models for each route on screen (via Route
's model()
, beforeModel()
, and afterModel()
hooks).
If any of your routes return a promise from one of these hooks, Ember will wait until those promises resolve before rendering the templates.
This avoids a jarring "pop-in" effect.
Sometimes, though, you don't want secondary UI to block the main content. For example, imagine an app for managing contacts. It shows information for one contact, as well as a list of all contacts in the sidebar.
You may not want to wait for the entire list of contacts to load before showing the primary information the user is after: details about a specific contact.
So, once the model()
hook finishes loading for the route, the template is rendered, and the sidebar shows a loading spinner while the full list is loaded.
In the browser, the router's promise chain controls when the template is rendered. But you can always render immediately (by not returning a promise) and have each component on the page update once its backing data becomes available.
FastBoot is different. Because it's sending a static page of HTML to the user's browser, it needs to know when the page is "done." As soon as FastBoot thinks the page is done rendering, it converts the DOM into HTML, sends it to the browser, and destroys the application instance.
Like when deciding when to render a route's templates, FastBoot uses the promise chain built by routes' beforeModel()
, model()
, and afterModel()
hooks.
But if some components are responsible for marshalling their own data, they may render too late for the HTML response and the user (or the search crawler!) may see the loading state instead of the content you intended.
To work around this, you can add additional promises to the afterModel()
hook when in FastBoot mode that block rendering until all of the data is loaded.
Use services to coordinate between the route and the components on the page.
For example, let's imagine we're building a news page that also has a weather widget.
In the browser, we load the primary model (the article) in the model()
hook.
The weather component fetches data from a weather
service.
In the browser, we let the weather component render a loading spinner if the article loads before the weather data. But in FastBoot, we block rendering until the weather data is available.
Here's what the Route
for that page might look like:
// app/routes/article.js
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class Application extends Route {
@service fastboot;
@service store;
@service weather;
model({ article_id }) {
return this.store.find('article', article_id);
},
afterModel() {
if (this.fastboot.isFastBoot) {
return this.weather.fetch();
}
}
}
In this example, the Route
always returns the article model from the model hook.
The template won't render in the browser or in FastBoot until the article finishes loading.
We detect if the app is running in FastBoot via the call to this.get('fastboot.isFastBoot')
(and do notice that we've injected the fastboot
service!).
If we're in FastBoot mode, we add an additional promise to the promise chain in afterModel()
: we ask the weather
service for a promise for the weather data.
With this setup, the page in the browser will render as soon as the article has finished loading. But in FastBoot, we wait until both the article and weather have loaded to send the HTML back to the browser.
didInsertElement
for client-side DOM manipulationIn FastBoot, we do not invoke either didInsertElement
or willInsertElement
hooks.
If your components have any direct DOM manipulation you would do it in those hooks.
FastBoot calls the init
, didReceiveAttrs
, didUpdateAttrs
, willRender
, didRender
, and willDestroy
hooks.
Any code in these hooks will be run inside of FastBoot and should be free of references to browser APIs or DOM manipulation.
FastBoot relies on Ember.ApplicationInstance
to execute your Ember application on the server.
jQuery is disabled by default for these instances because most of jQuery depends on having full DOM access.
ember-fetch
for XHR requestsIf you are depending on jQuery for XHR requests, use ember-fetch and replace your $.ajax
calls with fetch
calls.
Because your app is now running in Node, not the browser, you will need a new set of tools to help diagnose problems.
Enable verbose logging by running the FastBoot server with the following environment variables set:
DEBUG=ember-cli-fastboot:* ember serve
Pull requests for adding or improving logging facilities are very welcome.
You can get a debugging environment similar to the Chrome developer tools running with a FastBoot app, although it's not (yet) as easy as in the browser.
First, install the Node Inspector:
npm install node-inspector -g
Next, start the inspector server.
Using the --no-preload
flag is recommended.
It waits to fetch the source code for a given file until it's actually needed.
node-inspector --no-preload
Once the debug server is running, you'll want to start up the FastBoot server with Node in debug mode.
One thing about debug mode: it makes everything much slower.
Since the ember serve
command does a full build when launched, this becomes agonizingly slow in debug mode.
Avoid the slowness by manually running the build in normal mode, then run FastBoot in debug mode without doing a build:
ember build && node --debug-brk ./node_modules/.bin/ember serve
This does a full rebuild and then starts the FastBoot server in debug mode.
Note that the --debug-brk
flag will cause your app to start paused to give you a chance to open the debugger.
Once you see the output debugger listening on port 5858
, visit http://127.0.0.1:8080/debug?port=5858 in your browser.
Once it loads, click the "Resume script execution" button (it has a ▶︎ icon) to let FastBoot continue loading.
Assuming your app loads without an exception, after a few seconds you will see a message that FastBoot is listening on port 3000.
Once you see that, you can open a connection.
Any exceptions should be logged in the console, and you can use the tools you'd expect such as console.log
, debugger
statements, etc.
FastBoot implements server-side rendering, which means it runs your JavaScript Ember app on the server so it can send HTML to the user, not an empty white screen while the JavaScript loads.
Server-side rendering, or SSR, is a relatively new idea. Because it's so new, there are often misconceptions about how it works. Let's start by defining what SSR is not.
FastBoot does not replace your existing API server. Instead, FastBoot drops in on top of your existing API server to improve startup performance and make your Ember app more accessible to user agents without JavaScript.
Perhaps the best way to think about FastBoot is that it is like a browser running in a data center instead of on a user's device. This browser already has your Ember application loaded and running.
When a request comes in from an end user, the browser in the datacenter is told to visit the same URL. Because it is already running, there is little startup time. And because it's in the same datacenter as your API server, network requests are very low latency. It's like a turbocharged version of the app running in the user's browser.
Once the browser on the server finishes loading the requested URL, we take its DOM, serialize it to HTML, and send it to the user's browser running on their device. They don't need to download a bunch of JavaScript, wait for it to start up, then wait some more while it makes API requests for the data needed to render.
Instead, the very first thing the user's browser downloads is the rendered HTML. Only once the HTML and CSS have finished loading does the browser start to download the Ember app's JavaScript.
For users with slow connections or slow devices, this means the very first thing they see is the content they were after. No more waiting around for multi-hundred-kilobyte payloads to download and parse just to read a blog post.
When you run ember build
at the command line, Ember compiles your application into the dist
directory.
That directory contains everything you need to make your application work in the browser.
You can upload it to a static hosting service like S3 or Firebase, where browsers can download and run the JavaScript on the user's device.
FastBoot is a little different because, instead of being purely static, it renders HTML on the server and therefore needs more than just static hosting. We need to build additional assets that's designed to work in Node rather than the browser.
When you run ember build
, your FastBooted app will produce app.js
and vendor-d364124232469f73db00693819bb2575.js
in dist/assets
that are used in the browser and Node and will produce an additive asset containing FastBoot overrides for your app in app-fastboot.js
in dist/assets
.
The artifacts in dist/assets
are client-side copies of your application that will be sent to all browsers that request your application.
The artifacts for your FastBoot server are defined by a manifest
key entry in dist/package.json
which
will minimally contain vendor.js
, app.js
and app-fastboot.js
.
You can test that the process is working (and that your app is FastBoot-compatible) by running ember serve
, which builds your app then starts up a local server and renders your app on server side.
Once you've confirmed everything looks good, it's ready to hand off to the FastBoot server running in the production environment.
The good news is that this process is usually handled for you by a deployment plugin for ember-cli-deploy
; see Deploying for more information about different deployment strategies.
The [FastBoot server][fastboot-server] is the heart of the FastBoot architecture.
The server offers an [Express middleware][express] that can be integrated into an existing Node infrastructure, or run standalone.
var server = new FastBootServer({
appFiles: [appFile, appFastBootFile],
vendorFiles: [vendorFile],
htmlFile: htmlFile,
ui: ui
});
var app = express();
app.get('/*', server.middleware());
var listener = app.listen(options.port, function() {
var host = listener.address().address;
var port = listener.address().port;
console.log('FastBoot running at http://' + host + ":" + port);
});
Does this mean I need to rewrite my API server in Ember or JavaScript?
No. FastBoot works with your existing API rather than replacing it. That means you can drop in FastBoot no matter what backend you use, whether it's Node, Rails, PHP, .NET, Java, or any other stack.
That said, the FastBoot server does require Node. So even though you don't need to replace your backend, you do need to have the ability to deploy Node apps.
If the app is running in FastBoot and not the user's browser, how do I access things like localStorage
where I keep authentication tokens?
The only information about the user requesting the page is any HTTP cookies you have set. To work with FastBoot, you should store critical information in cookies.
Alternatively, you can store session data for users in stateful persistence on the server, such as a Redis instance. When the request comes in, you will need to exchange a cookie for the full user session. We hope that the community can work together to build a robust solution to this scenario.
If you discover a security vulnerability in FastBoot, we ask that you follow Ember' responsible disclosure security policy.