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:
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:
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
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:
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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.