I have some code to offer to start the node process(es) with custom arguments, e.g.
--prof. However, I'd first like to highlight the following bits of information about GraphQL and JSS that might provide some insight.
Sitecore GraphQL includes some built-in diagnostics features that can help diagnose API performance issues:
When to use Integrated GraphQL queries: https://jss.sitecore.com/docs/fundamentals/services/graphql#integrated-graphql
vs When to use Connected GraphQL queries: https://jss.sitecore.com/docs/fundamentals/services/graphql#connected-graphql
For non-CM environments, you may want to consider using Headless SSR mode instead of Integrated Mode for high-traffic sites as recommended here:
Ok, with that out of the way, you need to extend some of the existing JSS Node invocation code. Admittedly, this is more difficult than it should be and perhaps is something we can look at addressing for the next release.
First, extend the
type where you can override the method that creates the Node invocation information. This is where you can add the
public class CustomHttpNodeInstance : HttpNodeInstance
public CustomHttpNodeInstance(NodeServicesOptions options, int port = 0) : base(options, port)
protected override ProcessStartInfo PrepareNodeProcessStartInfo(string entryPointFilename, string projectPath, string commandLineArguments,
IDictionary<string, string> environmentVars, bool launchWithDebugging, int debuggingPort)
var startInfo = base.PrepareNodeProcessStartInfo(entryPointFilename, projectPath, commandLineArguments,
environmentVars, launchWithDebugging, debuggingPort);
// You might ask why you can't simply add custom arguments to the `commandLineArguments` parameter.
// The answer is that the `commandLineArguments` value is added to the node.exe command _after_ the script name/path that will be executed by node.
// However, the `--prof` flag must be placed _before_ the script name/path that will be executed by node.
// Therefore, we invoke the base `PrepareNodeProcessStartInfo` method to generate the command arguments,
// then prepend the arguments string with `--prof`.
startInfo.Arguments = "--prof " + startInfo.Arguments;
Next, create a custom class that implements the
HttpNodeInstance (defined above).
public class CustomNodeServicesFactory : INodeServicesFactory
public INodeServices CreateNodeServices(NodeServicesOptions options)
if (options == null)
throw new ArgumentNullException(nameof(options));
// Use our `CustomHttpNodeInstance` when creating Node instances.
options.NodeInstanceFactory = () => new CustomHttpNodeInstance(options);
return new DefaultNodeServices(options.NodeInstanceFactory);
Next, you need to register your custom
CustomNodeServicesFactory as the implementation of
INodeServicesFactory that will be served by the DI container.
public class CustomRegisterDependencies : IServicesConfigurator
public void Configure(IServiceCollection serviceCollection)
// Remove the existing `INodeServicesFactory` service
var toRemove = serviceCollection.FirstOrDefault(d => d.ServiceType == typeof(INodeServicesFactory));
// Add the custom service
And lastly, you need a config patch to tell Sitecore to include your custom DI registration (be sure to replace
MyLibrary with your relevant values).
Now, when you browse to your JSS app in Integrated Mode, Node will generate a profiler log file. However, it will be empty/locked until the Node process(es) used by your JSS app exit. Therefore, you must kill the
w3wp.exe process for your Sitecore instance or kill the
node.exe process(es) attached to the
w3wp.exe process in order for the profiler output to be finalized and written.
The profiler output file, e.g.
isolate-xxxxx-v8.log, will be generated in the folder next to your
Note: depending on your JSS Node pooling configuration, multiple Node instances may be used by your app when load testing. Therefore, there may be multiple profile log files.