Categories
JavaScript JavaScript Basics

More JavaScript Array Tips — Removing Falsy Values, Get Random Array Entry, and Reversing Arrays

JavaScript, like any other programming languages, have many handy tricks that let us write our programs more easily. In this article, we will look at how to do different things that involve arrays, like removing falsy values, getting random values from an array, and reversing arrays.

Removing Falsy Values

In JavaScript, falsy values include 0, empty string, null , undefined , false and NaN . We often run into these values when we’re processing arrays and they may cause errors.

Values like null , undefined , and NaN often are unexpected and it’s easier if we eliminate them from an array before doing anything else to them.

We can do this with a few ways which involve using the filter method that is available to arrays. The filter method takes in a callback function.

The callback function that has 2 parameters, the first one is an array entry and the second one is the index of the array that’s being iterated to by the filter method. The filter method filter let us defined what we want to keep and return a new array with the entries kept according to the boolean expression we return.

Any function that’s defined that takes at least the value and returns a boolean value can be used as the callback function, so one way we can use the filter method to filter out is to use the Boolean factory function, which takes in any object in its first argument and returns true if it’s truthy and false if it’s falsy. If true is returned from the callback function then the item will be kept.

Otherwise, it will discard the value in the array it returns. For example, we can use the Boolean function by passing in straight into the filter method like we do in the following code:

let arr = [0, 'yellow', '', NaN, 1, true, undefined, 'orange', false];  
arr = arr.filter(Boolean);  
console.log(arr);

Then we get the following output from the console.log :

["yellow", 1, true, "orange"]

We can also use the exclamation mark operator twice to coerce truthy values to true and falsy values to false . We just put the exclamation 2 times in front of value to do this, so we can use the operator like in the following code to filter out falsy values:

let arr = [0, 'yellow', '', NaN, 1, true, undefined, 'orange', false];  
arr = arr.filter(a => !!a);  
console.log(arr);

If we run the code above, we should get the same thing as we did before. Likewise, we can pass in a function that returns the value from the Boolean function instead of passing in Boolean function directly to the filter method to make the expression more clear:

let arr = [0, 'yellow', '', NaN, 1, true, undefined, 'orange', false];  
arr = arr.filter(a => Boolean(a));  
console.log(arr);

We should get the same thing as we did before if we run the code above.

Get a Value Randomly from an Array

To get a random value from an array, we can use the Math.random method to get a random index in addition to some other code.

We more code than just calling the Math.random method because it only returns a random number between 0 and 1, which means that it returns a decimal number between 0 and 1, which we don’t want.

This means that it has to be multiplied by our array’s length to get a value between 0 and the length of our array. Then we need call Math.floor on that result to round it down to the nearest integer.

We can use the Math.random method like in the following code:

let arr = ['yellow', 'orange', 'blue', 'purple', 'green'];  
const chosenValue = arr[(Math.floor(Math.random() * (arr.length)))]  
console.log(chosenValue);

The code above generates the index by running Math.random() * (arr.length) , which will range from 0 to the length of the array. Then we round it down with the Math.floor method which takes a number that we want to round down so that we get the number rounded down to the nearest integer since we don’t want non-integer numbers.

Then we can pass that into the brackets to get the index that was generated.

Reversing an Array

We can reverse an array by using the reverse method that comes with JavaScript array objects. It takes no arguments and reverses an array in place. For example, we can use it as in the following code:

let arr = ['yellow', 'orange', 'blue', 'purple', 'green'];  
arr.reverse();  
console.log(arr);

Then we get:

["green", "purple", "blue", "orange", "yellow"]

from the console.log output above. The reverse method works with any primitive objects in addition to objects. For example, if we have:

let arr = [{  
  a: 1  
}, {  
  b: 2  
}, {  
  c: 3  
}];  
arr.reverse();  
console.log(arr);

Then we get back the following output from the console.log :

[  
  {  
    "c": 3  
  },  
  {  
    "b": 2  
  },  
  {  
    "a": 1  
  }  
]

The reverse method also works with any object that has numerical keys. For example, if we have:

let arrLikeObj = {  
  0: 'a',  
  1: 'b',  
  2: 'c',  
  length: 3  
}  
Array.prototype.reverse.call(arrLikeObj);  
console.log(arrLikeObj);

Then we get back the following output from the console.log :

{  
  "0": "c",  
  "1": "b",  
  "2": "a",  
  "length": 3  
}

As long as the object has numerical keys starting from 0 and increments by 1 from there and a length property, the code above will work. We can also use the apply method to do the same thing like in the following code:

let arrLikeObj = {  
  0: 'a',  
  1: 'b',  
  2: 'c',  
  length: 3  
}  
Array.prototype.reverse.apply(arrLikeObj);  
console.log(JSON.stringify(arrLikeObj, (key, value) => value, 2));

Then once again we get back the following output from the console.log :

{  
  "0": "c",  
  "1": "b",  
  "2": "a",  
  "length": 3  
}

Conclusion

In JavaScript, falsy values include 0, empty string, null , undefined , false and NaN . We often run into these values when we’re processing arrays and they may cause errors. Values like null , undefined , and NaN often are unexpected and it’s easier if we eliminate them from an array before doing anything else to them.

We can do this with a few ways which involve using the filter method that are available to arrays.

We can use the Boolean function or applying the exclamation mark twice to convert an object into a boolean value. To reverse an array, we can use call the reverse method on an array to reverse it.

The reverse method also works with any object that has numerical keys starting from 0 and increments by 1 and a length property. We can get a random entry from an array by using Math.random multiplied by the array’s length and Math.floor methods.

Categories
JavaScript

Why JavaScript Arrow Functions are Useful?

Since ES6, there are two types of general-purpose functions. One is the traditional function declared with the function keyword. The other is the newer arrow function introduced with ES6.

In this article, we’ll look at why arrow functions are useful and when we should use them.

Defining Arrow Functions

Arrow functions are functions that have the fat arrow. For example, we can declare them as follows:

const fn = () => 'foo';

The code above will define the fn function and return the string 'foo'.

Multi-line functions can be written as follows:

const add = (a, b) => {  
  return a + b;  
}

The code above adds two numbers together.


Syntactic Sugar

Arrow functions have a less verbose syntax than traditional functions. As we can see from the first example above, we can define functions in one line. Return is implicit if the function is one line.

Also, we don’t have to keep writing the function keyword to declare functions.


We Don’t Have to Worry About ‘this’

It doesn’t have its own this. This means that we don’t have to worry about the value of this inside it.

This is a good thing in most cases except when we want to write class methods or constructor function methods. In all other cases, we really don’t care about this if we use arrow functions instead of functions.

Also with traditional functions, if we want to access the value of this inside of the outer function in the inner function, we have to access the value as follows:

In the code above, we want to access this.bar from within the foobar function. We have to do it by writing:

var that = this;

And in the foobar function, we write:

console.log(that.bar);

This is a pain and it’s easy to forget.

If we use arrow functions, we can eliminate both lines and write:

This is much better since we need to write less code and we don’t have to worry about the value of this inside the arrow function.

In addition, we can’t use bind, apply, or call to change the value of this inside an arrow function since this can’t change inside an arrow function.

This is a nice characteristic for functions that don’t use this, as lots of confusion is removed.


Hoisting and Function Declaration vs. Expression

There are two ways to define traditional functions in JavaScript.

One is function declaration and the other is function expression. Function declarations are loaded before any code is run, while function expressions load when the JavaScript interpreter loads the code.

An example of a function declaration is below:

function foo() {  
  return 1;  
}

An example of a function expression is below:

const foo = function() {  
  return 1;  
}

Since function declarations are loaded before anything else runs, they can be referenced in any part of a code file with the function declaration. This is called hoisting.

Function expressions aren’t available until the code for defining the function is run.

This difference is often overlooked and it causes confusion easily. We can remove this confusion by using arrow functions since we don’t have to worry about hoisting, function declarations, and function expressions.

With arrow functions, we can only define the functions as we did above, and it’s never available before it’s declared.


Arguments Object

Arrow functions don’t have their own arguments object. The arguments object is an array-like object that has all the arguments that we pass into the function call.

arguments is an array-like object, so it has indexes, bracket notation for accessing its entries, and the length property. And, we can loop through the items with the for loop, but other than that, it’s nothing like an array.

This is another source of confusion since it’s kind of like an array but not really an array. With arrow functions, we can use the rest parameters instead, which does give us an array.

For instance, we can write:

const foo = (...args) => {  
  return args;  
}

Then when we call foo as follows:

foo(1, 2, 3, 4, 5)

We get:

[1, 2, 3, 4, 5]

Which is a real array, so we can use array methods and the spread operator with it. This is much better than the confusing arguments object that we had to use to get dynamic parameter values, before arrow functions and the rest operator existed.


Conclusion

As we can see, arrow functions are quite useful except for using them as class methods or constructor function methods. Lots of confusion is eliminated with this, hoisting, function declarations vs. expressions, etc.

Also, it’s much cleaner in terms of syntax since we don’t have to use the function keyword to declare functions and return is implicit in one-line functions.

Also, we don’t have to be confused about the arguments object anymore since arguments doesn’t bind to arrow functions.

Categories
JavaScript JavaScript Basics

Why it’s time to ditch the for…in Loop in JavaScript?

Since ES5.1, we have new features for iterating through the properties of an object. In the past, we only had the for...in loop to accomplish this.

In this article, we’ll look at why the new ways to loop through the properties of an object are much better than the for...in loop. This includes the Object.keys method, which is available since ES5.1, but updated in ES6 to support Symbols, and the Object.entries and Object.values methods new to ES2017. To loop through Symbol keys, we have the Reflect.ownKeys method.

Characteristics of the for…in Loop

The for...in loop lets us iterate over the non-Symbol, enumerable properties of an object.

It will iterate over the properties of an object in arbitrary order. Therefore, we can’t depend on the order of which the keys of an object are iterated over.

The properties that are deleted with the delete keyword won’t be iterated over. If a property is modified during iteration, then the value when it’s visited later will be the value after the change is done.

Modifying an object’s properties during iteration is a bad idea since it creates confusion because of the characteristics mentioned above.

Some people also use it for iterating through arrays since the for...in will get the indexes of an array like the keys of an object. However, this is again a bad idea since the order isn’t guaranteed.

Also, it iterates over its own properties and its prototype’s properties. For example, if we have:

const person = {  
  name: 'Joe'  
};  
const employee = {  
  title: 'waiter'  
};

employee.__proto__ = person;  
for (let prop in employee) {  
  console.log(prop);}

Then we see both name and title logged from the console.log in the for...in loop.

We might not want this, so we have to use the hasOwnProperty method from an object’s prototype to check if the property is actually an object’s own property as follows:

const person = {  
  name: 'Joe'  
};  
const employee = {  
  title: 'waiter'  
};

employee.__proto__ = person;  
for (let prop in employee) {  
  if (employee.hasOwnProperty(prop)) {  
    console.log(prop);  
  }  
}

As we can see, without hasOwnProperty, the for...in loop loops through all the own and inherited properties of an object.

This isn’t very convenient since it’s often not what we want.

Also, since the for...in loop was introduced before ES6 and was never updated, it’s not aware of Symbol property keys, so if we have the following object:

const foo = {  
  [Symbol('foo')]: 'Joe'  
};

When we try to loop through the foo object as follows:

for (let prop in foo) {  
  console.log(prop);  
}

We get nothing.

for…in Loop’s Performance

The for...in loop is also slow. This is because the loop has to check if each property is enumerable all the way up the prototype chain and then return the name of each enumerable property.

Getting it to work with arbitrary objects is tough because the structure isn’t constant. It has to check each key and its prototype’s keys to do this for each property. Therefore, a lot more computation is involved than a simple for loop.

Of course, the bigger the array or object, the bigger this issue is, but this is still something to keep in mind for those situations.

New Ways to Iterate Through Properties of an Object

Object.keys

Since ES5.1, we have the Object.keys method to get the keys of an object and return them as an array.

The keys are returned in the following order. First are keys with integer indexes in ascending numeric order. Then all other string keys in the order which they’re added to the object.

Unlike the for...in loop, there’s some reasoning to the order in which the keys are returned. And since Object.keys returns an array, we can sort it however we like.

We can also use the for...of loop or forEach method to write a cleaner loop.

Also, we don’t have to use hasOwnProperty to check if an object’s properties are its own properties anymore.

Since Object.keys is released with ES6, Symbols are supported.

For example, given that we have the same inherited object as the above example:

const person = {  
  name: 'Joe'  
};  
const employee = {  
  title: 'waiter'  
};

employee.__proto__ = person;

We can loop through it as follows:

for (let prop of Object.keys(employee)) {  
  console.log(prop);  
}

We only get back 'title' from the loop above.

The ordering can be seen as follows. If we have the following object:

const foo = {  
  a: 1,  
  b: 2,  
  1: 'a',  
  2: 'b',  
  3: 'c',  
};

When we loop through the keys with:

for (let prop of Object.keys(foo)) {  
  console.log(prop);  
}

Then we get back:

1  
2  
3  
a  
b

Object.values

To get the values from an object’s without the inherited properties, we can use the Object.values() method.

The order of iteration is the same as Object.keys , but we get back the values instead of the keys.

For example, given the following objects:

const person = {  
  name: 'Joe'  
};  
const employee = {  
  title: 'waiter'  
};  
employee.__proto__ = person;

We can loop through the own values of employee without the person ‘s property values as follows:

for (let val of Object.values(employee)) {  
  console.log(val);  
}

We should only get 'waiter' from the console.log .

The Object.values method is new to ES2017.

Object.entries

To get the key-value pair in one loop, we can use the Object.entries method. It takes an object as an argument like the other methods we mentioned above.

The method returns an array that has the key-value pair of its own properties in an array.

For example, given that we have the following object:

const person = {  
  name: 'Joe'  
};  
const employee = {  
  title: 'waiter'  
};  
employee.__proto__ = person;

We can use the Object.entries method as follows:

for (let entry of Object.entries(employee)) {  
  console.log(entry);  
}

Then we get back:

["title", "waiter"]

That is the key and the value of the property in the employee respectively.

Reflect.ownKeys

None of the Object methods support Symbols. To loop through objects with Symbol keys, we can use the Reflect.ownKeys method. Like Object.keys , it gets the keys of an object’s own properties, but Symbol keys are also supported. And it also returns an array of keys. String keys are also supported like Object.keys.

For example, given that we have the following object:

const foo = {  
  [Symbol('foo')]: 'Joe'  
};

We can use Reflect.ownKeys:

for (let prop of Reflect.ownKeys(foo)) {  
  console.log(prop);  
}

Then we get back Symbol(foo) .

Getting values from Symbol keys also work if we write the following loop:

for (let prop of Reflect.ownKeys(foo)) {  
  console.log(foo[prop]);  
}

Then we get back 'Joe’ .

This method is new with ES6.

As we can see, since ES5.1 or later, we have many alternatives to using the for...in loop to loop through the properties of objects. The only feature that’s useful in the for...in loop is to loop through the inherited properties of an object.

Otherwise, we should use Object.keys , Object.entries , Object.values since they all return arrays of keys or values or both. Also, the order is predictable, unlike the for...in loop. Since arrays are returned, we can sort them and loop through the properties or values the way we like to.

If we need to loop through Symbol keys, we can use the Reflect.ownKeys method.

Categories
JavaScript Nodejs

Node.js FS Module — Opening Files

Manipulating files is a basic operation for any program. Since Node.js is a server-side platform and can interact with the computer that it’s running on directly, being able to manipulate files is a basic feature. Fortunately, Node.js has a fs module built into its library. It has many functions that can help with manipulating files and folders.

File and folder operations that are supported include basic ones like manipulating and opening files in directories. Likewise, it can do the same for files. It can do this both synchronously and asynchronously. It has an asynchronous API that has functions that support promises. Also, it can show statistics for a file.

Almost all the file operations that we can think of can be done with the built-in fs module. In this article, we will introduce the fs module and how to construct paths that can be used to open files and open files with it.

We will also experiment with the fs promises API to do equivalent operations that exist in the regular fs module if they exist in the fs promises API. The fs promise API functions return promises so this let us run asynchronous operations sequentially much more easily.

To use the fs module, we just have to require it like in the following line of code:

const fs = require('fs');

Asynchronous Operations

The asynchronous versions of the file functions take a callback that has an error and the result of the operation as the argument. If the operation is done successfully, then null or undefined will be passed in for the error argument, which should the first parameter of the callback function passed in for each function. Asynchronous operations aren’t executed sequentially. For example, if we want to delete a file called file, we can write:

const fs = require('fs');  
  
fs.unlink('/file', (err) => {  
  if (err) throw err;  
  console.log('successfully deleted file');  
});

We have the err parameter which has the error data if it exists, which will have it if an error occurred. Asynchronous operations aren’t done sequentially, to do multiple operations sequentially, we can either convert it to a promise or nest operations in the callback function of the earlier operation. For example, if we want to rename and then check for the existence of a file a file, we can shouldn’t write the following:

fs.rename('/file1', '/file2', (err) => {  
  if (err) throw err;  
  console.log('renamed complete');  
});  
fs.stat('/file1', (err, stats) => {  
  if (err) throw err;  
  console.log(stats);  
});

because they aren’t guaranteed to be run sequentially. Therefore, we should instead write:

fs.rename('./file1', './file2', (err) => {  
  if (err) throw err;  
  console.log('renamed complete'); 

  fs.stat('./file2', (err, stats) => {  
    if (err) throw err;  
    console.log(stats);  
  })  
;});

However, this gets messy if we want to do lots of operations sequentially. We have too much nesting of callback functions if we have multiple file operations. Too much nesting of callback functions is called callback hell and it makes reading and debugging the code very hard and confusing. Therefore, we should use something like promises do run asynchronous operations. For example, we should rewrite the example above like the following code:

const fsPromises = require("fs").promises;

(async () => {  
  try {  
    await fsPromises.rename("./files/file1.txt", "./files/file2.txt");  
    const stats = await fsPromises.stat("./files/file2.txt");  
    console.log(stats);  
  } catch (error) {  
    console.log(error);  
  }  
})();

We used the fs promises API which has file operations functions that return promises. This is much cleaner and takes advantage of the async and await the syntax for chaining promises. Note that the fs promises API has a warning that it’s experimental.

However, it has been quite stable so far and it’s good for basic file manipulation that requires chaining of multiple file operations. Note that we are catching errors with the try...catch block. async functions look like synchronous functions but can only return promises.

Each of the examples above will output something like the following:

Stats {  
  dev: 3605029386,  
  mode: 33206,  
  nlink: 1,  
  uid: 0,  
  gid: 0,  
  rdev: 0,  
  blksize: undefined,  
  ino: 6192449489177455,  
  size: 0,  
  blocks: undefined,  
  atimeMs: 1572568634188,  
  mtimeMs: 1572568838068,  
  ctimeMs: 1572569087450.1968,  
  birthtimeMs: 1572568634187.734,  
  atime: 2019-11-01T00:37:14.188Z,  
  mtime: 2019-11-01T00:40:38.068Z,  
  ctime: 2019-11-01T00:44:47.450Z,  
  birthtime: 2019-11-01T00:37:14.188Z }

Synchronous Operations

Synchronous file operations usually have the word Sync at the end of its name and are called line by line. We get the error with the try...catch block. If we rewrite the example above as a synchronous operation, we can write:

const fs = require('fs');  
  
try {  
  fs.unlinkSync('./files/file1.txt');  
  console.log('successfully deleted ./files/file1.txt');  
} catch (err) {  
  console.err(err);  
}

We get the err binding with the catch clause to get the error data. The issue with synchronous operations in Node.js is that it holds up the processor until the process is finished, which makes long, resource-intensive operations hang the computer. Therefore, it slows down the execution of the program.

File Paths

Most file operation functions accept a path in the form of a string, a Buffer or a URL object using the file: protocol. String paths are interpreted as UTF-8 character sequences which identifies the absolute or relative path of the file or folder.

Relative paths are resolved relative to the current working directory, which is whatever is returned by the process.cwd() function. For example, we can open a file with a relative path like in the following code:

const fs = require("fs");

fs.open("./files/file.txt", "r", (err, fd) => {  
  if (err) throw err;  
  fs.close(fd, err => {  
    if (err) throw err;  
  });  
});

The dot before the first slash indicates the current work directory of the relative path. Or using the promise API we can rewrite it to the following code:

(async () => {  
  try {  
    const fileHandle = await fsPromises.open("./files/file.txt", "r");  
    console.log(fileHandle);  
  } catch (error) {  
    console.log(error);  
  }  
})();

Note that the promise API doesn’t have the close function. The regular fs API puts the file operation permission flag as the second argument. r stands for read-only.

The fs promise API has an open function and you can get the fd object, which stands by file descriptor, which is the reference to the file we opened. We can call the close function by passing the file descriptor fd .

The fs promise API doesn’t have this so the file can’t be closed with the promise API.

The fs API also accepts a URL object as a reference to the file location. It has to have the file: protocol and they must be absolute paths. For example, we can create a URL object and pass it into the read function to read a file. We can write the following code to do this:

const fs = require("fs");  
const fileUrl = new URL(`file://${__dirname}/files/file.txt`);

fs.open(fileUrl, "r", (err, fd) => {  
  if (err) throw err;  
  fs.close(fd, err => {  
    if (err) throw err;  
  });  
});

It does the exact same thing as with using relative paths. It’s just slightly more complex since we have to get the URL object and get the current directory of the code with the __dirname object. On Windows, any path that has a hostname prepended to the path is interpreted as a UNC path, which is the path to access a file over the local area network or Internet. For example, if we have the following:

fs.readFileSync(new URL('file://hostname/path/to/file'));

Then that will be interpreted as accessing a file on the server with the hostname hostname .

Any file URL that has a drive letter will be interpreted as an absolute path on Windows. For example, if we have the following path:

fs.readFileSync(new URL('file://c:/path/to/file'));

Then it’ll try to access the file in the c:\path\to\file path. Any file that has no hostname must have drive letters. Therefore only file paths with the same format above are valid file paths for a URL object on Windows. Paths that start with a drive letter must have a colon after the drive letter. On all other platforms, file: URLs with a hostname are unsupported and file paths like fs.readFileSync(new URL('file://hostname/path/to/file')); will throw an error.

Any file: URL with the escaped slash character will throw an error on all platforms, so any of the following examples would be invalid and throw errors. On Windows, these examples would throw errors:

fs.readFileSync(new URL('file:///C:/path/%2F'));  
fs.readFileSync(new URL('file:///C:/path/%2f'));

And on POSIX systems, these would fail:

fs.readFileSync(new URL('file:///pathh/%2F'));  
fs.readFileSync(new URL('file:///path/%2f'));

On Windows, file URLs with the encoded backslash character will throw an error, so the following examples are invalid:

fs.readFileSync(new URL('file:///D:/path/%5C'));  
fs.readFileSync(new URL('file:///D:/path/%5c'));

On POSIX systems the kernel maintains a list of opened files and resources for every process. Each file that’s opened is assigned a simple numeric identifier called the file descriptor.

The operating system uses the file description to identify and track each specific file. On Windows, the tracking file uses a similar mechanism and file descriptors are still used for tracking files and resources opened by various processes.

Node.js does the hard work of assigning file descriptors to the resources so we don’t have to do it manually. This is handy for cleaning up resources that are opened.

In Node.js, the fs.open() function is used to open files and assign a new file descriptor to opened files. After processing is done, it can be closed by the close function so that the open resources can be closed and cleaned up. This can be used like in the following code:

const fs = require("fs");

fs.open("./files/file.txt", "r", (err, fd) => {  
  if (err) throw err;  
  console.log(fd);  
  fs.fstat(fd, (err, stat) => {  
    if (err) throw err;  
    console.log("Stat", stat); fs.close(fd, err => {  
      if (err) throw err;  
      console.log('Closed');  
    });  
  });  
});

If we run the code above, we get output that resembles the following:

3  
Stat Stats {  
  dev: 3605029386,  
  mode: 33206,  
  nlink: 1,  
  uid: 0,  
  gid: 0,  
  rdev: 0,  
  blksize: undefined,  
  ino: 22799473115106240,  
  size: 0,  
  blocks: undefined,  
  atimeMs: 1572569358035.625,  
  mtimeMs: 1572569358035.625,  
  ctimeMs: 1572569358035.625,  
  birthtimeMs: 1572569358035.625,  
  atime: 2019-11-01T00:49:18.036Z,  
  mtime: 2019-11-01T00:49:18.036Z,  
  ctime: 2019-11-01T00:49:18.036Z,  
  birthtime: 2019-11-01T00:49:18.036Z }  
Closed

In the code above, we opened the file with the open function, which provided the fd object which contains the file descriptor in the callback function, we can use that to get the information about the file with the fstat function. And after we’re done, we can close the opened file resource with the close function.

We done the file open operation, read the file metadata and then close the file with the close function.

The promise API do not have the fstat or close function so we can’t do the same thing with it.

The Node.js run time platform has a fs module built into its standard library. It has many functions that can help with manipulating files and folders.

File and folder operations that are supported include basic ones like manipulating and opening files in directories.

Likewise, it can do the same for files. It can do this both synchronously and asynchronously. It has an asynchronous API that has functions that support promises.

Also, it can show statistics for a file. Almost all the file operations that we can think of can be done with the built-in fs module. In this article, we will introduce the fs module and how to construct paths that can be used to open files and open files with it.

We will also experiment with the fs promises API to do equivalent operations that exist in the regular fs module if they exist in the fs promises API.

The fs promise API functions return promises so this lets us run asynchronous operations sequentially much more easily. We barely scratch the surface, so stay tuned for Part 2 of this series.

Categories
Express JavaScript Nodejs

To store sessions in our Express apps, we can use the cookie-session middleware.

It’s a cookie-based session middleware that stores user sessions on the client within a cookie.

In this article, we’ll look at how to store sessions within an Express app with the cookie-session middleware.

How Cookie-Session Stores Sessions?

cookie-session doesn’t require any database or other server-side resources to store sessions. The session data can’t exceed the browser’s max cookie side.

It can also simplify certain load-balanced scenarios. It can also store a light session and include an identifier to look up a database-backed secondary store to reduce database lookups.

Adding the Library

We can install cookie-session by running:

npm i cookie-session

Then we can use it as follows:

const cookieSession = require('cookie-session')  
const express = require('express')  
  
const app = express()  
  
app.use(cookieSession({  
  name: 'session',  
  keys: 'secret',  
  maxAge: 24 * 60 * 60 * 1000  
}))

Options

The cookie-session middleware lets us pass in an options object with various properties to set the value.

This middleware will automatically add a Set-Cookie header to the response if the contents of the req.session were altered.

The Set-Cookie header won’t be in the response unless there are contents in the session, so we should add something to req.session as soon as we have identifying information to store the session.

The following options are available:

name

Name of the cookie to be set and defaults to session .

keys

The list of keys to sign and verify cookie values, or a Keygrip instance. Set cookies are always signed with keys[0] , and other keys are valid for verification.

This allows for key rotation. It can be used to change signature parameters like the algorithm of the signature.

secret

The string will be used as a single key if keys isn’t provided.

Cookie Options

The cookie object within the options have the following properties:

  • maxAge — a number of milliseconds from Date.now() for expiry
  • expires — a Date object indicating the cookie’s expiration date
  • path — a string indicating the path of the cookie, which defaults to /
  • domain — a sting indication the domain of the cookie
  • sameSite — boolean or string indicating whether the cookie is a ‘same site’ cookie. The default value is false , 'strict' , 'lax' or true are other possible values
  • secure — boolean indicating whether the cookie is only sent over HTTPS. If this is true and communication isn’t through TLS, then the cookie may not be set correctly
  • httpOnly — boolean indicating whether the cookie is sent over HTTP(S) and not available to client JavaScript
  • signed — boolean indicating whether the cookie is signed. Default value is true . If it’s true , then the cookie is appended with a .sig suffix.
  • overwrite — boolean indicating whether to overwrite previously set cookies of the same name

req.session

req.session holds the session for the given request.

.isChanged

A method that returns trie is the session has been changed during the request.

.isNew

A method that returns true if the session is new.

.isPopulated

A method that returns true is the session has been populated with data.

req.sessionOptions

We can set the sessionOptions object to change cookie settings.

Destroying a Session

We can set req.session to null to destroy a session.

Example

We can track the number of views a user made as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const cookieSession = require('cookie-session');  
const app = express();  
app.set('trust proxy', 1);  
app.use(cookieSession({  
  name: 'session',  
  keys: ['key1', 'key2']  
}))
app.get('/', (req, res, next) => {  
  req.session.numViews = (req.session.numViews || 0) + 1  
  res.end(`${req.session.numViews} views`);  
})
app.listen(3000);

In the code above, we just set req.session with the properties that we want to add and set the values the way we want.

The numViews property is added to track the number of views and increments as more request to the / is made.

Setting Max Age

We can set the maxAge as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const cookieSession = require('cookie-session');  
const app = express();  
app.set('trust proxy', 1);  
app.use(cookieSession({  
  name: 'session',  
  keys: ['key1', 'key2'],  
  maxAge: 1000,  
}))
app.use((req, res, next) => {  
  req.sessionOptions.maxAge = req.session.maxAge || req.sessionOptions.maxAge  
  next()  
})
app.get('/', (req, res, next) => {  
  req.session.numViews = (req.session.numViews || 0) + 1  
  res.end(`${req.session.numViews} views`);  
})
app.listen(3000);

We set maxAge to 1000 milliseconds above, then we’ll see that the values reset to 1 as the session expires.

In both cases, the Set-Cookie response header will be set as the session is set for the client.

We have multiple keys with the first one for encryption and the second one for verification.

Keygrip Integration

We can use keygrip as follows for encrypting our sessions:

const express = require('express');  
const bodyParser = require('body-parser');  
const cookieSession = require('cookie-session');  
const Keygrip = require('keygrip');  
const app = express();  
app.set('trust proxy', 1);  
app.use(cookieSession({  
  name: 'session',  
  keys: new Keygrip(['key1', 'key2'], 'SHA384'),  
  maxAge: 60 * 60 * 1000,  
}))
app.use((req, res, next) => {  
  req.sessionOptions.maxAge = req.session.maxAge || req.sessionOptions.maxAge  
  next()  
})
app.get('/', (req, res, next) => {  
  req.session.numViews = (req.session.numViews || 0) + 1  
  res.end(`${req.session.numViews} views`);  
})
app.listen(3000);

In the code above, we set the encryption algorithm to SHA384 for secure encryption.

Conclusion

The cookie-session lets us store sessions securely. We can use it to store identifying information about a user securely by encrypting the content with keys.

Then we can get and set the data via req.session .

Various options are also available for sessions. We can set maxAge to set when the session expires.

We can use domain , path , sameSite , secure , and httpOnly to control what kind of domain and path to set in our cookies, and also set what sites the cookies will be set for.

cookie-session also works with Keygrip for encryption.