Supporting ES6 import on Windows App Service (Node.js/IISNode)
Published Sep 28 2022 02:08 AM 8,268 Views
Microsoft

This article is intended to guide you in supporting ES6 Modules import on Node.js on Windows App Service and also an introduction to ES modules as well as ES import.

 

Node.js has two module systems:

1)  CommonJS modules and

2)  ECMAScript modules.

 

1) CommonJS: The de facto standard for modules in Node.js currently  is CommonJS. CommonJS modules are defined in normal .js files using module.exports or .cjs.

Modules can be used later within other .js files with the require() function.

Import using require (Common JS) :

 

 

// foo.js
module.exports = function() {
  return 'Hello foo!';
}

// index.js
var foo = require('./foo');
console.log(foo()); // Hello foo!

 

 

 

to run this example:

 

 

 

node index.js

 

 

 

2) Import using ES modules: Since Node v8.5, developers have been able to run variations of support for the ES modules specification using the --experimental-modules flag. These modules can be defined in either .mjs files or .js files also by adding { "type": "module" } in the  package.json.

For example: Using .mjs extension:

 

 

 

// foo.mjs
export function foo() {
  return 'Hello foo!';
}

// index.mjs
import { foo } from './foo.mjs';
console.log(foo()); // Hello foo!

 

 

 

to run this sample example use following startup command:

 

 

 

node --experimental-modules index.mjs

 

 

 

Alternatively we can use .js extension normally  by adding { "type": "module" } in the nearest package.json  & run above example code normally without experimental module flag ( Node.js >=12.17)

 

Please refer to following table for support of ES Modules:

 

shuanand_1-1664283021261.png

How to enable ES6 Modules import on Node.js on Windows App Service:

1. In case of Windows App Service, iisnode uses interceptor.js to pull entrypoint from the web.config file and if we use ES Modules in our sample server.js file as:

server.js

 

 

import express from 'express';
  
const app = express();
  
app.get('/',(req,res) => {
const body = "testdata"
// Adds header
res.append('Content-Type', 'text/plain;charset=utf-8');
res.append('Connection', 'keep-alive')
res.append('Set-Cookie', 'divehours=fornightly')
res.append('Content-Length', Buffer.byteLength(body, 'utf-8'));
res.status(200).send(body);
})
  
const PORT = process.env.PORT||3000;
  
app.listen(PORT,() => {
    console.log(`Running on PORT ${PORT}`);
})

 

 

package.json

 

 

{
    "name": "index",
    "version": "1.0.0",
    "description": "",
    "main": "server.js",
    "type": "module",
    "scripts": {
      "test": "echo \"Error: no test specified\" && exit 1",
      "start": "node --experimental-modules server.js"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "dependencies": {
        "express": "^4.18.1"
    }
  }

 

 

 

The web.config file corresponding to above reference is:

 

 

 

<?xml version="1.0" encoding="utf-8"?>
<!--
     This configuration file is required if iisnode is used to run node processes behind
     IIS or IIS Express.  For more information, visit:

     https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config
-->

<configuration>
  <system.webServer>
    <!-- Visit http://blogs.msdn.com/b/windowsazure/archive/2013/11/14/introduction-to-websockets-on-windows-azure-web-sites.aspx for more information on WebSocket support -->
    <webSocket enabled="false" />
    <handlers>
      <!-- Indicates that the server.js file is a node.js site to be handled by the iisnode module -->
      <add name="iisnode" path="server.js" verb="*" modules="iisnode"/>
    </handlers>
    <rewrite>
      <rules>
        <!-- Do not interfere with requests for node-inspector debugging -->
        <rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
          <match url="^server.js\/debug[\/]?" />
        </rule>

        <!-- First we consider whether the incoming URL matches a physical file in the /public folder -->
        <rule name="StaticContent">
          <action type="Rewrite" url="public{PATH_INFO}"/>
        </rule>

        <!-- All other URLs are mapped to the node.js site entry point -->
        <rule name="DynamicContent">
          <conditions>
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
          </conditions>
          <action type="Rewrite" url="server.js"/>
        </rule>
      </rules>
    </rewrite>
    
    <!-- 'bin' directory has no special meaning in node.js and apps can be placed in it -->
    <security>
      <requestFiltering>
        <hiddenSegments>
          <remove segment="bin"/>
        </hiddenSegments>
        <requestLimits maxAllowedContentLength="4294967295"/>
      </requestFiltering>
    </security>

    <!-- Make sure error responses are left untouched -->
    <httpErrors existingResponse="PassThrough" />

    <!--
      You can control how Node is hosted within IIS using the following options:
        * watchedFiles: semi-colon separated list of files that will be watched for changes to restart the server
        * node_env: will be propagated to node as NODE_ENV environment variable
        * debuggingEnabled - controls whether the built-in debugger is enabled

      See https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config for a full list of options
    -->
    <!--<iisnode watchedFiles="web.config;*.js"/>-->
  </system.webServer>
</configuration>

 

 

On browsing the application with referenced server.js as entrypoint on Windows App Service, the application will throw HTTP 500 Error as shown below:

es_6.png

 

2.  To enable logging to check application errors, create a new file called iisnode.yml at D:\home\site\wwwroot folder and add following line in it and save it:

 

 

 

loggingEnabled: true

 

 

 

Then, we can notice the below error at following location:  

 

 

 

D:\home\site\LogFiles\Application\logging-errors.txt

 

 

 

error_app.png

 

 

 

The reason that we see this error is because we have specified  { "type": "module" } in the  package.json which defines all .js files in that package scope as ES modules while interceptor.js from iisnode expects a Common JS File as entrypoint as shown in error above.

 

3. This error can be solved by adding a new file next to your entrypoint file (server.js) and configuring it as the iisnode's entry point ( in web.config). Let's call the new file run.cjs and put only the following line into it:

 

 

 

import("./server.js");

 

 

 

The cjs file extension is important because it tells Node that this file is not a ES module, as it would expect because of "type": "module" in the package.json. It allows other CommonJS files to include our new file - namely iisnode's interceptor.js. It again imports the server.js which then runs fine as ES module.

 

Here is the modified sample code using es6 modules on iisnode (Windows App Service):

run.cjs:

 

 

 

import("./server.js");

 

 

 

server.js:

 

 

 

import express from 'express';
  
const app = express();
  
app.get('/',(req,res) => {
const body = "testdata"
// Adds header
res.append('Content-Type', 'text/plain;charset=utf-8');
res.append('Connection', 'keep-alive')
res.append('Set-Cookie', 'divehours=fornightly')
res.append('Content-Length', Buffer.byteLength(body, 'utf-8'));
res.status(200).send(body);
})
  
const PORT = process.env.PORT||3000;
  
app.listen(PORT,() => {
    console.log(`Running on PORT ${PORT}`);
})

 

 

 

web.config:

 

 

 

<?xml version="1.0" encoding="utf-8"?>
<!--
     This configuration file is required if iisnode is used to run node processes behind
     IIS or IIS Express.  For more information, visit:
     https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config
-->

<configuration>
  <system.webServer>
    <!-- Visit http://blogs.msdn.com/b/windowsazure/archive/2013/11/14/introduction-to-websockets-on-windows-azure-web-sites.aspx for more information on WebSocket support -->
    <webSocket enabled="false" />
    <handlers>
      <!-- Indicates that the run.cjs file is a node.js site to be handled by the iisnode module -->
      <add name="iisnode" path="run.cjs" verb="*" modules="iisnode"/>
    </handlers>
    <rewrite>
      <rules>
        <!-- Do not interfere with requests for node-inspector debugging -->
        <rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
          <match url="^run.cjs\/debug[\/]?" />
        </rule>

        <!-- First we consider whether the incoming URL matches a physical file in the /public folder -->
        <rule name="StaticContent">
          <action type="Rewrite" url="public{PATH_INFO}"/>
        </rule>

        <!-- All other URLs are mapped to the node.js site entry point -->
        <rule name="DynamicContent">
          <conditions>
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
          </conditions>
          <action type="Rewrite" url="run.cjs"/>
        </rule>
      </rules>
    </rewrite>
    
    <!-- 'bin' directory has no special meaning in node.js and apps can be placed in it -->
    <security>
      <requestFiltering>
        <hiddenSegments>
          <remove segment="bin"/>
        </hiddenSegments>
        <requestLimits maxAllowedContentLength="4294967295"/>
      </requestFiltering>
    </security>

    <!-- Make sure error responses are left untouched -->
    <httpErrors existingResponse="PassThrough" />

    <!--
      You can control how Node is hosted within IIS using the following options:
        * watchedFiles: semi-colon separated list of files that will be watched for changes to restart the server
        * node_env: will be propagated to node as NODE_ENV environment variable
        * debuggingEnabled - controls whether the built-in debugger is enabled
      See https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config for a full list of options
    -->
    <!--<iisnode watchedFiles="web.config;*.js"/>-->
  </system.webServer>
</configuration>

 

 

package.json:

 

 

 

{
    "name": "index",
    "version": "1.0.0",
    "description": "",
    "main": "server.js",
    "type": "module",
    "scripts": {
      "test": "echo \"Error: no test specified\" && exit 1",
      "start": "node server.js"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "dependencies": {
        "express": "^4.18.1"
    }
  }

 

 

 

4.  On browsing the application now with run.cjs as entrypoint on Windows App Service, the application with ES Modules will work properly:

 

windows_app_service.png

Please refer to the below article for more reference:

https://nodejs.org/api/esm.html#esm_package_json_type_field

If you have any questions or feedback, please add a comment.

1 Comment
Co-Authors
Version history
Last update:
‎Sep 30 2022 01:23 AM
Updated by: