RESTful API with Node.js and Mysql

RESTful API with Node.js and Mysql

APIs are the way applications communicate with each other. And APIs are the way to make versatile applications. One API and multiple frontends or multiple micro APIs and multiple front ends, there is no doubt that API development is the core of...

APIs are the way applications communicate with each other. And APIs are the way to make versatile applications.

One API and multiple frontends or multiple micro APIs and multiple front ends, there is no doubt that API development is the core of scalable and manageable applications.

In this post, we will talk about creating a REST API on Node.js with Express and MySQL.

First things first; important prerequisites for this article to follow up are:

First let's start with project initialization i.e. git, npm, etc.

npm i -S express


HTTP Server

Now we need to add express and related dependencies to get up and running with the HTTP server.

npm i -S express body-parser

Express is a framework for API development on Node.js; similar tools in this area are:

Now we set up the basic express app server with following server.js file:

const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => res.send('Hello World!'))

app.listen(port, () => console.log(`Example app listening on port ${port}!`));

As you can see, like with the function call app.get

  • We are defining a Route on our server
  • The server will respond to GET calls for this Route
  • The Callback will handle it in the following way: (req, res) => res.send('Hello World!'); And for this route, the response will be Hello World String.

And if we write the same line as follows:

app.post('/', (req, res) => res.send('Hello POST World!'))

This is the same as GET; except with the .post method, the server with .post method will respond to POST requests on these Routes with string Hello POST World

You can send the same response to all HTTP methods with the .all method as:

app.all('/ping', (req, res) => res.send(new Date()))


Database Access & CRUD

As we are using MySQL Database here, we need to set up the database access for Node.js.

For that, we will need mysql package for Node.js. Let's go ahead and install the package as the main dependency

npm i -S mysql

After installing mysql, we can write our basic code to connect to the database and then get some data from a table. It can be written as follows:

const mysql = require('mysql');

// Get the Host from Environment or use default
const host = process.env.DB_HOST || 'localhost';

// Get the User for DB from Environment or use default
const user = process.env.DB_USER || 'root';

// Get the Password for DB from Environment or use default
const password = process.env.DB_PASS || '';

// Get the Database from Environment or use default
const database = process.env.DB_DATABASE || 'twitter_clone';

// Create the connection with required details
const con = mysql.createConnection({
  host, user, password, database,
});

const query = "SELECT * FROM tweets";

// make to connection to the database.
con.connect(function(err) {
  if (err) throw err;

  // if connection is successful
  con.query(query, (err, result, fields) => {
    // if any error while executing above query, throw error
    if (err) throw err;

    // if there is no error, you have the result
    console.log(result);
 });
});

Here we are making a connection to the database with mysql.createConnection function and then using the connection reference to fire a Select query to the Database with connection.query function.

In both functions, we are using callbacks with first error parameter which will tell us if any error occurred while executing the provided command. We can convert the above code to async/await or Promise style with the help of the following functions:

Connect as a Promise
// connect
const mysql = require('mysql');

module.exports = async (params) => new Promise(
(resolve, reject) => {
    const connection = mysql.createConnection(params);
  connection.connect(error => {
      if (error) {
      reject(error);
      return;
    }
    resolve(connection);
  })
});

Query as a Promise
// query
module.exports = async (conn, q, params) => new Promise(
(resolve, reject) => {
  const handler = (error, result) => {
      if (error) {
      reject(error);
      return;
    }
    resolve(result);
  }
  conn.query(q, params, handler);
});

Now we merge the basic express app, promise based mysql connection & query; the code to connect and the query should look like this:

const express = require('express')

const dbConfig = require('./dbConfig');
// ↑ exports = {user, password, host, databse}

const connection = require('./helpers/connection');
const query = require('./helpers/query');

const app = express()
const port = 3000;

app.get('/', (req, res) => res.send('Hello World!'))

app.get('/list', async (req, res) => {
  const conn = await connection(dbConfig).catch(e => {}) 
  const results = await query(conn, 'SELECT * FROM tweets').catch(console.log);
  res.json({ results });
})

app.listen(port, () => console.log(`Example app listening on port ${port}!`))

The above code will be following the following DB schema:

CREATE TABLE users(
    id int NOT NULL AUTO_INCREMENT,
    username varchar(15) NOT NULL,
    password varchar(32) NOT NULL,
    followers int DEFAULT 0,
    following int DEFAULT 0,
    tweets int DEFAULT 0,
    PRIMARY KEY (id)
);

CREATE TABLE following(
    id int NOT NULL AUTO_INCREMENT,
    user1_id int REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
    user2_id int REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
    PRIMARY KEY (id)
);

CREATE TABLE tweets(
    id int NOT NULL AUTO_INCREMENT,
    username varchar(15) NOT NULL,
    user_id int REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
    tweet varchar(140) NOT NULL,
    timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id)
);
############################
# Some Random Data
INSERT INTO USERS(username, password) VALUE('pankaj', MD5('pankaj'));

INSERT INTO TWEETS(username, user_id, tweet) VALUE('pankaj', 1, 'Hello World!');

INSERT INTO TWEETS(username, user_id, tweet) VALUE('pankaj', 1, 'Hello World Again!');


Till now we have seen the basic arrangement of MySQL Functions. Let's take a brief look at how we can arrange our Express app efficiently.

Middleware

Middleware Functions are the functions that transform the server app functionalities in terms of extending the request and/or response and passing them on to the next function or middleware function in the chain.

Request
  → Middleware-1
  → next()
  → Middleware-2
  → Route Handler 1
  → Route handler 2
  → Send Response
  → End

By calling next() inside a middleware function; the processing is handed over to the next function or middleware function

Middleware functions can perform the following tasks:

  • Execute any code.
  • Make changes to the request and the response objects.
  • End the request-response cycle.
  • Call the next middleware function in the stack.

What does a middleware function look like? As following:

+ app.use(function (req, res, next) {
+  console.log('Time:', Date.now())
+  next()
+ })

The above middleware will log the time on each request received

Or you can run it on a very specific route (for example /users ) in the following way:

- app.use(function (req, res, next) {
+ app.use('/users', function (req, res, next) {
    console.log('Time:', Date.now());
    next();
  })

Or multiple middleware functions on the GET call of some route like /users/pankaj

- app.use('/users', function (req, res, next) {
+ app.get('/user/:username', function (req, res, next) {
    console.log('Time:', Date.now());
    next();
+ }, function (req, res, next) {
+   // send an entry to system log
+   next();
  })


Some common middlewares for an Express app are:

  • Body Parser - Parses the request body and transforms it into a JavaScript Object for easy operation
  • Logger - Logs the requests to console or any other specific log
  • CORS - Enables CORS for requests; it depends on how the middleware is used.

Now how do we use middleware to make a modular application?

We will be doing so by separating functionalities with respect to route and then attaching them as middleware function on some route base.

  • We will create an instance of router from express
  • Then we will add basic CRUD routes to the router instance
  • Then we will add this route instance to another route-base in the main Express app

Let's create one basic route based middleware for Tweets:

// file-name: app-middlewares/tweets.js
const express = require('express');
const router = express.Router();

router.get('/:id', (req, res) => {
  const { id } = req.params;
  res.send({ id });
});

router.get('/feed', (req, res) => {
  res.send('Here get the feed');
});

module.exports = router;

Here we created two routes:

/feed → for the tweets feed
/:id → for the tweet of matching id

As you notice, we did not put anything related to the base route except for how it should behave on the mount point.

For now, these routes are not accessible as we have not mounted them in our app.

Not let's mount the route middleware on to the route /tweets and see how it behaves in the app:

  const express = require('express')

+ const appRouter = require('./app-middlewares/tweets');
  ...
  const app = express();
  const port = 3000;

+ app.use('/tweets', appRouter);
  ...
  app.listen(port, () => {
    console.log(`Example app listening on port ${port}!`)
  });

After this when you restart your node server, you will see that /feed is available as a child of /tweets making the final route into /tweets/feed Similarly, for /:id, it will become /tweets/:id.

Till now we understood the following:

  • How to create basic Express server
  • How to use MySQL in Express
  • What is Middleware
  • How to arrange our application with middleware functions

Now let's create resolver functions to provide data to the feed and the tweet endpoint.

Here we are trying to keep the data-layer and controller separate. This way any future changes in controller or data-layer are very minimally affecting each other.

Feed

For the feed, we need to provide items in a paginated fashion. But before, we need to think about what kind of pagination we should go for. That means we will have two ways to choose from for the pagination: -Limit and Offset Based -Pivot Based

Limit and Offset based pagination

Limit and Offset based pagination is good for systems where the paginated data is not realtime or frequently changing. If we do so, we will run into problems of duplicated entries in the feed.

Pivot based pagination

In a Pivot based pagination system, the items before or after the pivot are requested. It is very similar to the Limit and Offset based pagination; except that the data is sorted with the pivoted information as well: generally by creation timestamp.


For a social media feed, the data is very dynamic or let's say real-time. So here we will go for the Pivot based pagination system.

Now here in our DB design, we can go for Row ID or Timestamp for our pivot, as both are sortable entities in our DB design; this might change for NoSQL based design or if we choose to use UUID or strings hash for our IDs.

We will go ahead with ID column as our pivot, and the following will be our SQL query to get a feed with 50 items:

// query-builders/feed-query.js
module.exports = (user, pivot) => `
  SELECT username, tweet, timestamp
  FROM tweets WHERE ( user_id = ${user}
   OR ( user_id IN (
    SELECT user2_id FROM following WHERE user1_id = ${user}
   ) )
  ) ${pivot ? `AND id < ${pivot}` : ''}
  ORDER BY id DESC
  LIMIT 0, 50`;

Here special to notice that, if this is the first load, we don't need pivot; so we will send the first 50 from the Tweets table sorted in descending order by ID.

Ad when we will pass the ID pivot, it will give us the 50 records from the specified ID, omitting that specific ID.

So using the above query; our /feed endpoint looks like the following:

...
const connection = require('../helpers/connection');
const query = require('../helpers/query');
const feedQuery = require('../query-builders/feed-query');
...
router.get('/feed', async (req, res) => {
  const user = 1;
  const pivot = null;
  const conn = await connection(dbConfig).catch(e => {});
  const feed = await query(conn, feedQuery(user, pivot))
  res.send(feed);
});
...

And our rote for specific tweet itself:

...
const connection = require('../helpers/connection');
const query = require('../helpers/query');
...
router.get('/:id', async (req, res) => {
  const { id } = req.params;
  const conn = await connection(dbConfig).catch(e => {});
  const tweet = await query(conn, `SELECT * FROM TWEETS
    WHERE id = ?`, [id])
  res.send(tweet);
});
...

With the above things in place we need a few more endpoints like the following:

GET /user => logged in user
GET /user/followers => followers of the user
GET /user/following => all the friends user is following
GET, PUT /user/settings => to update things like password,name etc
POST /user/follow -> to follow some friend

Authentication

POST /auth/register -> register as new user
POST /auth/login -> login for users
GET /auth/confirm -> confirm the email for user
GET, POST /auth/forgot -> forgot password

For authentication, we will use third party authentication scripts such as Firebase or Auth0

The following code will take care of auth and set the session or JWT for logged in user.

This logged in user will be the basic initial information for all the other routes like feed or user-related info

Let's implement the user-related routes considering that we have information about currently logged-in user.

User Profile

// GET
// /user
...
router.get('/', async (req, res) => {
  const user = 1;
  const conn = await connection(dbConfig).catch(e => {});
  const currentUser = await query(conn, `SELECT * FROM USERS
    WHERE id = ?`, [user])
  res.send(currentUser);
});
...

User's Followers

// GET
// /user/followers
...
router.get('/followers', async (req, res) => {
  const user = 1;
  const conn = await connection(dbConfig).catch(e => {});
  const followers = await query(conn, `SELECT
    USER_INFO.*, username as user1_username
    FROM (SELECT 
    user1_id, user2_id, username as user2_username
    FROM FOLLOWING LEFT JOIN USERS ON user2_id = users.id
    WHERE user1_id = ?) as USER_INFO
    LEFT JOIN USERS ON user1_id = users.id`, [user])
  res.send(followers);
});
...

User's friends whom User is Following

// GET
// /user/following
...
router.get('/following', async (req, res) => {
  const user = 1;
  const conn = await connection(dbConfig).catch(e => {});
  const followers = await query(conn, `SELECT
    USER_INFO.*, username as user1_username
    FROM (SELECT 
    user1_id, user2_id, username as user2_username
    FROM FOLLOWING LEFT JOIN USERS ON user2_id = users.id
    WHERE user2_id = ?) as USER_INFO
    LEFT JOIN USERS ON user1_id = users.id`, [user])
  res.send(followers);
});
...

Following a new Friend by User

// POST
// /user/follow
...
router.post('/following', async (req, res) => {
  const user = 1;
  const { id } = req.params;
  const conn = await connection(dbConfig).catch(e => {});
  const follow = await query(conn, `INSERT INTO FOLLOWING
    (user1_id, user2_id)
    VALUE (?, ?)`, [user, id])
  res.send(follow);
});

User's settings

// GET, PUT
// /user/settings 
...
router.get('/settings', async (req, res) => {
  const user = 1;
  const conn = await connection(dbConfig).catch(e => {});
  const settings = await query(conn, `SELECT * FROM SETTINGS WHERE user_id = ?`, [user])
  res.send(settings);
});
router.put('/settings', async (req, res) => {
  const user = 1;
  const vals = req.body;
  const values = Object.keys(vals).map(k => `${k}=${vals[k]}`);
  const conn = await connection(dbConfig).catch(e => {});
  const status = await query(conn, `UPDATE SETTINGS
        SET ? WHERE user_id = ?`, [values, user])
  res.send(status);
});
...


Attaching User Routes to App

As we created all above routes middleware for a user; let's mount this middleware on the /user route base:

  ...
- const appRouter = require('./app-middlewares/tweets');
+ const tweetsRouter = require('./app-middlewares/tweets');
+ const userRouter = require('./app-middlewares/user');
  ...
- app.use('/tweets', appRouter);
+ app.use('/tweets', tweetsRouter);
+ app.use('/user', userRouter);
  ...


And this how we have created:

  • REST(ish) API with Node.js and Express
  • Twitter-like application API with Node.js, Express, and MySQL

You can download following postman collection to browse through the API

View/Download the above code from this repo: Github: express-mysql


Conclusion

So in this article, we saw how to create APIs with Node.js, Express, and MySQL and how to develop the application in a maintainable and modular way.

Let me know what do you think about this article through comments 💬 or on Twitter at @patel_pankaj_ and @time2hack

If you find this article helpful, please share it with others 🗣; subscribe to the blog for new posts and see you the next time.

Learn Complete PHP & MYSQL Programming From Scratch

Learn Complete PHP & MYSQL Programming From Scratch

Learn Complete PHP & MYSQL Programming From Scratch

Description
Are you new to PHP or need a refresher? Then this course will help you get all the fundamentals of Procedural PHP, Object Oriented PHP, MYSQLi ,Ajax,XML.

Do you want to be a web developer? Do you need to brush up on your PHP skills? Then you’re in the right place!

You will learn everything from the basics to more advanced PHP programming using real world examples and sample projects.

Why?

Because Millions of websites and applications (the majority) use PHP. You can find a job anywhere or even work on your own, online and in places like freelancer or Odesk. You can definitely make a substantial income once you learn it.

My Promise to You

I created this course not just to make money but to teach you the knowledge that will help you with PHP since I was in your shoes some years ago. If you need support, I will be just a message or an email away. I love what I do, but most importantly I love when my students succeed and that for me is priceless. You success depends on yours and I will make sure with that you get all the help you need when you need it.

Who is the target audience?

This course is meant for all level students who want to learn php.
Aspiring web developers
Current web developers
Basic knowledge
Some HTML is needed for this course.
A Computer
A Desire to Learn
What will you learn
You will learn how to use Databases
You will learn MySQL
Object Oriented Programming
How to use forms to submit data to databases
All PHP Fundamentals and Building Blocks with practical implementation in Projects
Form Validation with most Secure way using Regular Expressions
Making web pages dynamic with the variety of PHP Techniques
Understand and use Javascript, Ajax,Xml
Login Logout Admin System
Full
Password hashing
To continue:

JavaScript: ForEach and For…In - The difference

JavaScript: ForEach and For…In - The difference

This is going to be a quick introduction to foreach, and for...in in JavaScript. This article was written to introduce you to new methods that you can you can use instead of always using for loops.

JavaScript — The difference between ForEach, and For…In. Learn what ForEach and For…In do in JavaScript

This is going to be a quick introduction to foreach, and for...in in JavaScript. This article was written to introduce you to new methods that you can you can use instead of always using for loops.

The For Loop

Here’s a quick ‘n simple refresher on the for loop:

You’ve probably used a for loop before. It’s the most basic of loops in JavaScript and is quite versatile. Here’s the basic for loop syntax:

for (i = 0; i < 10; i++) { 
  // do something
}

Our for loop consists of three statements, one that is executed before our loop starts ( i = 0 ), one that defines how long our loop should run ( i < 10 ), and one that is executed after each loop ( i++ ).

In this example, we are setting i = 0 before our loop starts. We will continue to loop as long as i < 10, and each iteration of the loop will increase i by one. Finally, within our brackets is the code that will be run on each iteration of the loop.

ForEach

forEach is an Array method that we can use to execute a function on each element in an array. It can only be used on Arrays, Maps, and Sets.

A simple example would be to console.log each element of an array. Here’s what this might look like with a for loop:

const arr = ['cat', 'dog', 'fish'];

for (i = 0; i < arr.length; i++) { 
  console.log(arr[i])
}

// cat
// dog
// fish

Great. It works. Here’s how we accomplish the same thing with the forEach() method:

const arr = ['cat', 'dog', 'fish'];

arr.forEach(element => {
  console.log(element);
});

// cat
// dog
// fish

When using forEach, we simply have to specify a callback function. This callback will be executed on each element in the array.

For…In

Here’s what the for...in syntax looks like:

for (variable in object) {  
  // do something
}

for...in is used to iterate over the enumerable properties of objects. Every property in an object will have an Enumerable value — if that value is set to true, then the property is Enumerable.

Remember, when you create an Object it will inherit certain methods from its prototype. For example, the Object.keys() method. These are non-enumerable. Any properties you add to an object will for the most part be enumerable. Lets look at an example to help with the understanding. In the example below we’ll log out each enumerable value in the object:

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

for (let elem in obj) {  
  console.log( obj[elem] )
}

// 1
// 2
// 3
// 4

Better yet, we can log out both the key and value pair:

for (let elem in obj) {
  console.log(`${elem} = ${obj[elem]}`);
}

// a = 1
// b = 2
// c = 3
// d = 4

Don’t forget, arrays are objects too — which means we can also use the for...in loop on Arrays:

const arr = ['cat', 'dog', 'fish'];

for (let i in arr) {  
  console.log(arr[i])
}

// cat
// dog
// fish

And since each character in a string has an index, we can even use for...in on strings. Check this out:

const string = 'hello';

for (let character in string) {  
    console.log(string[character])
}

// h
// e
// l
// l
// o

Note: The for...in loop executes in an arbitrary order and should not be relied upon if you need to loop in a specific order.

Notes:

Thanks for reading, and hopefully this was helpful!. If you like this tutorial please share it with others.

New Upcoming JavaScript features in 2020

New Upcoming JavaScript features in 2020

You should know if you use JavaScript everyday. That is "New Upcoming JavaScript features" in this post

Upcoming new JavaScript features You should know if you use JavaScript everyday

Since ECMAScript2015 (also called ES6) was released, JavaScript has changed and improved widely. This is excellent news for all of the JavaScript developers. Furthermore, a new ECMAScript version has released every year. You likely didn’t notice what features were added in the latest ECMAScript, which was released in June 2019. I will briefly show you the new features added in the latest version and talk about the new features for the future version.

The features that I will show you are NOT yet decided to be in the next version. All of what I will talk about in this post are currently in stage 3. Check out this repo if you want to get more details.

Features in ECMAScript2019 (ES10)

1. Array.prototype.flat

A method that creates a new array with all sub-array elements concatenated into it recursively up to the specified depth.

const array = [1, 2, [3, 4]];
array.flat(); // [1, 2, 3, 4];

This is very useful, especially when you want to flatten your nested array. If the depth of your array is deeper than one depth, calling flat once can’t entirely flatten your array. flat takes a parameter for depth, which refers to how many depths you want it to go into to flatten the array.

// Crazy example
const crazyArray = [1, 2, [3, 4], [[5], [6, [7,8]]]];
crazyArray.flat(Infinity); // [1, 2, 3, 4, 5, 6, 7, 8];
// The parameter must be the number type

The deeper you want to search the array, the more computing time will be required to flatten it. Note that IEs and Edge do not support this feature.

2. Array.prototype.flatMap

A method first maps each element using a mapping function, then flattens the result into a new array.

const arr = ["it's Sunny in", "", "California"];
arr.flatMap(x => x.split(" "));
// ["it's","Sunny","in", "", "California"]

The difference between flat and flatMap is that you can put a custom function in flatMap to manipulate each value. Additionally, unlike flat, flatMap flattens one depth array, only. The return value should be an array type. This would be very useful when you should do something before flattening the array.

There were more features added to ES10. if you want to know more about them.

New Features In Stage 3

In stage 3, there are a few interesting features suggested. I will introduce some of them to you briefly.

Numeric Separators

When you assigned a big number to a variable, weren’t you confused on how big that number is or if you wrote it right? This proposal allows you to put an underscore between numbers so you can count it easier.

1_000_000_000           // Ah, so a billion
101_475_938.38          // And this is hundreds of millions

let fee = 123_00;       // $123 (12300 cents, apparently)
let fee = 12_300;       // $12,300 (woah, that fee!)
let amount = 12345_00;  // 12,345 (1234500 cents, apparently)
let amount = 123_4500;  // 123.45 (4-fixed financial)
let amount = 1_234_500; // 1,234,500

let budget = 1_000_000_000_000;
// What is the value of `budget`? It's 1 trillion!
// 
// Let's confirm:
console.log(budget === 10 ** 12); // true

It will be up to each developer whether to use this feature once it’s released, but one thing’s for sure, this feature would reduce your headaches for counting how big a number is!

Top-level await

Top-level _await_ enables modules to act as big async functions: With top-level _await_, ECMAScript Modules (ESM) can _await_ resources, causing other modules who _import_ them to wait before they start evaluating their body.

The motivation of this feature was that when you import a module which has async function, the output of the async function is undefined.

// awaiting.mjs
import { process } from "./some-module.mjs";
const dynamic = import(computedModuleSpecifier);
const data = fetch(url);
export const output = process((await dynamic).default, await data);

There are two files. output could be undefined if it’s called before the Promises tasks are done.

// usage.mjs
import { output } from "./awaiting.mjs";
export function outputPlusValue(value) { return output + value }
console.log(outputPlusValue(100));
setTimeout(() => console.log(outputPlusValue(100), 1000);

usage.mjs will not execute any of the statements in it until the awaits in awaiting.mjs have had their Promises resolved.

Nullish Coalescing for JavaScript

This would be one of the most useful features amongst proposals in stage 3. We often wrote this kind of code.

const obj = { 
  name: 'James'
};

const name = obj.name || 'Jane'; // James

If obj.name is falsy, then return ‘Jane’, so undefined won’t be returned. But the problem is, an empty string(‘’) is also considered falsy in this case. Then we should rewrite it again like this below.

const name = (obj.name && obj.name !== '') || 'Jane';

It is a pain in the neck to write the code like that every time. This proposal allows you to check null and undefined only.

const response = {
  settings: {
    nullValue: null,
    height: 400,
    animationDuration: 0,
    headerText: '',
    showSplashScreen: false
  }
};

const undefinedValue = response.settings.undefinedValue ?? 'some other default'; // result: 'some other default'
const nullValue = response.settings.nullValue ?? 'some other default'; // result: 'some other default'
const headerText = response.settings.headerText ?? 'Hello, world!'; // result: ''
const animationDuration = response.settings.animationDuration ?? 300; // result: 0
const showSplashScreen = response.settings.showSplashScreen ?? true; // result: false

Optional Chaining

This proposal goes with Nullish Coalescing for JavaScript, especially in TypeScript. TypeScript has announced that they will include Nullish Coalescing for JavaScript and this proposal in their next released version, 3.7.0.

const city = country && country.city; 
// undefined if city doesn't exist

Look at the example code. To get city, which is in country object, we should check if country exists and if city exists in country.

With Optional Chaining, this code can be refactored like this.

const city = country?.city; // undefined if city doesn't exist

This feature seems very handy and useful for this situation.

import { fetch } from '../yourFetch.js';
(async () => {
  const res = await fetch();
  // res && res.data && res.data.cities || undefined
  const cities = res?.data?.cities;
})();

Promise.any

_Promise.any_ accepts an iterable of promises and returns a promise that is fulfilled by the first given promise to be fulfilled, or rejected with an array of rejection reasons if all of the given promises are rejected.

With async-await,

try {
  const first = await Promise.any(promises);
  // Any of the promises was fulfilled.
} catch (error) {
  // All of the promises were rejected.
}

With Promise pattern,

Promise.any(promises).then(
  (first) => {
    // Any of the promises was fulfilled.
  },
  (error) => {
    // All of the promises were rejected.
  }
);

Since there were Promise all, allSettled, and race, there wasn’t any. So this feature is simple but powerful for a needed situation.

However, this proposal wasn’t tested yet, so this proposal might have a longer time to be accepted in a future version of ECMAScript.

Conclusion

There are so many interesting proposals in stage 3. I can’t wait to meet them in ES11 or ES12. Of course, I won’t need all of them, but some of them would definitely make my codes more elegant.

Thank you ! If you liked this post, share it with all of your programming buddies!