webapp
The webapp
package is what lets your Meteor app serve content to a web
browser. It is included in the meteor-base
set of packages that is
automatically added when you run meteor create
. You can easily build a
Meteor app without it - for example if you wanted to make a command-line
tool that still used the Meteor package system and DDP.
This package also allows you to add handlers for HTTP requests. This lets other services access your app’s data through an HTTP API, allowing it to easily interoperate with tools and frameworks that don’t yet support DDP.
webapp
exposes the connect API for
handling requests through WebApp.connectHandlers
.
Here’s an example that will let you handle a specific URL:
// Listen to incoming HTTP requests (can only be used on the server).
WebApp.connectHandlers.use('/hello', (req, res, next) => {
res.writeHead(200);
res.end(`Hello world from: ${Meteor.release}`);
});
WebApp.connectHandlers([path], handler)
Register a handler for all HTTP requests.
Arguments
- path String
-
This handler will only be called on paths that match this string. The match has to border on a
/
or a.
.For example,
/hello
will match/hello/world
and/hello.world
, but not/hello_world
. - handler connectHandlersCallback
-
A handler function that will be called on HTTP requests. See
connectHandlersCallback
callback handler for WebApp.connectHandlers
Arguments
- req Object
-
a Node.js IncomingMessage object with some extra properties. This argument can be used to get information about the incoming request.
- res Object
-
a Node.js ServerResponse object. Use this to write data that should be sent in response to the request, and call
res.end()
when you are done. - next Function
-
Calling this function will pass on the handling of this request to the next relevant handler.
Serving a Static Landing Page
One of the really cool things you can do with WebApp is serve static HTML for a landing page where TTFB (time to first byte) is of utmost importance.
The Bundle Visualizer and Dynamic Imports are great tools to help you minimize initial page load times. But sometimes you just need to skinny down your initial page load to bare metal.
The good news is that WebApp makes this is really easy to do.
Step one is to create a your static HTML file and place it in the private folder at the root of your application.
Here’s a sample index.html you might use to get started:
<head>
<title>Fast Landing Page</title>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no" />
<link rel="stylesheet" href="path to your style sheet etc">
</head>
<body>
<!-- your content -->
</body>
<script>
// any functions you need to support your landing page
</script>
</html>
Then using the connectHandlers method described above serve up your static HTML on app-root/ page load as shown below.
/* global WebApp Assets */
import crypto from 'crypto'
import connectRoute from 'connect-route'
WebApp.connectHandlers.use(connectRoute(function (router) {
router.get('/', function (req, res, next) {
const buf = Assets.getText('index.html')
if (buf.length > 0) {
const eTag = crypto.createHash('md5').update(buf).digest('hex')
if (req.headers['if-none-match'] === eTag) {
res.writeHead(304, 'Not Modified')
return res.end()
}
res.writeHead(200, {
ETag: eTag,
'Content-Type': 'text/html'
})
return res.end(buf);
}
return res.end('<html><body>Index page not found!</body></html>')
})
}))
There are a couple things to think about with this approach.
We’re reading the contents of index.html using the Assets module that makes it really easy to read files out of the private root folder.
We’re using the connect-route NPM package to simplify WebApp route processing. But you can use any package you want to understand what is being requested.
And finally, if you decide to use this technique you’ll want to make sure you understand how conflicting client side routing will affect user experience.
Dynamic Runtime Configuration
In some cases it is valuable to be able to control the meteor_runtime_config variable that initializes Meteor at runtime.
Example
There are occasions when a single Meteor server would like to serve multiple cordova applications that each have a unique ROOT_URL
. But there are 2 problems:
- The Meteor server can only be configured to serve a single
ROOT_URL
. - The
cordova
applications are build time configured with a specificROOT_URL
.
These 2 conditions break autoupdate
for the cordova applications. cordova-plugin-meteor-webapp
will fail the update if the ROOT_URL
from the server does not match the build time configured ROOT_URL
of the cordova application.
To remedy this problem webapp
has a hook for dynamically configuring __meteor_runtime_config__
on the server.
Dynamic Runtime Configuration Hook
WebApp.addRuntimeConfigHook(({arch, request, encodedCurrentConfig, updated}) => {
// check the request to see if this is a request that requires
// modifying the runtime configuration
if(request.headers.domain === 'calling.domain') {
// make changes to the config for this domain
// decode the current runtime config string into an object
const config = WebApp.decodeRuntimeConfig(current);
// make your changes
config.newVar = 'some value';
config.oldVar = 'new value';
// encode the modified object to the runtime config string
// and return it
return WebApp.encodeRuntimeConfig(config);
}
// Not modifying other domains so return undefined
return undefined;
})
WebApp.addRuntimeConfigHook(callback)
Hook that calls back when the meteor runtime configuration,
__meteor_runtime_config__
is being sent to any client.
returns: Object { stop: function, callback: function }
stop
Function Callstop()
to stop getting callbacks.callback
Function The passed incallback
.
Arguments
- callback addRuntimeConfigHookCallback
-
See
addRuntimeConfigHookCallback
description.
Callback for addRuntimeConfigHook
.
If the handler returns a falsy value the hook will not modify the runtime configuration.
If the handler returns a String the hook will substitute the string for the encoded configuration string.
Warning: the hook does not check the return value at all it is the responsibility of the caller to get the formatting correct using the helper functions.
addRuntimeConfigHookCallback
takes only one Object
argument
with the following fields:
Arguments
- options.arch String
-
The architecture of the client requesting a new runtime configuration. This can be one of
web.browser
,web.browser.legacy
orweb.cordova
. - options.request Object
-
A NodeJs IncomingMessage https://nodejs.org/api/http.html#http_class_http_incomingmessage
Object
that can be used to get information about the incoming request. - options.encodedCurrentConfig String
-
The current configuration object encoded as a string for inclusion in the root html.
- options.updated Boolean
-
true
if the config for this architecture has been updated since last called, otherwisefalse
. This flag can be used to cache the decoding/encoding for each architecture.
Additionally, 2 helper functions are available to decode the runtime config string and encode the runtime config object.
WebApp.decodeRuntimeConfig(rtimeConfigString)
Takes an encoded runtime string and returns a runtime configuration object.
Arguments
- rtimeConfigString String
WebApp.encodeRuntimeConfig(rtimeConfig)
Takes a runtime configuration object and returns an encoded runtime string.
Arguments
- rtimeConfig Object
Updated Runtime Configuration Hook
const autoupdateCache;
// Get a notification when the runtime configuration is updated
// for each arch
WebApp.addUpdatedNotifyHook(({arch, manifest, runtimeConfig}) => {
// Example, see if runtimeConfig.autoupdate has changed and if so
// do something
if(!_.isEqual(autoupdateCache, runtimeConfig.autoupdate)) {
autoupdateCache = runtimeConfig.autoupdate;
// do something...
}
})
WebApp.addUpdatedNotifyHook(handler)
Hook that runs when the meteor runtime configuration is updated. Typically the configuration only changes during development mode.
Arguments
- handler addUpdatedNotifyHookCallback
-
The
handler
is called on every change to anarch
runtime configuration. SeeaddUpdatedNotifyHookCallback
.
callback handler for addupdatedNotifyHook
Arguments
- options.arch String
-
The architecture that is being updated. This can be one of
web.browser
,web.browser.legacy
orweb.cordova
. - options.manifest Object
-
The new updated manifest object for this
arch
. - options.runtimeConfig Object
-
The new updated configuration object for this
arch
.