
Creating Custom Plugins with Fusion.js, Uber’s Open Source Web Framework
22 February 2019 / Global
Plugins are a core element of Fusion.js, Uber’s open source universal web framework, and its architecture. Plugins can be used to encapsulate functions, which are reused in different parts of the application. Examples of functions encapsulated in plugins are logging, translations, implementation of libraries and integration with data sources.
A number of official plugins are provided by Fusion.js, and they allow developers to build complex applications without having to write their own plugins. However, the framework also enables developers to build their own plugins. In this article, we offer an an example of how to create a custom plugin with Fusion.js.
Plugin concepts
Fusion.js applications are universal, which means they have a single entry point. Fusion.js plugins share this architecture, making it possible to install a library into an application with a single line of code. This is achieved through dependency injection.
Dependency injection
Dependency injection is not a built-in concept for many JavaScript-based back-end frameworks, and if it is, the implementation is not always ideal. Because of that, developers are often hesitant to use dependency injection, even more so if a third party library has to be used to manage the dependencies.
Unlike traditional JavaScript-based plugins, dependency injection is a vital part of Fusion.js plugins, and plugins can expose a well-defined API as a service to other plugins. This means plugins can be created, and easily injected where needed, across the application.
Some of the benefits of Fusion.js’ dependency injection functionality include the ability to:
- Create a plugin and inject it where needed
- Easily share code across the application
- Decouple the application
- Increase test coverage with testable plugins
Create and inject where needed
A common way to manage dependencies, even in larger applications, is to add them at the top of the application and include everything in the build. Fusion.js has a different approach: dependencies are injected where needed, which means only what is needed is included in the browser bundles.
Dependency injection is not only a core feature in Fusion.js, it strengthens the plugin concept by allowing them to expose well-defined APIs as services, which can be injected into other plugins. This functionality makes decoupling components or features, and even sharing code, easier.
Even encapsulating simple features as plugins can be useful. Take logging, for example. Logging is typically useful in all parts of an application, and with a logging service plugin, the application can easily implement logging features where needed. Using a plugin for logging also makes code easier to maintain, since all logging code is maintained in a single location. It’s not necessary to make changes everywhere logging is implemented.
Testing made easy
Fusion.js architects have been focused on optimizing testability since the early design phases. Coming from an architecture like Uber’s, where testing was a challenge due to high coupling of subsystems and order-of-operation requirements, improving testability was a requirement.
Fusion.js supports modern testing tools like Jest, Puppeteer, and Enzyme. In addition to supporting third-party testing tools, Fusion.js provides fusion-test-utils, a tool for testing plugins. This tool can mock the server itself, making it possible to quickly run integration tests.
Services exposed through Fusion.js plugins with dependencies can be unit tested by calling the service, and passing in the required dependencies as arguments. (Check out an example of how to unit test a plugin.)
Sample application
To illustrate how plugins can be created and used, we built a small sample application. In our use case, this sample application converts temperatures from Fahrenheit to Celsius, and vice versa.
The application’s functionality is handled by two plugins. One plugin exposes the temperature conversion services, taking the temperature as input and returning the converted temperature. The plugin’s middleware will render the result of the temperature conversion in both directions in the browser. The second plugin will inject the conversion services from the first plugin to do a one-way conversion, and thereby demonstrate how dependency injection works in Fusion.js.
The steps to our temperature conversion application are:
- Create a Fusion.js application
- Create the converter plugin
- Create a component for rendering results
- Register the plugin
- Re-use the plugin by injecting it in a new plugin
Create Fusion.js application
Creating the Fusion.js application is very easy when using yarn create. If you already have done this before, you can skip to the next step. If you are new to Fusion.js, this is where it all starts.
In a terminal, run the following command:
yarn create fusion-app <my-app-name> |
This command will create a boilerplate app, with all necessary files and directories to run an included demo page. After running yarn create, run this command:
cd <my-app-name> && yarn dev |
This serves up the Fusion.js demo page on http://localhost:3000.
The file structure inside the “src”-directory looks like this:
. ├── pages │ ├── home.js │ └── pageNotFound.js ├── main.js └── root.js |
The default demo page is not needed for this example, so the home.js file can be deleted, and the route for the home page can be removed from the root.js file as well. They can also be kept in the project.
Create converter plugin
The converter services are created in the converter plugin, and the result of the temperature conversion is rendered in the browser.
The converter plugin is created with the createPlugin() function:
// src/plugins/converters.js import {createPlugin} from ‘fusion-core’; export default createPlugin({ deps: {}, provides() {}, middleware() {} }); |
The plugin will not depend on other plugins, so deps can be removed. The conversion services will be created in provides, and the component will be rendered in middleware.
Conversion services
The plugin contains two services, one for converting temperatures from Celsius to Fahrenheit, and one for converting Fahrenheit to Celsius. Both services take one parameter, the temperature, and both return the converted temperature.
The temperature conversion functions are inserted into the plugin’s provides() function:
provides() { return { convertToCelsius(temp) { const celsius = (temp – 32) * 5/9; return celsius; }, convertToFahrenheit(temp) { const fahrenheit = (temp * (5/9)) + 32; return fahrenheit; } }; } |