This talk was part of Developer Growth Summit 2022. Go to the DGS2022 page to view recordings of all sessions.
About the talk
In this talk, we'll use Rocket.Chat's open source apps-engine as an example of how to use VM module native to the platform, and how to create the core of an external plugin system on top of Node.js VM.
This talk will cover
- Why external plugins?
- Environment requirements for the system
- The Node.JS VM Module
- The structure of a plugin
- Interacting with a plugin
About the speaker
![Douglas Gubert](https://assets.codementor.io/avatar/default.png)
When Douglas is not coding and worrying about developer experience and code semantics, he is studying languages or playing video-games with his kid.
Highlights of the talk
Why external plugins and why you should build them
External plugins allow third parties to integrate with your product. Plugins also foster your community, engaging them more deeply with your product or project. This is paramount for open source projects. In Rocket.chat’s case, it streamlined the community management process as now people who wanted new features don't need to come up with pull requests, style of codes, and even potentially getting rejected because it doesn’t fit the product. Instead, they can develop a plugin for this purpose, which is much easier than diving into a humongous project to get a grasp of what’s going on. Having plugins also enables a marketplace to distribute the plugins, which leads to potential monetization strategy of your product. Finally, being able to extend and enhance applications you use and love is a cool and awesome thing to experience.
What are the environment requirements to run a plugin system?
Generally speaking, there are two approaches to designing a plugin system. First, you can have a set of APIs over HTTP, with some web hooks, and call it a day. A very important component of this plugin system would be the Internet, as it is the means through which your app and your plugin will communicate with each other.
Another more unconventional approach of doing this is to host the plugin within your own web application on the server side in order to provide the extended functionalities to your users at any time within their network, running third party and trusted code inside your server. The plugin system is going to be in your core application and provide the means for the plugin system to communicate with the business logic of the application.
One thing to note is that the two approaches have a commonality, which is that you’ll need to specify and implement a public contract with the API itself, the known methods and parameters that the plugin would need to interact with your applications.
At the base of the plugin system, you need a tool that’ll provide you with a sandbox environment, isolated from the entire application and other plugins, and will restrict the scope accessible to the plugin, providing access only to the feature set you want it to access.
What is the structure of a Node.js VM external plugin?
The virtual machine module enables compiling and running within a V8 virtual machine context. JavaScript code can be compiled and run immediately or compiled, saved, and run later. The invoking code provides a context, which is treated as the global variable of the invoked code. Here’s an example of the code. The isolation happens by contextifying an object, by using the createContext method from the VM itself. You can use it as the context for an execution inside the VM. On the global scope, x is 1 and y is not defined.
const vm = require(‘vm’);
const x = 1;
constant context = { x: 2 };
vm,createContext(context); // Contexify the object.
const code = x += 40; var y = 17;’;
// ‘x’ and ‘y’ are global variables in the context.
// Initially, x has the value 2 because that is the value of context.x.
vm.runInContext(code, context);
console.log(context.x); // 42
console.log(context.y); // 47
console.log(x); // 1; y is not defined
From this example, you can see that what you need is the plugin’s code and a properly built context object that “injects” dependencies. The context object makes dependencies visible and available to the plugin, and these dependencies will be what the plugin uses to communicate with the outside world in a controlled manner. They will heavily depend on your application as well and can be anything from repositories with entities, logger objects, or npm packages you want to make available.