Fusion.js: Building Testable, Modular, and Universal JavaScript Applications

Written by

You can put away your safety goggles, this post does not involve nuclear fusion. Instead, this will be a brief overview of some of the features in Fusion.js and its plugins.

Burdened with their existing monolithic web framework, Uber set out to create software that would allow them to spin up applications that are testable, modular, but most importantly, universal. Fusion.js is an open-source lightweight JavaScript boilerplate that provides flexibility through its plugin-based architecture. The goal of Fusion.js is to consolidate server and client logic into a single code base through the use of its plugins, which act as services, middleware, or sometimes both at once. This allows components to fetch data and render HTML on the server, improving page load performance.

Universal

Unlike some javascript boilerplates like Next.js, Electrode, or CRA, which are more focused on front-end development, Fusion.js is universal, meaning that apps have a single entry point file and code can be reused on both the server and the client. Plugins share that architecture and plugins are easy to integrate with a single line of code, using the register() method.

// src/main.js

import React from 'react';
import App from 'fusion-react';
import MyPlugin from './plugins/myPlugin.js';
import Root from './Root;

export default () => {
  const app = new App(<Root />);

  app.register(MyPlugin);

  return app;
};

Some operations are required only by specific environments, such as dealing with file systems or accessing browser APIs. For these use cases, the entry point file offers two variables to delineate client and server logic: __NODE__ and __BROWSER__.

// src/main.js
export default () => {
  const app = new App(<Root />);
  if (__NODE__) {
    app.register(FileSystemPlugin);
  }
  if (__BROWSER__) {
    app.register(BrowserApiPlugin);
  }
  return app;
};

This means that when the app is built, the compiler will only bundle the code required for that environment, __NODE__ for server, and __BROWSER__ for the client.

Modularity

Plugins act as a service, a middleware, or both at once, handling requests and processing data for the client to consume. They can be chained together and used to handle API routes, add logging, and many other operations.

Plugins are created using the createPlugin function and are composed of three simple elements:

  • Dependencies for the plugin are passed to deps. These dependencies are available to both the services and middleware.
  • Services are created in provides
  • Rendering and components are returned in middleware. This is where services can be leveraged to perform any number of tasks and return data to the client.
// src/plugins/myPlugin.js

import { createPlugin } from 'fusion-core';

export default createPlugin({
  deps: {},
  provides: (deps) => {},
  middleware: (deps, service) => {},
});

Here is a simple example of a plugin that calculates the area of certain shapes and passes the area to a logger. 

// src/plugins/myPlugin.js

import { LoggerToken } from 'fusion-tokens';

export const CalculatorToken = createToken('CalculatorToken');

export default createPlugin({
  deps: { logger, LoggerToken },
  provides: ({ logger }) => {
    areaSquare(length) {
      const area = length * length;
      logger.log(area);
      return area;
    },
    areaCircle(length) {
      const area = Math.PI * length * length;
      logger.log(area);
      return area;
    },
  },
});

Creating an HTTP endpoint and displaying the results of a request using the services created in another plugin can be accomplished by passing the plugin as a dependency to the middleware.

// src/plugins/endpoint.js

import { CalculatorToken } from '../plugins/calculator';
export default createPlugin({
  deps: { calculator: CalculatorToken },
  middleware: ({ calculator }) => (ctx, next) => {
    const { length } = ctx.query;
    if (ctx.path.startsWith('calculator/square')) {
      const areaSquare = calculator.areaSquare(length);
      ctx.body = 'The area of a square with length ' + length + 'cm is ' + ' areaCircle + 'cm squared';
    }
    if (ctx.path.startsWith('calculator/circle)) {
      const areaCircle = calculator.areaCircle(length);
      ctx.body = 'The area of a circle with radius ' + length + 'cm is ' + ' areaCircle + 'cm squared';
    }
  },
});

Testable

Straight out of the box, Fusion.js offers support for a suite of modern testing utilities, including Jest, Enzyme, and Puppeteer. When writing tests, a plugin and its dependencies is easily simulated, thanks to the nature of dependency injection.

// src/__tests__/myPlugin.js

import plugin from '../calculator.js';

test('calculates area', () => {
  const logger = {
    log: jest.fn(),
  };
  const calculator = plugin.provides({ logger });
  calculator.areaSquare(10);

  expect(logger.log.mock.calls[0][0]).toEqual(100);
});

These are just a few of the features available with Fusion.js. Check out the Fusion documentation for more information.

Frequently Asked Questions