• Stars
    star
    247
  • Rank 164,117 (Top 4 %)
  • Language
    JavaScript
  • License
    BSD 3-Clause "New...
  • Created over 11 years ago
  • Updated over 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Share configuration and state data of an Express app with the client-side via JavaScript.

Express State

npm Version Dependency Status Build Status

Share configuration and state data of an Express app with the client-side via JavaScript.

Overview

Goals

Express State is designed to make it easy to share configuration and state data from the server to the client. It can be used to share any data that needs to be available to the client-side JavaScript code of the an app: e.g., the current user, a CSRF token, model data, routes, etc.

Progressively enhanced Web apps can be built by rendering an app's initial state on the server and using Express State as the conduit through which the server passes data and control over to the client-side JavaScript code.

How It Works

Configuration and state data are exposed to client-side JavaScript via two methods: app.expose() and res.expose(), both of which make the data available on a special state "locals" object for views/templates to serialize and embed into HTML pages.

When views/templates embed this exposed data into an HTML page, it is serialized as literal JavaScript. The JavaScript serialization format is limited to expressions that initialize namespaces and the exposed data assigned to those namespaces, which is a superset of JSON that includes regular expressions and functions.

Features

Express State was written because of the shortcomings of express-expose. The following is a list of features highlighting differences when compared with express-expose:

  • An efficient and powerful serialization format: Literal JavaScript is used to namespace exposed data that is a superset of JSON and includes regular expressions and functions. This avoids the cost of allocating and parsing large JSON strings on the client and enables things like sharing routes defined as regular expressions with a client-side URL router.

  • Smart namespacing: A root namespace can be set via an app's state namespace setting and it will be prepended to namespaces passed to expose() unless they already contain it or they start with "window.". The "global" on to which the namespaces are created can also be controlled.

  • Precise data value overrides: Sub-values within exposed objects can be easily overridden without clobbering the entire object. Request scoped values can even override data exposed at the app's scope.

  • Lazy serialization: Exposed data objects are stored by reference, making them "live" and allowing their values to be updated even after the object has been exposed. Only the namespaces and data that are still reachable after the series of expose() calls will be serialized. Serialization can happen at anytime, on demand, by calling the toString() method on state "locals" objects.

    When data is not going to change the {cache: true} option can be set to eagerly serialize exposed objects, making repeated toString() calls more efficient.

  • Explicit extension of each Express app: Express State's functionality has to be explicitly added to an Express app via the exported extend() function. This prevents problems in complex apps where multiple versions of Express and/or multiple Express apps are used.

Installation

Install using npm:

$ npm install express-state

Usage

Extending an Express App

To use Express State with an Express app, the app must first be extended. Use the extend() method that Express State exports:

var express  = require('express'),
    expstate = require('express-state'),

    app = express();

expstate.extend(app);

Once extended, the app will have the app.expose() method, and response objects will have the res.expose() method.

Note: It's perfectly fine for the same Express app to be extended more than once; after the first time the app is extended, the subsequent extend() calls will be noops.

Exposing Data

Data can be exposed at two different scopes: the app's scope, and a request/response's scope via app.expose() and res.expose() respectively.

Express State uses Express's built-in "locals" system. When data is exposed at the app's scope, a special app.locals.state object is created and used as the backing store for all app.expose() calls. Express also merges app.locals with res.locals to create the context object in which views/templates are rendered. This means that, by default, data exposed at the app's scope will also be present when rendering views/templates for all requests.

Express State sets up a similar relationship using prototypal inheritance where res.locals.state inherits from app.locals.state. This means data exposed at the request scope will also contain exposed data from the app's scope. If values for the same namespace are exposed at both scopes, the request/response scope takes precedence and shadows the value at the app's scope.

Exposing App-Scoped Data

When data that needs to be exposed to the client-side JavaScript code is not request-specific and should be available to all requests, it should be exposed at the app's scope using app.expose().

The following example exposes a Flickr API key required by Flickr to identify requests:

app.expose({
    api_key: '02348notreal2394879137872358bla'
}, 'MY_APP.Flickr');

The client-side JavaScript code can now look up the Flickr API key at MY_APP.Flickr.api_key when it needs to make a request to Flickr's API.

Exposing Request-Scoped Data

When data that needs to be exposed to the client-side JavaScript is request-specific, it should be exposed at the request/response's scope using res.expose().

The following example shows how to create a middleware function to expose the current person's Cross Site Request Forgery (CSRF) token—this is a best practice where the CSRF is used to validate HTTP requests that mutate state:

// Add Express' packaged `cookieParser()`, `session()`, and `csrf()` middleware.
app.use(express.cookieParser());
app.use(express.session({secret: 'something secure, not this!'}));
app.use(express.csrf());

// Create a middleware function that will expose the CSRF token for the current
// request only.
app.use(function (req, res, next) {
    res.expose(req.session._csrf, 'MY_APP.CSRF_TOKEN');
    next();
});

The client-side JavaScript code can now add the X-CSRF-Token HTTP header with the value at MY_APP.CSRF_TOKEN to all XHRs it makes to the server.

Increase Performance of Unchanging or Static Data

It's common to expose app-scoped data which will not change during the lifecycle of the Express app instance. To improve per-request performance, this unchanging/static data can be eagerly serialized and cached by setting the {cache: true} option:

var CONFIG = {
    hostname : 'example.com',
    someOther: 'constant value'
};

app.expose(CONFIG, 'MY_APP.config', {cache: true});

Setting this option allows Express State to optimize the serialization process by keeping the serialized value around and re-using it every time the toString() method is invoked (which happens for every request.)

Note: When a large amount of data needs to be exposed to the client-side, it is recommended to come up with a strategy where all data which is common to most/every request be exposed at the app-scope with the {cache: true} option set.

Untrusted User Input

Always escape untrusted user input to protected against XSS attacks!

Express State provides a mechanism to expose configuration and state data as first-party JavaScript, which means any untrusted user input should be properly escaped based on the OWASP HTML escaping recommendations.

Express State will automatically encode any <, >, / characters within string values of exposed data to their Unicode counterparts during serialization. This provides a basic level of protection against XSS attacks by not allowing the "</script><script>" character sequence within an exposed string value to be interpreted and cause the browser prematurely close a script element and reopen a new one.

Even with the basic XSS protection Express State provides, it's still important to always escape untrusted user input.

Exposing Functions

Express State allows for functions to be serialized and sent to the browser, but this has a few limitations and practical constraints:

  • A TypeError will be thrown if a native built-in function is being serialized, like the Number constructor. Native built-ins should be called in wrapper functions, which can be serialized.

  • Functions should only be exposed if they are dependency free and monadic in nature. The original scope in which a function defined is not guaranteed to be present in the client-side environment. If a function references variables or has other dependencies outside its scope, it's likely not to work properly.

  • Application code should not be sent to the browser by exposing it via Express State. That would be a misuse of this library and it's recommended that client-side code be organized into serve-able files or modules allowing the browser to download the code via standard <script src=""> elements or a script loader.

Setting a Root Namespace

A common practice is to set a root namespace for an app so all of its exposed data is contained under one global variable in the client-side JavaScript code. A root namespace can be setup for an app using the state namespace setting:

app.set('state namespace', 'MY_APP');

Now anytime data is exposed, the root namespace will be prepended unless it already exists in the namespace passed into the expose() call or the passed-in namespace starts with "window.".

With the above "MY_APP" root namespace, the following are all equivalent and result in MY_APP.foo === 123 in the client-side JavaScript:

// These all have the same result on the client: `MY_APP.foo === 123`
app.expose(123, 'foo');
app.expose(123, 'MY_APP.foo');
app.expose(123, 'window.MY_APP.foo');

Setting a root namespace helps keep code DRY and configurable at the app level while having the "window." escape hatch for data that needs to be exposed at a specific namespace on the client.

Overriding Exposed Values

Objects that are exposed through either expose() method are stored by reference, and serialization is done lazily (unless the {cache: true} option was set). This means the objects are still "live" after they've been exposed. An object can be exposed early during the life cycle of a request and updated up until the response is sent.

The following is a contrived example, but shows how values can be overridden at any time and at any scope:

app.expose({root: '/'}, 'url');

app.use(function (req, res, next) {
    res.expose(req.path, 'url.path');
    res.expose(req.query, 'url.query');
    next();
});

On the client, the resulting url object would look like the following for a request to the URL "/foo?bar=baz":

{ root: '/',
  path: '/foo',
  query: { bar: 'baz' } }

Notice how exposing values at the url.path and url.query namespaces did not clobber the original url object exposed at the app's scope.

However, previously exposed data can be completely clobbered by simply exposing a new value at the same namespace. When this happens, Express State is smart enough to know it can release its references to the previous value objects and not waste CPU and bytes serializing them.

Serialization

Express State serializes exposed data to literal executable JavaScript. The JavaScript produced during serialization is limited to expressions that initialize namespaces and the exposed data assigned to those namespaces, which is a superset of JSON that includes regular expressions and functions.

JavaScript, as the serialization format, is more powerful and efficient than JSON. It avoids the cost of allocating and parsing large JSON strings on the client and enables things like sharing routes defined as regular expressions with a client-side URL router.

The special app.locals.state and res.locals.state objects contain a custom toString() method implementation, which serializes the objects to JavaScript that is human readable and can be embedded inside a <script> element in an HTML page.

The following example shows a series of expose() calls and the resulting output from serialization:

app.expose({bar: 'bar'}, 'foo');
app.expose(/baz/, 'foo.baz');
app.expose(function () { return 'bla'; }, 'a.very.big.ns');

// Serialize `app.locals.state` and log the result.
console.log(app.locals.state.toString());

The output of the console.log() call would be:

(function (root) {
/* -- Data -- */
root.foo = {"bar":"bar"};
root.foo || (root.foo = {});
root.foo.baz = /baz/;
root.a || (root.a = {});
root.a.very || (root.a.very = {});
root.a.very.big || (root.a.very.big = {});
root.a.very.big.ns = function () { return 'bla'; };
}(this));

Note: A TypeError will be thrown if a native built-in function is being serialized, like the Number constructor. Native built-ins should be called in wrapper functions, which can be serialized. See the Exposing Functions section.

Embedding Data in HTML with Templates

To pass along the exposed configuration and state data to the client-side JavaScript code, it needs to be embedded in a <script> element of the app's HTML pages.

In Express, res.render() is used to render a view/template and send the response to the client. When rendering, Express sets up a context, which is an object resulting from merging app.locals with res.locals. This means the special state object is available to the views/templates.

The following example is a basic Handlebars template that renders the serialized state object:

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Test App</title>
</head>

<body>
    <h1>Test App</h1>

    <script>
        {{{state}}}
    </script>
</body>
</html>

Note: In this example triple-mustaches ({{{ }}}) are used so that Handlebars does not HTML-escape the value. Handlebars will automatically call the toString() method on the special state object, which renders the JavaScript. See the Untrusted User Input section above.

Examples

Basic Usage

A runnable example of the most basic Express app that uses Express State.

API

Configuration and Defaults

The following properties are exported from the Express State module. Assigning values to these properties affects all Express apps extended with this Express State module instance. To set these values for a specific app, use App Settings.

local = "state"

A string property name on app.locals and res.locals where Express State creates its special objects used to store and serialize exposed data.

By default, Express State will create these objects:

  • app.locals.state
  • res.locals.state

namespace = null

The root namespace is a string that should be prepended on the namespaces provided to app.expose() and res.expose() method calls. By default, no root namespace is used, and namespaces are created directly on the global (window) object in the browser.

See Setting a Root Namespace for more details.

App Settings

The following settings use the Express Settings feature and only apply to the app which they are set(). These app settings take precedence over the Express State's global configuration settings above.

state local

Use state local to create a property on app.locals and res.locals where Express State creates its special objects used to store and serialize exposed data.

By default, no value is set, so Express State's exported local configuration value is used.

The following example sets the locals properties to app.locals.exposed and res.locals.exposed:

app.set('state local', 'exposed');

state namespace

Use state namespace to create a root namespace that should be prepended on the namespaces provided to app.expose() and res.expose() method calls. By default, no root namespace is used, and namespaces are created directly on the global (window) object in the browser.

The following example sets the root namespace to "MY_APP":

app.set('state namespace', 'MY_APP');

See Setting a Root Namespace for more details.

Static Methods

extend (app)

This function is exported from the Express State module that extends the functionality of the specified Express app by adding the two expose() methods: app.expose() and res.expose().

It's perfectly fine for the same Express app to be extended more than once; after the first time the app is extended, the subsequent extend() calls will be noops.

Parameters:

  • app: Express app instance to extend with Express State's functionality.

See Extending an Express App for more details.

Methods

app.expose (obj, [namespace], [options])

res.expose (obj, [namespace], [options])

The two expose() methods behave the same, the only difference being what scope the data is exposed, either the app's or at the request's scope.

These two methods are used to expose configuration and state to client-side JavaScript by making the data available on a special state "locals" object for views/templates to serialize and embed into HTML pages.

See the Untrusted User Input section above, and make sure untrusted user input is always escaped before it passed to this method.

Parameters:

  • obj: Any serializable JavaScript object to be exposed to the client-side.

  • [namespace]: Optional string namespace where the obj should be exposed. This namespace will be prefixed with any configured root namespace unless it already contains the root namespace or starts with "window.".

  • [options]: Options which can be specified as either the second or third argument to this method, and may contain the following:

    • [cache]: Optional boolean to signal that it's safe to cache the serialized form of obj because it won't change during the lifecycle of the app or res (depending on which expose() method is invoked.) The eagerly serialized result is cached to greatly optimize to speed of repeated calls to the toString() method.

    • [local]: Optional string name of the "locals" property on which to expose the obj. This is used to specify a locals property other than the configured or default ("state") one.

    • [namespace]: Used to specify a namespace (described above) when options is passed as the second argument to this method.

    • [isJSON]: Optional boolean to signal that the obj being serialized is pure JSON data. This allows serialization to take a fast-path making it over 3x faster.

Note: A TypeError will be thrown if a native built-in function is being serialized, like the Number constructor. Native built-ins should be called in wrapper functions, which can be serialized. See the Exposing Functions section.

See Exposing Data and Overriding Exposed Values for more details.

License

This software is free to use under the Yahoo! Inc. BSD license. See the LICENSE file for license text and copyright information.

More Repositories

1

anthelion

Anthelion is a plugin for Apache Nutch to crawl semantic annotations within HTML pages.
Java
2,841
star
2

mojito

[archiving soon] Yahoo! Mojito Framework
JavaScript
1,570
star
3

boomerang

End user oriented web performance testing and beaconing
JavaScript
1,182
star
4

xss-filters

Secure XSS Filters.
JavaScript
1,079
star
5

flux-examples

Isomorphic Flux examples with Fluxible
1,073
star
6

YangMingShan

YangMingShan is a collection of iOS UI components that we created while building Yahoo apps.
Objective-C
641
star
7

samoa

SAMOA (Scalable Advanced Massive Online Analysis) is an open-source platform for mining big data streams.
Java
426
star
8

dispatchr

A Flux dispatcher for applications that run on the server and the client.
385
star
9

oozie

Oozie - workflow engine for Hadoop
Java
375
star
10

android-range-seek-bar

It provides a SeekBar similar to the default Android one, but with two thumb controls allowing a range to be selected, and some other extras as well.
Java
345
star
11

omid

Transactional Support for HBase (Mirror of https://github.com/apache/incubator-omid)
Java
301
star
12

strip-loader

Webpack loader to strip arbitrary functions out of your production code.
JavaScript
282
star
13

flux-router-component

[DEPRECATED] React components and mixins for router related features that work for applications with Fluxible architecture
JavaScript
247
star
14

end-to-end

Use OpenPGP-based encryption in Yahoo mail.
JavaScript
223
star
15

swiv

For the open source UI formerly know as Pivot
TypeScript
167
star
16

kobold

Visual regression testing framework, comparing screenshots from multiple builds
JavaScript
153
star
17

Optimal-LSH

This package provides an efficient implementation of locality-sensitve hashing (LSH)
MATLAB
150
star
18

ygloo-ymagine

[archiving soon]
C
140
star
19

rtrace

Rtrace is an x86/x86_64 native code debugger written in Ruby with zero dependencies
Ruby
139
star
20

yos-social-objc

[dormant] Yahoo! Social SDK, an Objective-C library for Mac and iPhone developers.
Objective-C
110
star
21

coname

An experimental cooperative keyserver based on ideas from dename.
Go
107
star
22

crow

Cross-dimensional weighting for aggregated deep convolutional features.
Python
105
star
23

fluxible-router

MOVED TO FLUXIBLE REPO
104
star
24

pngjs-image

JavaScript-based PNG image encoder, decoder, and manipulator
JavaScript
93
star
25

mendel

A build toolchain for experimentation on isomorphic web applications with tree-inheritance and multivariate support.
JavaScript
88
star
26

daytona

An application-agnostic framework for automated performance testing and analysis.
PHP
80
star
27

howl

Common metadata layer for Hadoop's Map Reduce, Pig, and Hive
Java
78
star
28

yos-social-php5

[dormant] Yahoo! Social SDK - PHP5 library
PHP
76
star
29

ypromise

An ES6 Promise polyfill
JavaScript
73
star
30

cronshot

Node module to schedule, take, alter, and store web page screenshots.
JavaScript
69
star
31

yos-social-php

[dormant] Yahoo! Social SDK - PHP library
PHP
68
star
32

yos-social-python

[dormant] Python SDK
Python
62
star
33

gondola

High-performance Raft-based Java Web Container
Java
61
star
34

PyIOCe

Python IOC Editor
Python
60
star
35

Pluton

C++
58
star
36

YaraParser

Yara K-Beam Arc-Eager Dependency Parser
Java
55
star
37

fake-server

Fake-server is a generic and non-intrusive tool used to mock any server response. It has been designed to address issues when running tests against unstable or slow external servers.
JavaScript
54
star
38

csptester

A quick and easy way to test CSP behavior on modern browsers
HTML
49
star
39

storage-lru

An LRU cache to be used with pluggable storage mechanism
JavaScript
47
star
40

messenger-sdk-php

PHP SDK for Yahoo! Messenger API
PHP
46
star
41

secure-handlebars

Handlebars Context Pre-compiler
JavaScript
45
star
42

fs-lock

File restrictions in Node.js
JavaScript
41
star
43

html-purify

HTML5 Purify
HTML
40
star
44

scrollable

React Scrollable
JavaScript
40
star
45

YMPromptKit

YMPromptKit
Objective-C
36
star
46

simplified-lambda

Java
36
star
47

fluxible-plugin-routr

[DEPRECATED] A plugin for fluxible applications to provide routing methods
JavaScript
33
star
48

messenger-sdk

Yahoo! Messenger API SDK
Java
30
star
49

process-watcher

Launch and control your NodeJS processes.
JavaScript
29
star
50

filelogger

filelogger monitors files and sends a copy of every new line to a local or remote syslog
C
28
star
51

mojito-shaker

[archiving soon] Static asset rollup manager for Mojito applications.
JavaScript
27
star
52

locator

A node module that gives semantic meaning to filesystem paths.
JavaScript
24
star
53

mockaccino

A simple Node (and Express.js) server to quickly mock web services responses.
JavaScript
24
star
54

cnet

A wrapper around the Chromium HTTP network stack.
C++
21
star
55

pipr

Tool to pip install missing imports and more
Python
20
star
56

http-filters

Filters for HTTP requests
C++
20
star
57

ACT

ACT.js - Advertising Creative Technology library.
JavaScript
19
star
58

artifactory_ssh_proxy

An ssh proxy for Artifactory
Java
18
star
59

firefoxos-webbing

A template to package web apps for Firefox OS.
JavaScript
18
star
60

xpath_proto_builder

xpath_proto_builder is a library to convert objects (JSON, XML, POJO) into protobuf using xpath notation.
Java
18
star
61

express-yui

Express extension for YUI Applications.
JavaScript
17
star
62

yos-social-java

[dormant] YOS SDK for Java
Java
17
star
63

guerilla

Guerilla is a distributed build/test server specialized for iOS and Android.
JavaScript
17
star
64

spartan

A Scalable Client Authentication & Authorization System for Container-based Environments
JavaScript
17
star
65

Sparkle

Objective-C
16
star
66

express-combo

Combo handler for express applications.
JavaScript
15
star
67

express-secure-handlebars

Express with Secure Handlebars
JavaScript
13
star
68

express-map

Assembles an Express app's route configuration for sharing with the client side via JavaScript.
JavaScript
12
star
69

CocoaSanitizer

A suite of dynamic analysis tools to find bugs at design time. The tools leverage the Objective-C runtime, to look for common anti-patterns which can lead to crashes.
Objective-C
12
star
70

css-js

CSS Parser
JavaScript
11
star
71

express-annotations

Module to augment Express routes with metadata.
JavaScript
11
star
72

node-restrict

Nodejs module that blocks applications from using procss.binding('process_wrap'), process.kill and child_process methods.
JavaScript
11
star
73

extractify

Browserify plugin to extract code to be lazy loaded into separate bundles
JavaScript
10
star
74

bossmashup

Mashup library for Yahoo! Search BOSS
10
star
75

Openstack-EC2

Python
10
star
76

druid-extensions

This repository holds a number of extensions to Druid that we have created at Yahoo.
Java
10
star
77

keyshop

Stub keyserver for the End-to-End extension
Go
9
star
78

formality-classifier

A Python library to predict the formality level of text.
Python
9
star
79

mojito-pipeline

[archiving soon] A rendering scheduler and streamer for Mojito webapps
JavaScript
9
star
80

FlowEtl

This is a framework designed for the creation of testable components which can be interconnected via arbitrary inputs and outputs and those components can be executed in the correct order (inputs satisfied before running) automatically. This is useful since it aids developers in thinking in the paradigm where they plan components ahead of time, allowing for simpler reuse and refactoring later. At yahoo! this was created for a ETL like framework & pipeline, but its applications are not limited to solely that, since the concept itself is generic.
Java
9
star
81

yos-social-as3

[dormant] Yahoo! Social SDK, an ActionScript 3 library for Flash and AIR
ActionScript
9
star
82

locator-handlebars

Handlebars template compiler for locator
JavaScript
7
star
83

yupdates-wordpress

Posts a Yahoo! Update to your connections when you update your WordPress blog.
PHP
7
star
84

metafs

MetaFS is for generating a datastore of file metadata for rapid complex searches
Python
7
star
85

viper

Viper is a utility that returns a live host from a list of host names.
Java
7
star
86

yamall

PLSQL
6
star
87

openstack-ops-tools

Operational tools and utilities for managing openstack
Python
6
star
88

imlost-Android

Random Hack for Kindness project
Java
6
star
89

secure-handlebars-helpers

secure-handlebars-helpers
JavaScript
5
star
90

Image-Builder

Python
5
star
91

node-xlog

JavaScript
5
star
92

locator-lang

Locator plugin to compile language bundles
JavaScript
4
star
93

zeus

Configuration Compiler
C++
4
star
94

memcached-binary

Binary Memcached client for Node.js
JavaScript
4
star
95

cronshot-local

JavaScript
4
star
96

mojito-jscheck

[archiving soon]
JavaScript
4
star
97

mojito-markup-test

[archiving soon]
JavaScript
4
star
98

mojito-rs-hotswap

[archiving soon] Hotswap plugin for Mojito's Resource Store
JavaScript
4
star
99

ygloo

[archiving soon]
4
star
100

yos-social-demo-dotnet

[dormant] A sample implementation of Hybrid OAuth and YQL in .Net
C#
4
star