This library is based on https://github.com/tjanczuk/edge all credit for original work goes to Tomasz Janczuk.
Edge.js allows you to run Node.js and .NET code in one process on Windows, macOS, and Linux
You can call .NET functions from Node.js and Node.js functions from .NET.
Edge.js takes care of marshaling data between CLR and V8. Edge.js also reconciles threading models of single-threaded V8 and multi-threaded CLR.
Edge.js ensures correct lifetime of objects on V8 and CLR heaps.
The CLR code can be pre-compiled or specified as C#, F#, Python (IronPython), or PowerShell source: Edge.js can compile CLR scripts at runtime.
Edge can be extended to support other CLR languages or DSLs.
Edge.js provides an asynchronous, in-process mechanism for interoperability between Node.js and .NET. You can use this mechanism to:
- script Node.js from a .NET application on Windows using .NET Framework
- script C# from a Node.js application on Windows, macOS, and Linux using .NET Framework/.NET Core
- use CLR multi-threading from Node.js for CPU intensive work more...
- write native extensions to Node.js in C# instead of C/C++
- integrate existing .NET components into Node.js applications
- access MS SQL from Node.js using ADO.NET
- script F# from Node.js
- script Powershel from Node.js
- script Python (IronPython) from Node.js
Read more about the background and motivations of the project here.
- Support for new versions of Node.Js.
- Support for .NET Core 3.1 - 9.x on Windows/Linux/macOS.
- Fixes AccessViolationException when running Node.js code from C# PR #573.
- Fixes StackOverflowException PR #566 that occurs when underlying C# code throws complex exception.
- Fixes issues #469, #713
- Other PRs: PR #725, PR #640
- Multiple bug fixes and improvements to the original code.
NPM package edge-js
NuGet package EdgeJs
For use with Electron electron-edge-js
VS Code uses Electron shell, to write extensions for it using Edge.js use
electron-edge-js
Sample app that shows how to work with .NET Core using inline code and compiled C# libraries.
https://github.com/agracio/edge-js-quick-start
- Windows: Visual C++ Redistributable
- Windows supports 4 latest LTS or candidate LTS releases (even numbered).
- Windows supports up to 1 "Current" (odd numbered) release and drops it when superseeded by new LTS candidate.
- macOS comes precompiled with same releases as Windows. When using Node.js version that is not pre-compiled
edge-jsbinaries will be compiled duringnpm installusingnode-gyp. - Linux will always compile
edge-jsbinaries duringnpm installusingnode-gyp.
| Version | x86 | x64 | arm64 |
|---|---|---|---|
| 18.x | ✔️ | ✔️ | ❌ |
| 20.x | ✔️ | ✔️ | ✔️ |
| 22.x | ✔️ | ✔️ | ✔️ |
| 24.x | ❌ | ✔️ | ✔️ |
| 25.x | ❌ | ✔️ | ✔️ |
| Version | x64 | arm64 |
|---|---|---|
| 18.x | ✔️ | ✔️ |
| 20.x | ✔️ | ✔️ |
| 22.x | ✔️ | ✔️ |
| 24.x | ✔️ | ✔️ |
| 25.x | ✔️ | ✔️ |
| Version | x64 | arm64 |
|---|---|---|
| 16.x - 25.x | ✔️ | ✔️ |
| Version | x64 | arm64 |
|---|---|---|
| 16.x - 25.x | ✔️ | ✔️ |
Other Linux architectures might work but have not been tested.
| Script CLR from Node.js | Script Node.js from CLR | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
Mono is no longer actively supported. Existing code will remain In Edge.Js but focus will be on .NET Core.
* Edge.js does not have a flag to force it to run in Mono environment on Windows, it would only be possible to run if you have Windows without .NET Framework present.
* On Linux there is a known bug that will throw exception when executing C# code under certain conditions. Use one of the following options.
- Set
LD_PRELOADenv variable before running Edge.js with Mono
EXPORT LD_PRELOAD="libmono-2.0.so libmonosgen-2.0.so libstdc++.so.6"- Set
LD_PRELOADin your Node.js app entry point using Javascript
Object.assign(process.env, {
// Work around Mono problem: undefined symbol: mono_add_internal_call_with_flags
LD_PRELOAD: 'libmono-2.0.so libmonosgen-2.0.so libstdc++.so.6',
});Source: mono/mono#17079 (comment)
Edge.js uses this code in test setup:
Lines 19 to 24 in 8bab03a
When packaging your application using Webpack make sure that edge-js is specified as external module.
module.exports = {
target: 'node',
externals: {
'edge-js': 'commonjs2 edge-js',
'edge-cs': 'commonjs2 edge-cs',
},
node: {
__dirname: true,
__filename: true,
},
}next.config.js
const nextConfig = {
serverExternalPackages: ['edge-js'],
}
module.exports = nextConfignext.config.ts
const nextConfig: NextConfig = {
serverExternalPackages: ['edge-js'],
};
export default nextConfig;Provides simple access to SQL without the need to write separate C# code.
| Framework | Platform | NPM Package | Language code | Documentation |
|---|---|---|---|---|
| .NET 4.5 | Windows | edge-sql |
sql |
Script SQL in Node.js 🔗 |
| CoreCLR | Any | edge-sql |
sql |
Script SQL in Node.js 🔗 |
NOTE This functionality requires IronPython 3.4
| Framework | Platform | NPM Package | Language code | Documentation |
|---|---|---|---|---|
| .NET 4.5 | Windows | edge-py |
py |
Script Python in Node.js 🔗 |
| CoreCLR | Any | edge-py |
py |
Script Python in Node.js 🔗 |
NOTE CoreCLR requires dotnet 8
| Framework | Platform | NPM Package | Language code | Documentation |
|---|---|---|---|---|
| .NET 4.5 | Windows | edge-ps |
ps |
Script PowerShell in Node.js 🔗 |
| CoreCLR | Windows | edge-ps |
ps |
Script PowerShell in Node.js 🔗 |
| Framework | Platform | NPM Package | Language code | Documentation |
|---|---|---|---|---|
| .NET 4.5 | Windows | edge-fs |
fs |
Script F# in Node.js 🔗 |
| CoreCLR | Windows | edge-fs |
fs |
Script F# in Node.js 🔗 |
Scripting CLR from Node.js - full documentation
Scripting Node.js from CLR - full documentation
Scripting CLR from Node.js sample app https://github.com/agracio/edge-js-quick-start
- Inline C# code
- Using compiled dll
- Using CoreCLR
- Executing synchronously without function callback
- Using promises/async
- Scripting Node.js from CLR
- Docker
var edge = require('edge-js');
var helloWorld = edge.func(function () {/*
async (input) => {
return ".NET Welcomes " + input.ToString();
}
*/});
helloWorld('JavaScript', function (error, result) {
if (error) throw error;
console.log(result);
});var edge = require('edge-js');
var helloWorld = edge.func(`
async (input) => {
return ".NET Welcomes " + input.ToString();
}
`);
helloWorld('JavaScript', function (error, result) {
if (error) throw error;
console.log(result);
});var edge = require('edge-js');
var helloWorld = edge.func(function () {/*
async (dynamic input) => {
return "Welcome " + input.name + " " + input.surname;
}
*/});
helloWorld({name: 'John', surname: 'Smith'}, function (error, result) {
if (error) throw error;
console.log(result);
});var getPerson = edge.func({
source: function () {/*
using System.Threading.Tasks;
using System;
public class Person
{
public Person(string name, string email, int age)
{
Id = Guid.NewGuid();
Name = name;
Email = email;
Age = age;
}
public Guid Id {get;set;}
public string Name {get;set;}
public string Email {get;set;}
public int Age {get;set;}
}
public class Startup
{
public async Task<object> Invoke(dynamic input)
{
return new Person(input.name, input.email, input.age);
}
}
*/}
});
getPerson({name: 'John Smith', email: 'john.smith@myemailprovider', age: 35}, function(error, result) {
if (error) throw error;
console.log(result);
});When using inline C# class code must include
public class Startup
{
public async Task<object> Invoke(object|dynamic input)
{
// code
// return results
}
}// People.cs
using System;
namespace People
{
public class Person
{
public Person(string name, string email, int age)
{
Id = Guid.NewGuid();
Name = name;
Email = email;
Age = age;
}
public Guid Id {get;}
public string Name {get;}
public string Email {get;}
public int Age {get;}
}
}
// EdgeJsMethods.cs
using System.Threading.Tasks;
using People;
namespace EdgeJsMethods
{
class Methods
{
public async Task<object> GetPerson(dynamic input)
{
return await Task.Run(() => new Person(input.name, input.email, input.age));
}
}
}var edge = require('edge-js');
var getPerson = edge.func({
assemblyFile: myDll, // path to compiled .dll
typeName: 'EdgeJsMethods.Methods',
methodName: 'GetPerson'
});
getPerson({name: 'John Smith', email: 'john.smith@myemailprovider', age: 35}, function(error, result) {
if (error) throw error;
console.log(result);
});public async Task<object> MyMethod(object|dynamic input)
{
//return results sync/async;
}- If not set Edge.js will run as .NET 4.5 on Windows.
- On macOS and Linux Edge.js will default to Mono if it is installed otherwise will run as CoreCLR.
- Can be set using
jscode below or as an environment variableSET EDGE_USE_CORECLR=1orEXPORT EDGE_USE_CORECLR=1depending on your platform. - Must be set before
var edge = require('edge-js');
// set this variable before
// var edge = require('edge-js');
process.env.EDGE_USE_CORECLR=1
var edge = require('edge-js');
var helloWorld = edge.func(function () {/*
async (input) => {
return ".NET Welcomes " + input.ToString();
}
*/});
helloWorld('JavaScript', function (error, result) {
if (error) throw error;
console.log(result);
});If your C# implementation will complete synchronously you can call this function as any synchronous JavaScript function as follows:
var edge = require('edge-js');
var helloWorld = edge.func(function () {/*
async (input) => {
return ".NET Welcomes " + input.ToString();
}
*/});
var result = helloWorld('JavaScript', true);Calling C# asynchronous implementation as a synchronous JavaScript function will fail
var edge = require('edge-js');
var helloWorld = edge.func(function () {/*
async (input) => {
return await Task.Run(() => ".NET Welcomes " + input.ToString());
}
*/});
// sync call will throw exception
var result = helloWorld('JavaScript', true);var func = edge.func(function () {/*
async (dynamic input) => {
return "Welcome " + input.name + " " + input.surname;
}
*/});
function helloWorld(){
return new Promise((resolve, reject) =>{
func({name: 'John', surname: 'Smith'}, function (error, result) {
if(error) reject(error);
else resolve(result);
});
});
}using System;
using System.Threading.Tasks;
using EdgeJs;
class Program
{
public static async Task Start()
{
var func = Edge.Func(@"
return function (data, callback) {
callback(null, 'Node.js welcomes ' + data);
}
");
Console.WriteLine(await func(".NET"));
}
static void Main(string[] args)
{
Start().Wait();
}
}More examples in tests DoubleEdge.cs
Dockerfile: Dockerfile
Docker Hub image: agracio/ubuntu-node-netcore
- Based on Ununtu 24.04
- User directory
devvol
- Node.js 22
- dotnet 9
- git
- build tools
- sudo, curl, wget
- node-gyp
- Run interactive starting in
devvol, setEDGE_USE_CORECLR=1at container level - Git clone
edge-jsand enter cloned repo directory npm install- Run tests
docker run -w /devvol -e EDGE_USE_CORECLR=1 -it agracio/ubuntu-node-netcore:latest
git clone https://github.com/agracio/edge-js.git && cd edge-js
npm i
npm test
Ah, whatever problem you have. If you have this problem, this solves it.
--Scott Hanselman (@shanselman)
Read the Edge.js introduction on InfoQ.
Listen to the Edge.js podcast on Herdingcode.