Categories
JavaScript Nodejs

Node.js FS Module — Read Streams

Manipulating files and directories are basic operations 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 directory 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 create read streams to read a file’s data sequentially and listen to events from a read stream. Since Node.js ReadStreams are descendants of the Readable object, we will also listen to events to it.

Streams are collections of data that may not be available all at once and don’t have to fit in memory. This makes stream handy for processing large amounts of data.

It’s handy for files because files can be big and streams can let us get a small amount of data at one time. In the fs module, there are 2 kinds of streams. There’s the ReadStream and the WriteStream.

ReadStream

ReadStreams are for reading in data from a file and then outputting them a small part at a time. A ReadStream can read a small part of a file or it can read in the whole file.

To create a ReadStream, we can use the fs.createReadStream function. The function takes in 2 arguments. The first argument is the path of the file.

The path can be in the form of a string, a Buffer object, or an URL object.

The second argument is an object that can have a variety of options as properties. The flag option is the file system flag for setting the mode for opening the file. The default flag is r. The list of flags are below:

  • 'a' – Opens a file for appending, which means adding data to the existing file. The file is created if it does not exist.
  • 'ax' – Like 'a' but exception is thrown if the path exists.
  • 'a+' – Open file for reading and appending. The file is created if it doesn’t exist.
  • 'ax+' – Like 'a+' but exception is thrown if the path exists.
  • 'as' – Opens a file for appending in synchronous mode. The file is created if it does not exist.
  • 'as+' – Opens a file for reading and appending in synchronous mode. The file is created if it does not exist.
  • 'r' – Opens a file for reading. An exception is thrown if the file doesn’t exist.
  • 'r+' – Opens a file for reading and writing. An exception is thrown if the file doesn’t exist.
  • 'rs+' – Opens a file for reading and writing in synchronous mode.
  • 'w' – Opens a file for writing. The file is created (if it does not exist) or overwritten (if it exists).
  • 'wx' – Like 'w' but fails if the path exists.
  • 'w+' – Opens a file for reading and writing. The file is created (if it does not exist) or overwritten (if it exists).
  • 'wx+' – Like 'w+' but exception is thrown if the path exists.

The encoding option is a string that sets the character encoding in the form of the string. The default value is null .

The fd option is the integer file descriptor which can be obtained with the open function and its variants. If the fd option is set, then the path argument will be ignored. The default value is null .

The mode option is the file permission and sticky bits of the file, which is an octal number that are the same as Unix or Linux file permissions. It’s only set if the file is created. The default value is 0o666. The autoClose option specifies that the file descriptor will be closed automatically. The default value is true .

If it’s false , then the file descriptor won’t be closed even if there’s an error. It’s completely up to us to close it it autoClose is set to false to make sure there’s no file descriptor leak. Otherwise, the file descriptor will be closed automatically if there’s an error or end event emitted.

The emitClose option will emit the close event when the read stream ends. The default value is false .

The start and end options specifies the beginning and end parts of the file to read. Everything in between will be read in addition to the start and end . start and end are numbers that are the starting and ending bytes of the file to read.

The highWaterMark option is limit to the number of bytes that are read in the stream. The read stream will continue to be read and buffered if the highWaterMark value is reached, but the memory usage will be high and the garbage collection performance will be poor, or it can crash your program with the Allocation failed - JavaScript heap out of memory error.

The createReadStream function returns a ReadStream object where you can attach event handlers to it.

To create a ReadStream, we can use the createReadStream like in the following code:

const fs = require("fs");  
const file = "./files/file.txt";  
const stream = fs.createReadStream(file, {  
  flags: "r",  
  encoding: "utf8",  
  mode: 0o666,  
  autoClose: true,  
  emitClose: true,  
  start: 0  
});

stream.on("open", () => {  
  console.log("Stream opened");  
});

stream.on("ready", () => {  
  console.log("Stream ready");  
});

stream.on("data", data => {  
  console.log(data);  
});

stream.on("readable", () => {  
  while ((chunk = stream.read())) {  
    console.log(chunk);  
  }  
});

stream.on("close", () => {  
  console.log("Stream closed");  
});

When we run the code above, we should get something like the following outputted to the screen, assuming that you have ‘datadatadatadata’ written your a files.txt file:

Stream opened  
Stream ready  
datadatadatadata  
datadatadatadata  
Stream closed  
Stream closed

ReadStream Events

With a ReadStream, we can listen to the following events. There’s a close event that is emitted when the close event is emitted after the file is read.

The open event is emitted when the stream is opened. The file descriptor number fd will be passed with the event when it’s emitted. The ready event is emitted when the ReadStream is ready to be used. It’s fired immediately after the open event is fired.

The ReadStream extends the stream Readable object, which emits events of its own. The data event is emitted whenever the stream data is sent to the consumer. It’s emitted when the readable.pipe() function or readable.resume() are called, or by attaching a listener callback to the data event.

The data event will also be emitted when the readable.read() function is called and a chunk of data is available to be returned. The end event is emitted when there’s no more data to be consumed from the stream. It won’t be emitted until the data is completely consumed.

This can be done by switch the stream to flowing more or calling stream.read() repeated until all the data are consumed.

The error event is emitted whenever an error occurs during the streaming or consumption of the stream. It can be because the stream can’t generate data due to internal failure or a stream attempts to push invalid chunks of data. The pause event is emitted whenever ReadStream.pause() is called and readableFlowing isn’t false .

readableFlowing can have one of 3 states. One is null. When it’s null , this means that no mechanism for consuming the stream’s data is provided and therefore the stream won’t generate data.

When readableFlowing is null, attaching a listener for the 'data' event, calling the readable.pipe() method, or calling the readable.resume() method will switch readable.readableFlowing to true, causing the ReadStream to start emitting events as data is generated.

Calling readable.pause(), readable.unpipe(), or receiving backpressure, which is the situation where data fills the buffer, readable.readableFlowing to be set as false, temporarily halting the flow of events but not halting the generation of data.

Attaching a listener for the 'data' event will not switch readable.readableFlowing to true when readable.readableFlowing is set as false .

The readable event is emitted when there’s data available to be read from the stream or the end of the stream has been reached. Attaching an event listener for the readable event may cause some amount of data to be read into an internal buffer.

It will also be emitted when the end of the stream is reached but before the end event is emitted. The resume event is emitted when ReadStream.resume() is called and the readableFlowing isn’t true .

A ReadStream object also has the following properties. The bytesRead property let us get the number of bytes read so far.

The path property is a string or a buffer that gets us the reference to the file. It’s the same as the first argument of createReadStream() .

The data type will also be the same as what we pass in as the first argument. The pending property is a boolean which is true if the underlying file hasn’t been opened yet, or before the ready event is emitted.

By using the fs.createReadStream function, we created read streams to read a file’s data sequentially and listen to events from a read stream. Since Node.js ReadStreams are descendants of the Readable object, we will also listen to events to it.

We have lots of control over how the read stream is created. We can set the path or file descriptor of the file. Also, we can set the mode of the file to be read and the permission and sticky bit of the file being read.

Also, we can choose to close the streams automatically or not or emit close event automatically. We can also set the highWaterMark option which sets the event of maximum buffer size for storing the read data.

Also, we can call pipe to move data to a writable stream, and pause the streaming of data with the pause function, and resume streaming with the resume function.

Categories
Flow JavaScript

JavaScript Type Checking with Flow — Arrays and Tuples

Flow is a type checker made by Facebook for checking JavaScript data types. It has many built-in data types we can use to annotate the types of variables and function parameters.

In this article, we’ll look at how to add Flow types to arrays and tuples.

Array Definition

We define arrays exactly like in JavaScript. To define an array, we can use the literal:

let arr = [1, 2, 3];

We can also use the Array constructor as follows:

new Array(3);

Array constructor with one argument will create an empty array with the given length, so new Array(3) returns:

[empty × 3]

Calling the Array constructor with more than one argument creates an array with the arguments as the content, so:

new Array('foo', 'bar')

will get:

["foo", "bar"]

We can also add array entries after it’s defined:

let arr = [];  
arr[0] = 'foo';

Array Type

We can type arrays by using the Array<Type> syntax, where Type is any type that we want the array entries to be.

For example, we can define a number array by writing:

let arr: Array<number> = [100, 200];

We can type an array with different types of values by using the mixed type as the type. For instance, we can write:

let arr: Array<mixed> = [1, true, "foo"];

Array Type Shorthand

We can use the Type[] syntax as a shorthand for typing array. For example, we can write:

let arr: number[] = [100, 200];

to declare a number array.

We can declare an array that can be null by putting a question mark before the type name. For example, we can write:

let arr: ?number[] = null;

Also, we can assign a number array to it:

let arr: ?number[] = [1, 2];

If we want an array to take on null values in addition to values with the given type, we can put parentheses around the type name. For example, if we want a numerical array that can have null entries, we can write:

let arr: (?number)[] = [1, 2, null];

Accessing Array Values

Flow doesn’t check whether an array entry is defined before we access it. Therefore, we can run into undefined values at runtime, which may cause errors.

For instance, we can write:

let arr: number[] = [1, 2];  
let num = arr[3];

We should check if entries are undefined before accessing them.

Read Only Arrays

We can define read-only arrays by writing $ReadOnly<T> , where T is the placeholder for the type that we want the array values to take on.

For example, we can define a numerical read-only array by writing:

const arr: $ReadOnlyArray<number> = [100, 200];

We can only read the values from the arr array.

Also, read-only arrays can take on object types. For example, we can write:

const arr: $ReadOnlyArray<{foo: number}> = [{foo: 1}, {foo: 2}];

In the case of object entries, we can modify the property value of the object’s existing properties as follows:

arr[0].foo = 2

Note that read-only arrays have subtypes of a union type. For example, we $ReadOnlyArray<number> is a subtype of $ReadOnlyArray<number | string> .

However, Array<number> isn’t a subtype of Array<number | string> .

For example, we can write:

const foo = (arr: $ReadOnlyArray<number | string>) => {  
    
}const array: Array<number> = [1]  
foo(array)

In the code above, array will be converted to a $ReadOnlyArray<number | string> as it’s passed in to foo . This is because read-only arrays can’t have its entries modified to types that aren’t specified, so Flow allows it to be passed in.

Tuples

Tuples are lists with a small number of items. In JavaScript, we create a tuple with arrays.

With Flow, we have a tuple type to type these kinds of arrays. For example, we can write:

let tuple: [string, boolean] = ['foo', true];

Then the first entry always is a string and the second is always a boolean.

We can access entries by its index, so given the tuple that we have above, we can write:

let str : string  = tuple[0];

We can’t access indexes that aren’t defined in the tuple. For example, the following code will fail:

let str : string  = tuple[3];

since we don’t have 4 or more elements in our tuple.

Flow doesn’t know the type of value we try to access, so whatever the value is will have the union of all the types in the tuple.

For example, the value of tuple we have above will be of type string | boolean :

let val: string | boolean = tuple[0];

When we set new values to a tuple entry, it must match the type of that’s defined in that location. For example, if we have:

let tuple: [number, boolean, string] = [1, false, "foo"];

Then whatever we assign to tuple[0] must be a number, tuple[1] must be a boolean, and tuple[2] must be a string.

Strictly Enforced Length

Lengths of tuples are strictly enforced, so we can assign a tuple of one length to one with different length.

For example, the following assignment will fails since tuple1 and tuple2 have different lengths:

let tuple1: [number, boolean, string] = [1, true, 'foo'];  
let tuple2: [number, boolean] = tuple1;

Tuples aren’t Arrays

In Flow, tuples aren’t arrays even though they look the same. We can’t use arrays methods with tuples and we can’t assign arrays to tuples regardless of length.

For example, the following will fail:

let array: number[] = [10, 20];  
let tuple: [number, number] = array;

Assigning tuples to arrays also doesn’t work. For example:

let tuple: [number, number] = [1, 2];  
let arr: number[] = tuple;

will fail.

Array methods like join or push can’t be used on tuples, so:

tuple.push(3);  
tuple.join(',');

will give errors.

Arrays and tuples are lists of items. They aren’t the same. Arrays can use array methods. Also, we specify the type for all entries at once. With tuples, we define the type for each entry. Tuples also can’t use array methods.

Arrays can have null entries if we add a question mark before the type name and parentheses around it. An array can be null if we skip the parentheses and keep the rest.

Categories
JavaScript

Using JavaScript’s Object Constructor — Part 1

In JavaScript, the Object constructor lets us create object wrapper with the given values. It will create an empty object if null or undefined is passed into the Object constructor. If the value passed into the constructor is an object already then, it will return the object.

The Object constructor has 2 properties. It has a length property that is always 1, and like all other objects, the Object constructor has a prototype to get all the property additions to the type Object.

The Object constructor has many useful methods that can be used without constructor a new object. Part 1 of this list is below:

Object.assign()

The Object.assign() method makes a shallow copy of an object. The first parameter is a target object that you copy the object to, and the second parameter accepts an object that you want to copy. Note that if the source and target objects have the same properties, the source object’s property value to overwrite the one in the target object. For example, we can write:

const target = { a: 1, b: 2 };  
const source = { b: 3, c: 4};
const newObj = Object.assign(target, source);  
console.log(newObj)

If we run the code, we will get { a: 1, b: 3, c: 4} . We can also copy arrays. For example, we can write:

const targetArr = [1,2];  
const sourceArr = [2,3];
const newArr = Object.assign(targetArr, sourceArr);  
console.log(newArr)

We get [2,3] logged when we run the code above. For arrays, it will overwrite the whole target array with the source array.

Object.create()

The Object.create() method creates a new object with the object you pass in as the prototype of the new object. For example, we can write:

const obj = { a: 1, b: 2, c: 3 };  
const newObj = Object.create(obj);  
console.log(newObj);

In the code above, when we log newObj , it doesn’t have its own properties that weren’t inherited from obj . This is because we only passed in the first argument to the constructor, which is the prototype for the object that’s returned. If we want to add properties that are available only in the returned object, we pass in an object with the property names as keys and the properties writable , configurable , enumerable and value as properties of the property name keys, which is called the property descriptor. For example, we can write:

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

const childObj = {  
  a: {  
    writable: true,  
    configurable: true,  
    value: 'hello'  
  },  
  d: {  
    writable: true,  
    configurable: true,  
    value: 'hello'  
  }  
}  
const newObj = Object.create(obj, childObj);  
console.log(newObj);

In the code above, writable means that the property’s value can be changed. configurable means the property descriptor may be changed and if the property can be deleted from the object. The enumerable property means that the property shows up during enumeration of the properties with the for...in loop, and value is the value of the property.

If we want to create an object that has properties that can’t be changed, then we set writable to false , like in the following code:

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

const childObj = {  
  a: {  
    writable: false,  
    configurable: true,  
    value: 'hello'  
  },  
  d: {  
    writable: true,  
    configurable: true,  
    value: 'hello'  
  }  
}  
const newObj = Object.create(obj, childObj);  
newObj.a = 1;  
newObj.d = 1;  
console.log(newObj);

Notice that the assignment operator has no effect. If we have strict mode enabled, then a TyepError will be thrown. We have {a: “hello”, d: 1} logged in the console.log . This means that writable set to false is working for the property a and writable value set to true is working for the property d .

If we pass in null to the constructor, we get an empty object:

const nullObj = Object.create(null)  
console.log(nullObj)

We will get an error ‘Object prototype may only be an Object or null: undefined’ is we pass in undefined as the prototype of an object like in the code below:

const undefinedObj = Object.create(undefined);  
console.log(undefinedObj)

Object.defineProperty()

The Object.defineProperty() method defines a new property on an object. The first parameter is the object that you want to add the property to. The second parameter is the name of the property you want to add passed in as a string, and the last parameter is the property descriptor included in the Object.create() method when we try to add properties to the returned object. The property descriptor should have the properties writable , configurable , enumerable and value as properties of the property name keys. The writable means that the property’s value can be changed. configurable means the property descriptor may be changed and if the property can be deleted from the object. The enumerable property means that the property shows up during enumeration of the properties with the for...in loop, and value is the value of the property. For example, we can write:

let obj = {};

Object.defineProperty(obj, 'a', {  
  writable: false,  
  configurable: true,  
  value: 'hello'  
})

console.log(obj.a)  
obj.a  = 1;  
console.log(obj.a)

As we can see, the property descriptor acts the same way as in Object.create() . When writable is false , assignment to the property has no effect. If we have strict mode enabled, then a TyepError will be thrown.

We can also define getters and setter function for properties called get and set respectively for a property:

let obj = {};  
let value;

Object.defineProperty(obj, 'a', {  
  get() {  
    return value;  
  },  
  set(a) {  
    value = a;  
  }  
});

console.log(obj.a)  
obj.a = 1;  
console.log(obj.a)

As we can see, in the code above, we defined the property a for the object obj with the get and set functions to get the value of the property and set the value respectively.

Accessor properties are set on the prototype if we defined it on the prototype, but value properties are set on the current object. If an object inherits non-writable properties, it will still be non-writable on the current object. For example, if we have:

let ObjClass = function() {};  
ObjClass.prototype.a = 1;
Object.defineProperty(ObjClass.prototype, "b", {  
  writable: false,  
  value: 1  
});

const obj = new ObjClass();  
ObjClass.prototype.a = 3  
obj.a = 2  
ObjClass.prototype.b = 3  
obj.b = 2  
console.log(obj);

Then assigning to property b is no effect. If we have strict mode enabled, then a TyepError will be thrown.

Object.defineProperties()

The Object.defineProperties method let us define more than one property on an object. The first parameter of the method is the object that you want to define the properties on, and the second object contains the property names as key and the corresponding property descriptors as values. The property descriptor should have the properties writable , configurable , enumerable and value as properties of the property name keys. The writable means that the property’s value can be changed. configurable means the property descriptor may be changed and if the property can be deleted from the object. The enumerable property means that the property shows up during enumeration of the properties with the for...in loop, and value is the value of the property. For example, we can write:

let obj = {}  
Object.defineProperties(obj, {  
  a: {  
    writable: true,  
    configurable: true,  
    value: 'hello'  
  },  
  d: {  
    writable: false,  
    configurable: true,  
    value: 'hello'  
  }  
})  
obj.a = 1;  
obj.d = 1;  
console.log(obj);

To add the a and d properties where a ‘s value can change and d ‘s value can’t. The console.log will show {a: 1, d: “hello”} since the value assignment to d fails because the writable property of property d ‘s descriptor is set to false . We can also set configurable to false to prevent it from being deleted or having its property descriptor changed. For example, we can write:

let obj = {}  
Object.defineProperties(obj, {  
  a: {  
    writable: true,  
    configurable: true,  
    value: 'hello'  
  },  
  d: {  
    writable: false,  
    configurable: false,  
    value: 'hello'  
  }  
})  
obj.a = 1;  
delete obj.d;  
console.log(obj);

If we run the code above, then we can see that the delete operator had no effect on property d of obj .

Object.entries()

The Object.entries() method returns an array of key-value pairs of an object that are enumerable by the for...in loop. They are returned in the same order as they are in if they are iterated with the for...in loop. Each entry of the array will have the key as the first element and the value of the corresponding key as the second element. For example, we can use it to loop through the properties of an object in the following code:

const obj = {  
  a: 1,  
  b: 2  
};

for (let [key, value] of Object.entries(obj)) {  
  console.log(key, value);  
}

When we run the code above, we get a 1 and b 2 . These are the key-value pairs of this object.

Object.freeze()

The Object.freeze() method freezes an object. This means that all properties’ values in an object can’t be changed. Also, new properties can’t be added to it, and existing property descriptors for the frozen object can’t be changed. The object is frozen in place. This method doesn’t return a new frozen object. Instead, it returns the original object before it’s frozen. The frozen object’s prototype also can’t be changed. For example, if we run the following to freeze an object:

const obj = {  
  a: 1  
};
Object.freeze(obj);
obj.a = 2;
console.log(obj.a);

We get that obj.a is still 1. This is because the object’s properties’ values can’t be changed. If we have strict mode enabled, a TypeError will be raised. It’s important to note that values that are objects can still be modified. For example, if we have the following code:

const obj = {  
  a: 1,  
  b: {  
    c: 2  
  }  
};
Object.freeze(obj);
obj.b.c = 3;
console.log(obj.b.c);

Then obj.b.c is 3 since Object.freeze doesn’t freeze properties that are inside of nested objects unless they’re frozen explicitly. So if we have:

const obj = {  
  a: 1,  
  b: {  
    c: 2  
  }  
};

Object.freeze(obj);  
Object.freeze(obj.b);
obj.b.c = 3;
console.log(obj.b.c);

Then obj.b.c is 2 since we frozen obj.b so that we can’t modify the value of obj.b .

The Object constructor has many more methods for constructing objects from an array of arrays with key and value of properties, and also methods to get property descriptors from objects, property names, property symbols, gets the keys of objects and prevent properties from being added or deleted or modify their property descriptors.

Categories
Express JavaScript Nodejs

Use body-parser Express Middleware to Parse JSON and Raw Requests

By default, Express 4.x or later doesn’t come with anything to parse request bodies. Therefore, we need to add something to do this.

In this article, we’ll look at how to use the body-parser middleware to do this with JSON and raw bodies.

Adding the Body-Parser Package

body-parser is a Node package that we can add onto our Express app to parse request bodies.

It doesn’t support multipart bodies, so we have to use other middleware packages to parse those.

However, it can parse JSON bodies, raw request bodies, text, and URL-encoded bodies.

To install the package, we run:

npm install body-parser

Then we can include by adding:

const bodyParser = require('body-parser');

Parsing JSON Bodies

We can parse JSON bodies by calling the JSON method. It takes an optional object with a few properties.

The options include:

  • inflate — compressed request bodies will be inflated when this is set to true . Otherwise, they’ll be rejected.
  • limit — controls the maximum request body size. If it’s number, then it’s measured in bytes. If it’s a string then it can be parsed into a number of bytes.
  • reviver — this is a function that’s passed into JSON.parse as the second argument to map values to what we want
  • strict — only accepts objects and arrays when set to true . Otherwise, it’ll accept anything that JSON.parse accepts. Defaults to true .
  • type — this is used to determine what media type it’ll parse. It can be a string, array of strings or a function. If it’s not a function, then it’s directly passed into the type-is library. Otherwise, the request is parsed if the data type the function is called with returns a truthy value
  • verify — this is a function with signature (req, res, buf, encoding) , where buf is a Buffer object of the raw request body. The parsing can be aborted by throwing an error in the function.

For example, we can use it as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();  
const options = {  
  inflate: true,  
  limit: 1000,  
  reviver: (key, value) => {  
    if (key === 'age') {  
      if (value < 50) {  
        return 'young'  
      }  
      else {  
        return 'old';  
      }  
    }  
    else {  
      return value;  
    }  
  }  
};  
app.use(bodyParser.json(options));
app.post('/', (req, res) => {  
  res.send(req.body);  
});
app.listen(3000);

Then when we make a POST request to / , we get back:

{  
    "name": "foo",  
    "age": "young"  
}

as the response since we check the age field in the reviver function and return 'young' or 'old' depending on the value . Otherwise, we return the value as-is.

The request body is parsed and set as the value of req.body .

Parsing Raw Bodies

We can parse raw bodies as a buffer. It supports the automatic inflation of gzip and deflate encodings.

The parsed body containing the parsed data is populated on the request object, i.e. it’ll be set as the value of req.body .

It takes an optional option object that can take the following properties:

  • inflate — compressed request bodies will be inflated when this is set to true . Otherwise, they’ll be rejected.
  • limit — controls the maximum request body size. If it’s number, then it’s measured in bytes. If it’s a string then it can be parsed into a number of bytes.
  • type — this is used to determine what media type it’ll parse. It can be a string, array of strings or a function. If it’s not a function, then it’s directly passed into the type-is library. Otherwise, the request is parsed if the data type the function is called with returns a truthy value
  • verify — this is a function with signature (req, res, buf, encoding) , where buf is a Buffer object of the raw request body. The parsing can be aborted by throwing an error in the function.

For example, we can use it as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();  
const options = {  
  inflate: true,  
  limit: 1000,  
  type: 'text/plain'  
};  
app.use(bodyParser.raw(options));
app.post('/', (req, res) => {  
  res.send(req.body);  
});
app.listen(3000);

Then when we send a POST request to / with the body foo , we get back foo as the response.

This is because we specified text/plain as the type to parse the raw data.

It also smart enough to parse multiple body types. We can do that by passing in an array of type strings as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();  
const options = {  
  inflate: true,  
  limit: 1000,  
  type: ['text/plain', 'text/html']  
};  
app.use(bodyParser.raw(options));
app.post('/', (req, res) => {  
  res.send(req.body);  
});
app.listen(3000);

Then when we make a POST request to / with the body <b>foo</b> . Then we get back <b>foo</b> .

Conclusion

We can use the body-parser middleware to parse JSON and raw text bodies.

It also takes a variety of options to let us control whether to inflate compressed request bodies, map JSON values to something else, limit the size of a request body, and so on.

For raw request bodies, we can use body-parser to specify the type that the data is so we can parse it to that type of object and set it to req.body .

Categories
JavaScript

More JavaScript Array Tips — Truncating and Filling Entries

JavaScript, like any other programming language, has 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 truncating arrays, converting arrays to objects and filling arrays with data.

Truncating Arrays

In JavaScript, array objects have a length property that specifies the length of an array. It’s both a getter and setter property so in addition to being able to get the length of an array, we can also use it to set the length of an array.

Setting the length property to be less than the original length of the array will truncate the array up to the number of entries that we specify. For example, if we want to only keep the first entry of an array, then we can write something like:

let arr = [1, 2, 3, 4, 5, 6];  
arr.length = 1  
console.log(arr);

We simply set the length property of our array and then magically we get only the first element of the array left.

Alternatively, we can use the splice method to remove entries from an array. This method lets us removing, replace an existing element or adding new elements in place, but we only need it to delete elements from the array.

The argument of the splice method is the starting index start of which to start changing the array. It can be positive or negative. If it’s negative, then it’ll start changing the array from the end and move towards the start of the array. The end index is -1, the second is -2 and so on.

The second argument of the splice method is the deleteCount , which is an optional argument that lets us specify how many items to delete starting from the start parameter in the first element.

Subsequent arguments are the items that we want to insert into an array. This can go on for as long as we want. Only the first argument is required.

For example, if we want to truncate an array and keep only the first 2 elements, we can write something like the following code:

const arr = [1, 2, 3, 4, 5, 6];  
arr.splice(2)  
console.log(arr);

As we can see we only need to specify the first argument, and then the deleteCount will be automatically set so that it removes the entries on the right of the array. We can also specify a negative start index like in the following code:

const arr = [1, 2, 3, 4, 5, 6];  
arr.splice(-1 * arr.length + 1)  
console.log(arr);

So if we specify -1 * arr.length + n in the first argument, then we keep the first n entries of the original array.

We can also use the slice method to truncate arrays. It’s used for extracting values from an array in general and returns a new array with the extracted values. The slice method takes 2 arguments.

The first is an optional argument to specify where to begin extracting entries from an array. The second argument is another optional argument that specifies the end index of the original array to end the extraction of values from it. The value at the end index itself is excluded from the extraction.

For example, if we want the first 2 values from the original array, then we can write something like the following code:

let arr = [1, 2, 3, 4, 5, 6];  
arr = arr.slice(0, 2);  
console.log(arr);

When we run the code, we get back [1, 2] as the new value of arr . We can also specify negative indexes for the start and end. The last entry of the array is at -1, the second last element is at the index -2, and so on. So if we want to extract the first 2 elements of an array with negative indexes, we can write something like the following:

let arr = [1, 2, 3, 4, 5, 6];  
arr = arr.slice(-1 * arr.length, -1 * arr.length + 2);  
console.log(arr);

The first element of an array would be at index -1 * arr.length if we expressed it in terms of a negative index. Then the subsequent entries would be at -1 * arr.length + n where n is array entry at the n th position.

Filling Arrays with Data

With ES6 or later, we can fill arrays with an array with new data with the fill method. The fill method takes up to 3 arguments. The first is the value we want to add to the array.

The second optional argument is the start index of which we want to start filling data.

The default value of the second argument is 0. The third argument is an optional one that lets us specify the end index of to fill the array entry up to.

The default value for the third argument is the length of the array. Note that the end index itself is excluded from the filling so it won’t overflow when we call the fill method.

The fill the method returns a new array with the new values filled in. All existing values that were filled will replace any existing values that were in the original array.

For example, we can use it in the following way:

let arr = [1, 2, 3, 4, 5, 6];  
arr = arr.fill(8)  
console.log(arr);

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

[8, 8, 8, 8, 8, 8]

We can also change the index of the fill method to specify the fill boundary. For example, we can write:

let arr = [1, 2, 3, 4, 5, 6];  
arr = arr.fill(8, 3, 5)  
console.log(arr);

Then we get:

[1, 2, 3, 8, 8, 6]

from the console.log . If we specify an end index that’s over the length of the original array, then it’ll replace the values up to the original array up to the last position of the original array. For example, if we have:

let arr = [1, 2, 3, 4, 5, 6];  
arr = arr.fill(8, 3, 10)  
console.log(arr);

Then we get:

[1, 2, 3, 8, 8, 8]

We can also use negative indexes to specify the fill boundary with the fill method. The last position of the array would be -1, the second last would be -2, and so on. So we can call fill with negative indexes like the following:

let arr = [1, 2, 3, 4, 5, 6];  
arr = arr.fill(8, -4, -1)  
console.log(arr);

Then we get:

[1, 2, 8, 8, 8, 6]

since the third argument doesn’t include the last index. If we want to fill the last element as well with the new value, then we just omit the last argument like in the following code:

let arr = [1, 2, 3, 4, 5, 6];  
arr = arr.fill(8, -4)  
console.log(arr);

We get [1, 2, 8, 8, 8, 8] when we run the code above. If the second argument’s value is bigger than the third argument, then no fill operation will take place and the original array is returned. For example, if we have:

let arr = [1, 2, 3, 4, 5, 6];  
arr = arr.fill(8, 4, 1)  
console.log(arr);

Then we get back [1, 2, 3, 4, 5, 6] which is the original array.

Conclusion

In JavaScript, array objects have a length property that specifies the length of an array. It’s both a getter and setter property so in addition to being able to get the length of an array, we can also use it to set the length of an array. This means that setting the length property to be less than the original length of the array. We can also use the splice and slice methods to truncate arrays.

With ES6 or later, we can fill arrays with an array with new data with the fill method. The fill method takes up to 3 arguments. The first is the value we want to add to the array. The second optional argument is the start index of which we want to start filling data.

The default value of the second argument is 0. The third argument is an optional one that lets us specify the end index to fill the array entry up to. The default value for the third argument is the length of the array.

We can also use a negative index for the second and third arguments for setting the boundaries.