Node.js Folder Path Using __dirname in Es Module

Photo by RetroSupply on Unsplash

Node.js Folder Path Using __dirname in Es Module

ยท

5 min read

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 file

    package.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 command touch 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 command touch 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 in package.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