DEV Community

Cover image for HOW TO SEARCH FOR FILES WITH USER-INPUT (Node.js)
Patrick
Patrick

Posted on

HOW TO SEARCH FOR FILES WITH USER-INPUT (Node.js)

Node.js is an event-driven, JavaScript runtime which can
perform operations asynchronously in a such a way that is non-blocking in execution sense. These operations could include: FS/IO, HTTP operations etc. Node wraps the JavaScript language with extra rich features which enables the language perform operations that it can't do by default such as: reading and writing to files locally, running a full-fledged end-to-end network communication; as JavaScript's scope of operation is limited to the browser window. Hence, Node can allow us run JavaScript in environments other than the restricting interface of the browser.

In this article we will see how to search for existing files within a local directory via user-input in Node. At the end of this article, you should be familiar with the node and its

fs and readline modules and how to use its associated methods to find existing files in our directory.

Setup:

First off, we auto create a package.json file that will help us manage our dependencies and other things such as versioning in the future.
So on your terminallocked in on the root of the project directory, type the code:
npm init –y or yarn init -y
This generates the package file. So, we can now install npm packages such as nodemon which refreshes our node sessions each time we make and save a change to our files.
Finally, we would update the package.json file by adding a type: "module" field to the file. This would enable us use the more modern and elegant ES2015(ES6) syntax in our node project(this is optional, as you could choose to use commonjs require syntax). Exciting huh ?
Finally, we create an index.js file where we will write and test all our code.

To serve as our dummy files to be read, we would create a folder called blogs in our projects directory, there we will create three(3) text files - (text1.txt, text2.txt, text3.txt), and for each of these files we populate them with our preferred dummy data- Lorem Ipsum!
You could also use a more meaningful data for this, its your choice. Your folder structure, and package.json file should be looking similar to mine below:

package.json

{
  "name": "Nodescratch",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
  "start": "nodemon index.js"
},
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "nodemon": "2.0.2"
  }
}
Enter fullscreen mode Exit fullscreen mode

Ok, back to Node:

Nodejs has a couple of built-in core modules that can be used for various important operations, these includes: HTTP, Events, FS, etc. We would be working with the FS module (FS stands for File System; quite self-explanatory if you ask me ).
The FS module has some important helper methods that we can use to make manipulations on both local files on our machine and beyond. For this article, we would import and use three of these methods readFile, readdir, also we would import another
method from the readline module called readline. This module allows us to read/accept data from the user via an interface:

import readline from "readline";
import { readFile, readdir } from "fs";
const { log, error } = await import('console') /* this is to avoid typing console.log or console.error redundantly */

Enter fullscreen mode Exit fullscreen mode

To get users input, we would be using the readline method which provides an interface for reading data from a readable stream, the most common being –
process.stdin.
This operation takes some time to complete and hence traditionally
we would often use a callback function to handle the response once it’s done, but we would be making our codes more Promise-based by using the promisify method of nodes community module called util (I would definitely talk about that in depth in another article). So, lets how that is used in code:

import readline from "readline";
import { readFile, readdir } from "fs";
const { log, error } = await import('console')

import util from "util";
const rFile = util.promisify(readFile);
const rDir = util.promisify(readdir);
Enter fullscreen mode Exit fullscreen mode

The readFile asynchronously reads the entire contents of any file. This method takes two arguments, the relative path to the file and a callback function to handle response. But we would be using a rather modern approach by transforming this method to Promise-based method like the one above using promisify.
Inclusively, the readdir method can read the contents of a directory (folder) and just like the readFile method, readdir takes two arguments: a relative path to the
directory and a callback. Just like before, we would promisify this method.
Doing this enables us to elegantly use these methods with the await keyword in an async function as depicted below:

import readline from "readline";
import { readFile, readdir } from "fs";
const { log, error } = await import('console')
import util from "util";

const rFile = util.promisify(readFile);
const rDir = util.promisify(readdir);
async function get() {
    try {
        const file = await rDir("./blogs");
        setTimeout(() => log("Available files are:",file),3000);
    } catch (e) { error(e.message);}
}

Enter fullscreen mode Exit fullscreen mode

Another function we would define is the search function which would listen to our inputs, formats its and make search operations asynchronously based on this input.
But before we define this function, we need to create an interface that would let us key in inputs and also get outputs logged to the console. This function would be called later when we define an interface for accepting user input. Lets look at how the function is defined:

async function search(userInput) {
  try {
    const data = await rFile(`${userInput}`);
    log(data.toString());
  } catch (e) {
    error(e.message);
  }
}
Enter fullscreen mode Exit fullscreen mode

Finally, we should note that the question method of the readline can be used to throw a query or question a user to perform an input operation. It should also have a callback function that grabs the input and runs an operation using the input as a payload; thus when called, rl.question() will resume the input stream if it has been paused.
Note, the callback passed to rl.question does not follow the typical pattern of accepting an ‘err & data’ as arguments, rather it’s called with the provided answer (user-input) as the only argument. Lets see how that’s done:

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});
rl.question("Search for a file..\n", (input) => {
  const userInput = input.trim();
  search(userInput); // the 'search function is called at this point
  get(); // the 'get function is called after the search
  log(`You searched for ${userInput}`);
  rl.close();
});
rl.on("close", () => {
  log("Searching....");
});

Enter fullscreen mode Exit fullscreen mode

To execute this file and get our data using our input, open the Terminal, using the node runtime we would execute the module by typing - node index.js.
We would get a prompt that tells us to Search for a file….
Enter the keywords - blogs/text1.txt, which is the name of the dummy files we created earlier (Don’t forget to include the .txt extension).
And we should get a satisfactory result of -

You searched for blogs/text1.txt
Searching....
// data is output after some secs.
Enter fullscreen mode Exit fullscreen mode

After a moment you should get your file served to you beautifully on the Terminal, hurray.

Summary:

Now, let’s have a summary of everything we’ve done so far. All we did was use a fancy syntax to get our users input via the readline interface. A function is executed that uses the readFile and readdir method to search for and beautifully return our files content on the Terminal.

If you made it to the end of this article, Congrats! Now you have a task of making this even more better and elegant than I could.
Keep learning, keep discovering and keep sharing.

Ping my Twitter handle when you can- Patrick

Top comments (0)