Table of contents
BACKGROUND
So, I was writing a simple Node.js server application where I needed to access an html file in my root directory. This file was to be sent as a response to an http request. I believe if you have written any Node.js application at all you must have definitely done this.
RECREATE THE SCENARIO
- Go to a favorable directory on your machine, create the project directory,
cd
into it and initialize a node.js app with a package.json file using the command:mkdir node-app && cd node-app && npm init -y
- And this is what my
package.json
file looks like:
package.json
"name": "app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
Let us install a few necessary package for a Node.js server :
npm i express nodemon
We can now set our start script in the package.json. We will be running it with nodemon which installed to trigger a restart whenever we make changes to our code. Below is our new
package.json
filepackage.json "name": "app", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "nodemon index.js" }, "keywords": [], "author": "", "license": "ISC"
Create a javascript file named
index.js
in the root of the project. You can either create it manually from your code editor or use the commandtouch index.js
to create the file in the root of your project. And populate with the code:
index.js
const express = require("express");
const port = process.env.PORT || 7070;
const path = require("path");
const app = express();
// we want to respond with a file upon GET request on the default route "/"
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "/index.html"));
});
app.listen(port, () => {
console.log(`Server started on port port: ${port}`);
});
Note: the __dirname used can help us get the path of the folder containing the current javascript file, while the
path
module gives us access to the system file path
- Create an html file named
index.html
in the root of the project. You can either create it manually from your code editor or use the commandtouch index.html
to create the file in the root of your project.
- Populate the file with this simple html boilerplate code:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>Hello world</h1>
</body>
</html>
Now type the command
npm run start
and hit enter. You should get this on your terminal:app@1.0.0 start nodemon index.js [nodemon] 2.0.20 [nodemon] to restart at any time, enter `rs` [nodemon] watching path(s): *.* [nodemon] watching extensions: js,mjs,json [nodemon] starting `node index.js` Server started on port: 7070
- Now open a browser tab and visit
localhost:7070
and you should get the "Hello world" text from the index.html file. All looking good ๐ฅณ
At this point I need to point that our JavaScript file is a CommonJs file because the type field in our
package.json
file is not set hence it defaults to CommonJs. Now to the main reason why we are here, let us change our js file to Es Module file by setting the type field inpackage.json
to module so it looks like this:
package.json
"name": "app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type":"module",
"scripts": {
"start": "nodemon index.js"
},
"keywords": [],
"author": "",
"license": "ISC"
We should get an error like this after saving our changes:
ReferenceError: require is not defined in ES module scope, you can use import instead
This means we need to change our we import our packages in the index.js
file, so we have this:
index.js
import express from "express";
import path from "path";
const port = process.env.PORT || 7070;
const path = require("path");
const app = express();
// we want to respond with a file upon GET request on the default route "/"
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "/index.html"));
});
app.listen(port, () => {
console.log(`Server started on port port: ${port}`);
});
That ReferenceError must have disappeared now and the server running again on port 7070.
Now let us try to refresh our browser to see the "Hello world" again but no we get another error: ReferenceError: __dirname is not defined
๐ข
Now it says __dirname
is not defined hence we cannot access the path to our index.html
file containing the "Hello world" text. The issue here is that __dirname cannot be accessed directly in an Es Module and there is a number of steps to be taken.
We need to import a function fileURLToPath
from the node.js url package then we can use __dirname like so:
index.js
import express from "express";
import path from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const port = process.env.PORT || 7070;
const path = require("path");
const app = express();
// we want to respond with a file upon GET request on the default route "/"
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "/index.html"));
});
app.listen(port, () => {
console.log(`Server started on port port: ${port}`);
});
Our server must have restarted once we save the changes. Now if we refresh our browser we should get the "Hello world" text back. Congratulations, we are done ๐ฅณ ๐๐ฟ. ๐
I got to know of this solution from an article by flaviocopes
Conclusion
As a lot of us fancy the Es Module system we need to understand the differences that exist from the default CommonJs system.
I hope you find this helpful and kindly share if you have another solution. Thanks