Categories
MongoDB Node.js Basics

Node.js Basics — MongoDB Basics

Node.js is a popular runtime platform to create programs that run on it.

It lets us run JavaScript outside the browser.

In this article, we’ll look at how to start using Node.js to create programs.

MongoDB

MongoDB is another NoSQL database that we can use in our Node.js app.

It can also store key-value pairs.

MongoDB is hosted in a server either through the cloud or in our own servers.

To use it in our Node app, we run:

npm install mongodb

Then we can use it in our package.

We also need to install the MongoDB server from https://www.mongodb.com/try/download/community

Once we installed the package in our project and the server, we can connect to it in our app.

To do that, we write:

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

async function run() {
  try {
    await client.connect();
    await client.db("test").command({ ping: 1 });
    console.log("Connected successfully to server");
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

We call MongoClient function with the connection string to connect to the database server with the given connection string.

In the run function, we call client.connect() to do the connection.

Then we call client.db to connect to the given database.

The command method sends the command we want to the server.

The client.close method closes the connection and clean up any resources.

Inserting Data

We can get the collection we want to save our data to and save the data there.

To do that, we write:

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

async function run() {
  try {
    await client.connect();
    const testCollection = await client.db("test").collection('test');
    const result = await testCollection.insertOne({ foo: 'bar' });
    console.log(result)
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

We connect to the database we want with the db method.

Then we get the collection we want to save data to with the collection method.

Next, we call insertOne on the resolved collection object to insert an object.

And finally, we log the result.

Also, we can use the insertMany method to insert multiple documents.

For example, we can write:

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

const pizzaDocuments = [
  { name: "Sicilian pizza", shape: "square" },
  { name: "New York pizza", shape: "round" },
  { name: "Grandma pizza", shape: "square" },
];

async function run() {
  try {
    await client.connect();
    const testCollection = await client.db("test").collection('test');
    const result = await testCollection.insertMany(pizzaDocuments);
    console.log(result)
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

We just pass in an array into the insertMany method.

Conclusion

We can use the MongoDB driver to connect to a MongoDB database and then insert one or more documents into our database collection.

Categories
MongoDB Node.js Basics

Node.js Basics — Deleting and Updating MongoDB Documents

Node.js is a popular runtime platform to create programs that run on it.

It lets us run JavaScript outside the browser.

In this article, we’ll look at how to start using Node.js to create programs.

Delete a Document

We can delete a document within our MongoDB collection.

To do that, we can write:

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

const pizzaDocuments = [
  { name: "Sicilian pizza", shape: "square" },
  { name: "New York pizza", shape: "round" },
  { name: "Grandma pizza", shape: "square" },
];

async function run() {
  try {
    await client.connect();
    const testCollection = await client.db("test").collection('test');
    const result = await testCollection.insertMany(pizzaDocuments);
    console.log(result)
    const deleteResult = await testCollection.deleteOne({ name: { $type: "string" } });
    console.log(deleteResult)
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

We pass in a query into the deleteOne method to delete all the entries with the name that has type string.

Also, we can delete all the entries with the name set to type string with the deleteMany method:

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

const pizzaDocuments = [
  { name: "Sicilian pizza", shape: "square" },
  { name: "New York pizza", shape: "round" },
  { name: "Grandma pizza", shape: "square" },
];

async function run() {
  try {
    await client.connect();
    const testCollection = await client.db("test").collection('test');
    const result = await testCollection.insertMany(pizzaDocuments);
    console.log(result)
    const deleteResult = await testCollection.deleteMany({ name: { $type: "string" } });
    console.log(deleteResult)
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

Change a Document

We can update a document with thw updateOne method.

For example, we can write:

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

const filter = { foo: 'bar' };
const updateDocument = {
  $set: {
    foo: 'baz',
  },
};

async function run() {
  try {
    await client.connect();
    const testCollection = await client.db("test").collection('test');
    const result = await testCollection.updateOne(filter, updateDocument);
    console.log(result)
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

We call the updateOne method to find the item with the query object in the first argument.

The 2nd argument is the document to update the item with.

The $set property indicates that we want to add or update the key-value pair in the object we set as its value.

Update Arrays in a Document

We can update arrays in a document by accessing its property.

For example, we can write:

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

async function run() {
  try {
    await client.connect();
    const testCollection = await client.db("test").collection('test');
    const result = await testCollection.insertOne({
      name: "Popeye",
      address: "1 Sweethaven",
      items: [
        {
          type: "pizza",
          size: "large",
          toppings: ["garlic, spinach"],
        },
        {
          type: "calzone",
          toppings: ["ham"],
        },
      ],
    });
    console.log(result)
    const query = { name: "Popeye" };
    const updateDocument = {
      $set: { "items.0.toppings": "veggie" }
    };
    const updateResult = await testCollection.updateOne(query, updateDocument);
    console.log(updateResult)
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

We call insertOne to add the entry we want into our collection.

Then we define the updateDocument object with the $set property to set the item we want.

items.0.toppings will get the toppings property of the first entry ofitems array and update it.

To match all array elements, we can use the $[] positional operator:

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

async function run() {
  try {
    await client.connect();
    const testCollection = await client.db("test").collection('test');
    const result = await testCollection.insertOne({
      name: "Popeye",
      address: "1 Sweethaven",
      items: [
        {
          type: "pizza",
          size: "large",
          toppings: ["garlic, spinach"],
        },
        {
          type: "calzone",
          toppings: ["ham"],
        },
      ],
    });
    console.log(result)
    const query = { name: "Popeye" };
    const updateDocument = {
      $set: { "items.$[].toppings": "veggie" }
    };
    const updateResult = await testCollection.updateOne(query, updateDocument);
    console.log(updateResult)
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

Now both items in the items array has 'veggie' appended into it.

Conclusion

We can delete a document and update an array in an existing document with the MongoDB driver.

Categories
Node.js Basics

Node.js Basics — Level DB

Node.js is a popular runtime platform to create programs that run on it.

It lets us run JavaScript outside the browser.

In this article, we’ll look at how to start using Node.js to create programs.

Level DB

Most apps need to store data somewhere.

They most likely use a database to store their data.

One way to store data within a Node app is to use the Level DB database.

It’s a key-value pair database, which makes it a NoSQL database.

It supports JSON right off the bat so we won’t have to do much to get and set the data.

The database is stored in a file.

To use Level DB, we run:

npm install level

to install the package.

Then we can use it by writing:

const level = require('level')
const db = level('my-db')
db.put('name', 'level',  (err) => {
  if (err) {
    return console.log(err)
  }
  db.get('name',  (err, value) => {
    if (err) {
      return console.log(err)
    }
    console.log(`name=${value}`)
  })
})

We get the database file by calling the level function with the database file’s path.

Then we call db.put with the key and value as the first 2 arguments to save the key-value pair.

The 3rd argument is a function to get the error from the err parameter in case there is any.

The db.get method lets us get a piece of data by the key.

The first argument is the key .

The 2nd argument is the callback in the same format.

Then we should see the output:

name=level

displayed.

We can also store a bunch of key-value pairs with one operation.

For instance, we can write:

const level = require('level')
const db = level('my-db')

const ops = [
  { type: 'del', key: 'father' },
  { type: 'put', key: 'name', value: 'james smith' },
  { type: 'put', key: 'dob', value: '1990-01-01' },
  { type: 'put', key: 'spouse', value: 'jane smith' },
  { type: 'put', key: 'occupation', value: 'waiter' }
]

db.batch(ops, function (err) {
  if (err){
     return console.log(err)
  }
  console.log('Great success')
})

We have an ops array with a bunch of objects to define the operations.

The type is the type of the operation we want to do. They are the same as the method names.

The key is needed for getting the items to delete.

And the value is what we insert as the value of the key.

We can also chain the del and put methods to manipulate the key-value pairs:

const level = require('level')
const db = level('my-db')

db.batch()
  .del('father')
  .put('name', 'james smith')
  .put('dob', '1990-01-01')
  .put('spouse', 'jane smith')
  .put('occupation', 'waiter')
  .write(function () { console.log('Done!') })

We can read our data with a read stream.

For example, we can write:

const level = require('level')
const db = level('my-db')

db.put('foo', 'bar', function(error) {
  if (error){
    return console.log(error)
  }
  const stream = db.createReadStream();
  stream
    .on('data', function({ key, value }) {
      console.log(`${key}=${value}`);
    })
    .on('error', function(error) {
      console.log(error);
    })
    .on('end', function() {
      console.log('end');
    });
});

We call db.createReadStream to create the read stream.

Then we listen to the data to get the key-value pairs from the key and value properties respectively.

Conclusion

We can use the Level DB database to store key-value pairs in our app.

Categories
Node.js Basics

Node.js Basics — App Configuration

Node.js is a popular runtime platform to create programs that run on it.

It lets us run JavaScript outside the browser.

In this article, we’ll look at how to start using Node.js to create programs.

Configuration

We can configure our Node app in various ways. We can use it to make our app more flexible.

JSON Files

One way to configure our Node.js app is to read the configuration from one or more JSON files.

For example, we can configure our HTTP server from a JSON file by writing:

config.json

{
  "host": "0.0.0.0",
  "port": 8000
}

index.js

const { port, host } = require('./config.json'),
  http = require('http');

http
  .createServer(function(request, response) {
  })
  .listen(port, host, function() {
    console.log('Listening on port', port, 'and host', host);
  });

We read the configuration from the config.json file.

It’ll automatically be parsed into an object when we call require with the path.

Then we can call createServer and listen methods with it.

We can store multiple configurations with it.

For example, we can write:

config.json

{
  "server": {
    "host": "0.0.0.0",
    "port": 8000
  },
  "database": {
    "host": "db1.example.com",
    "port": 27017
  }
}

index.js

const { server: { port, host } } = require('./config.json'),
  http = require('http');

http
  .createServer(function(request, response) {
  })
  .listen(port, host, function() {
    console.log('Listening on port', port, 'and host', host);
  });

We have a server and database property so that we can store the config for the HTTP server and the config.

Then we destructured the property we need for the HTTP server.

Environmental Variables

We can store our config as environment variables. This way, we can store the settings in our server’s OS or in a file and read them from there.

To do that, we can use the dotenv library.

We can install it by running:

npm i dotenv

Then we can use it by creating and .env file:

HOST=0.0.0.0
PORT=8000

index.js

const http = require('http');
require('dotenv').config()
const { PORT, HOST } = process.env;

http
  .createServer(function(request, response) {
  })
  .listen(PORT, HOST, function() {
    console.log('Listening on port', PORT, 'and host', HOST);
  });

We read the environment variables from the process.env property after we call:

require('dotenv').config()

We store the key-value pairs in the .env file and then read them with the code above into the process.env property.

Arguments

A Node app can also take in arguments.

The command-line arguments will be put into an array and assigned to the process.argv property.

It’ll contain the parts of the command split by spaces.

For example, if we run:

node server.js --port=8001

then process.argv is:

['node', 'server.js', '--port=8001']

We should set some default values if there’s no value set for the command-line argument.

For example, we can write:

const http = require('http');
const [,, PORT = 8080, HOST = '0.0.0.0'] = process.argv;
http
  .createServer(function(request, response) {
  })
  .listen(PORT, HOST, function() {
    console.log('Listening on port', PORT, 'and host', HOST);
  });

We assign the default values as we’re destructuring the command line arguments.

Conclusion

We can configure our Node app in various ways.

Methods include using JSON files, environment variables, and command-line arguments.

Categories
Node.js Basics

Node.js Basics — Error Handling

Node.js is a popular runtime platform to create programs that run on it.

It lets us run JavaScript outside the browser.

In this article, we’ll look at how to start using Node.js to create programs.

Error Handling

Most nontrivial programs will run into errors at some point.

Therefore, we got to handle them so that we can keep our programming running properly.

There’re a few ways to handle errors. One way is to use try-catch .

The others include catching them at the process or domain level.

To catch errors with try-catch , we just write:

function parseJSON(input) {  
  let json;  
  try {  
    json = JSON.parse(input);  
  } catch (error) {  
    return Promise.reject("Couldn't parse JSON");  
  }  
  return Promise.resolve(json);  
}

We tried to parse JSON in our function with the JSON.parse method.

When there’s an error, we return a rejected promise with Promise.reject with the reason for rejection.

Otherwise, we return a resolved promise with the parsed json object.

We can catch errors when the uncaughtException event is emitted.

For example, we can write:

process.on('uncaughtException', function(error) {  
  logger.fatal(error);  
  logger.fatal('Fatal error encountered, exiting now');  
  process.exit(1);  
});

We call process.on to watch for the uncaughtException event.

Then we call logger.fatal to log a fatal error.

And then call process.exit with code 1 to exit the program with an error code.

We should always exit the process following an uncaught error.

This is because the app is in an unknown state when an uncaught exception is raised.

A better way to catch errors is to catch them at the domain level.

To do that, we can use the domain package.

We install it by running:

npm i domain

Then we can use it by writing:

const Domain = require('domain');    
const domain = Domain.create();  
domain.on('error', function(error) {  
  console.log('Domain error', error.message);  
});  
domain.run(function() {  
  console.log(process.domain === domain);  
  throw new Error('Error happened');  
});

This prevents us from stopping the program whenever an error occurs.

We just catch errors happening in some domains instead of catching all errors.

We run any code that may raise errors in the domain.run callback so that we can catch errors.

If we have any async code, then we can run the async code in the domain.run method’s callback.

For example, we can write:

const Domain = require('domain');  
const domain = Domain.create();  
domain.on('error', function(error) {  
  console.log('Domain error', error.message);  
});  
process.nextTick(function() {  
  domain.run(function() {  
    throw new Error('Error happened');  
  });  
});

to run the code for the nextTick callback in the domain.run ‘s callback instead.

Conclusion

There’re several ways to handle errors in a Node.js app.

One way is to use try-catch to catch the error.

Another way is to catch the error by listening to the uncaughtException event.

A better way to catch errors is to catch them by the domain.