SkillAgentSearch skills...

Javascript.NodeJS

Invoke Javascript in NodeJS, from C#

Install / Use

/learn @JeringTech/Javascript.NodeJS
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Jering.Javascript.NodeJS

Build Status codecov License NuGet

Table of Contents

Overview
Target Frameworks
Platforms
Prerequisites
Installation
Usage
API
Performance
Building and Testing
Projects Using this Library
Related Concepts
Contributing
About

Overview

Jering.Javascript.NodeJS enables you to invoke javascript in NodeJS, from C#. With this ability, you can use Node.js-javascript libraries and scripts from your C# projects.

You can use this library as a replacement for the obsoleted Microsoft.AspNetCore.NodeServices. InvokeFromFileAsync<T> replaces INodeService's InvokeAsync<T> and InvokeExportAsync<T>.

This library is flexible - it provides both a dependency injection (DI) based API and a static API. Also, it supports invoking both in-memory and on-disk javascript.

Static API example:

string javascriptModule = @"
module.exports = (callback, x, y) => {  // Module must export a function that takes a callback as its first parameter
    var result = x + y; // Your javascript logic
    callback(null /* If an error occurred, provide an error object or message */, result); // Call the callback when you're done.
}";

// Invoke javascript
int result = await StaticNodeJSService.InvokeFromStringAsync<int>(javascriptModule, args: new object[] { 3, 5 });

// result == 8
Assert.Equal(8, result);

DI-based API example:

string javascriptModule = @"
module.exports = (callback, x, y) => {  // Module must export a function that takes a callback as its first parameter
    var result = x + y; // Your javascript logic
    callback(null /* If an error occurred, provide an error object or message */, result); // Call the callback when you're done.
}";

// Create an INodeJSService
var services = new ServiceCollection();
services.AddNodeJS();
ServiceProvider serviceProvider = services.BuildServiceProvider();
INodeJSService nodeJSService = serviceProvider.GetRequiredService<INodeJSService>();

// Invoke javascript
int result = await nodeJSService.InvokeFromStringAsync<int>(javascriptModule, args: new object[] { 3, 5 });

// result == 8
Assert.Equal(8, result);

Target Frameworks

  • .NET Standard 2.0
  • .NET Framework 4.6.1
  • .NET Core 3.1
  • .NET 5.0
  • .NET 6.0

Platforms

  • Windows
  • macOS
  • Linux

Prerequisites

You'll need to install NodeJS and add the NodeJS executable's directory to the Path environment variable.

Installation

Using Package Manager:

PM> Install-Package Jering.Javascript.NodeJS

Using .NET CLI:

> dotnet add package Jering.Javascript.NodeJS

Usage

This section explains how to use this library. Topics:

Using the DI-Based API
Using the Static API
Invoking Javascript
Debugging Javascript
Configuring
Customizing Logic
Enabling Multi-Process Concurrency

Using the DI-Based API

First, create an INodeJSService. You can use any DI framework that has adapters for Microsoft.Extensions.DependencyInjection. Here, we'll use vanilla Microsoft.Extensions.DependencyInjection:

var services = new ServiceCollection();
services.AddNodeJS();
ServiceProvider serviceProvider = services.BuildServiceProvider(); 
INodeJSService nodeJSService = serviceProvider.GetRequiredService<INodeJSService>();

Once you've got an INodeJSService, you can invoke javascript using its invoke methods. All invoke methods are thread-safe. Here's one of its invoke-from-string methods:

string? result = nodeJSService.InvokeFromStringAsync<Result>("module.exports = (callback, message) => callback(null, message);", args: new[] { "success" });
Assert.Equal("success", result);

We describe all of the invoke methods in detail later on.

No clean up is required when you're done: the NodeJS process INodeJSService sends javascript invocations to kills itself when it detects that its parent process has died.

If you'd like to manually kill the NodeJS process, you can call INodeJSService.Dispose(). Once the instance is disposed, all invoke methods throw ObjectDisposedException. This is important to keep in mind since services.AddNodeJS() registers INodeJSService as a singleton (same instance injected every where).

Using the Static API

This library provides a static alternative to the DI-based API. StaticNodeJSService wraps an INodeJSService, exposing most of its public members.

With the static API, you don't need to worry about creating or managing INodeJSService. Example usage;

string result = await StaticNodeJSService
    .InvokeFromStringAsync<Result>("module.exports = (callback, message) => callback(null, message);", args: new[] { "success" });

Assert.Equal("success", result);

StaticNodeJSService's invoke methods are thread-safe.

Clean-up wise, StaticNodeJSService.DisposeServiceProvider() kills the NodeJS process immediately. Alternatively, the NodeJS process kills itself when it detects that its parent process has died.

Whether you use the static API or the DI-based API depends on your development needs. If you're already using DI and/or you want to mock out INodeJSService in your tests and/or you want to customize services, use the DI-based API. Otherwise, the static API works fine.

Invoking Javascript

We'll begin with the javascript side of things. You'll need a NodeJS module that exports either a function or an object containing functions. Exported functions can be of two forms:

Function With Callback Parameter

These functions take a callback as their first argument, and call the callback when they're done.

The callback takes two optional arguments:

  • The first argument is an error or an error message. It must be of type Error or string.
  • The second argument is the result. It must be a JSON-serializable type, a string, or a stream.Readable.

Note: this is known as an error-first callback. Such callbacks are used for error handling in NodeJS asynchronous code (check out NodeJS Event Loop for more information on asynchrony in NodeJS).

As mentioned before, you'll need a module that exports either a function or an object containing functions. This is a module that exports a valid function:

module.exports = (callback, arg1, arg2, arg3) => {
    ... // Do something with args

    callback(null /* error */, result /* result */);
}

This is a module that exports an object containing valid functions:

module.exports = {
    doSomething: (callback, arg1) => {
        ... // Do something with arg

        callback(null, result);
    },
    doSomethingElse: (callback) => {
        ... // Do something else

        callback(null, result);
    }
}

If an error or error message is passed to the callback, it's sent back to the calling .NET process, where an InvocationException is thrown.

Async Function

Async functions are the second valid function form. They're syntactic sugar for the function form described in the previous section (check out Callbacks, Promises and Async/Await for a summary on how callbacks, promises and async/await are related).

This is a module that exports a valid function:

module.exports = async (arg1, arg2) => {
    ... // Do something with args

    return result;
}

And this is a module that exports an object containing valid functions:

module.exports = {
    doSomething: async (arg1, arg2, arg3, arg4) => {
        ... // Do something with args

        // async functions can explicitly return promises
        return new Promise((resolve, reject) => {
            resolve(result);
        });
    },
    doSomethingElse: async (arg1) => {
        ... // Do something with arg
            
        return result;
    }
}

If an error is thrown in an async function, the error message is sent back to the calling .NET process, where an InvocationException is thrown:

module.exports = async () => {
    throw new Error('error message');
}

Invoking Javascript From a File

Now that we've covered the javascript side of things, let's invoke some javascript from C#.

If you have a javascript file named exampleModule.js (located in [NodeJSProcessOptions.ProjectPath](#nodejsprocessoptionsprojectpa

Related Skills

View on GitHub
GitHub Stars483
CategoryDevelopment
Updated1d ago
Forks44

Languages

C#

Security Score

85/100

Audited on Apr 1, 2026

No findings