Categories
Express JavaScript

Guide to the Express Request Object — Queries and Cookies

The request object lets us get the information about requests made from the client in middlewares and route handlers.

In this article, we’ll look at the properties of Express’s request object in detail, including query strings, URL parameters, and signed cookies.

Request Object

The req parameter we have in the route handlers above is the req object.

It has some properties that we can use to get data about the request that’s made from the client-side. The more important ones are listed below.

req.originalUrl

The originalUrl property has the original request URL.

For example, we can use it as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();app.get('/', (req, res) => {  
  res.send(req.originalUrl);  
})app.listen(3000);

Then when we have the request with query string /?foo=bar , we get back /?foo=bar .

req.params

params property has the request parameters from the URL.

For example, if we have:

const express = require('express')  
const app = express()app.use(express.json())  
app.use(express.urlencoded({ extended: true }))app.get('/:name/:age', (req, res) => {  
  res.json(req.params)  
})app.listen(3000, () => console.log('server started'));

Then when we pass in /john/1 as the parameter part of the URL, then we get:

{  
    "name": "john",  
    "age": "1"  
}

as the response from the route above.

req.path

The path property has the path part of the request URL.

For instance, if we have:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();app.get('/:name/:age', (req, res) => {  
  res.send(req.path);  
})app.listen(3000);

and make a request to /foo/1 , then we get back the same thing in the response.

req.protocol

We can get the protocol string with the protocol property.

For example, if we have:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();app.get('/', (req, res) => {  
  res.send(req.protocol);  
})app.listen(3000);

Then when we make a request through HTTP we get backhttp .

req.query

The query property gets us the query string from the request URL parsed into an object.

For example, if we have:

const express = require('express')  
const app = express()
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res) => {  
  res.json(req.query)  
})
app.listen(3000, () => console.log('server started'));

Then when we append ?name=john&age=1 to the end of the hostname, then we get back:

{  
    "name": "john",  
    "age": "1"  
}

from the response.

req.route

We get the current matched route with the route property.

For example, we can use it as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();app.get('/', (req, res) => {  
  console.log(req.route);  
  res.json(req.route);  
})app.listen(3000);

Then we get something like the following from the console.log :

Route {  
  path: '/',  
  stack:  
   [ Layer {  
       handle: [Function],  
       name: '<anonymous>',  
       params: undefined,  
       path: undefined,  
       keys: [],  
       regexp: /^/?$/i,  
       method: 'get' } ],  
  methods: { get: true } }

req.secure

A boolean property that indicates if the request is made with via HTTPS.

For example, given that we have:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();
app.get('/', (req, res) => {    
  res.json(req.secure);  
})
app.listen(3000);

Then we get false if the request is made via HTTP.

req.signedCookies

The signedCookies object has the cookie with the value deciphers from the hashed value.

For example, we can use it as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const cookieParser = require('cookie-parser');  
const app = express();  
app.use(cookieParser('secret'));
app.get('/cookie', (req, res, next) => {  
  res.cookie('name', 'foo', { signed: true })  
  res.send();  
})
app.get('/', (req, res) => {  
  res.send(req.signedCookies.name);  
})
app.listen(3000);

In the code above, we have the /cookie route to send the cookie from our app to the client. Then we can get the value from the client and then make a request to the / route with the Cookie header with name as the key and the signed value from the /cookie route as the value.

For example, it would look something like:

name=s%3Afoo.dzukRpPHVT1u4g9h6l0nV6mk9KRNKEGuTpW1LkzWLbQ

Then we get back foo from the / route after the signed cookie is decoded.

req.stale

stale is the opposite of fresh . See req.fresh for more details.

req.subdomains

We can get an array of subdomains in the domain name of the request with the subdomains property.

For example, we can use it as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();
app.get('/', (req, res) => {  
  res.send(req.subdomains);  
})
app.listen(3000);

Then when we make a request to the / route, we get something like:

["beautifulanxiousflatassembler--five-nine"]

req.xhr

A boolean property that’s true is X-Requested-With request header field is XMLHttpRequest .

For example, we can use it as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const app = express();
app.get('/', (req, res) => {  
  res.send(req.xhr);  
})
app.listen(3000);

Then when we make a request to the/ route with the X-Requested-With header set to XMLHttpRequest , we get back true .

Conclusion

There’re many properties in the request object to get many things.

We can get signed cookies with the Cookie Parser middleware. Query strings and URL parameters can access in various form with query , params , and route properties.

Also, we can get parts of the subdomain with the subdomains property.

Categories
Flow JavaScript

JavaScript Type Checking with Flow — Generics

ow 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 use generic types to make data types abstract and allows reuse of the code.

Defining Generic Types

To make the types abstract in different entities, we can use generic types to abstract away the types from entities.

For example, we can write a function with generic types as follows:

function foo<T>(obj: T): T {  
  return obj;  
}

In the code above, we have <T> to indicate that foo is a generic function. T is the generic type marker whenever it’s referenced.

To make use of generic types, we have to annotate it. Otherwise, Flow wouldn’t know that it can take a generic type.

We have to annotate the type in type aliases when we want to generic type in type aliases.

For example, we can write:

type Foo = {  
  func<T>(T): T  
}

Then it’ll fail if we write:

type Foo = {  
  func<T>(T): T  
}function foo(value) {  
  return value;  
}const f: Foo = { func: foo };

Since we didn’t add generic type markers to our foo function.

Once we annotate our foo function with generic types, it should work:

type Foo = {  
  func<T>(T): T  
}function foo<T>(value: T): T {  
  return value;  
}const f: Foo = { func: foo };

Syntax

Generic Functions

We can define a generic function as follows:

function foo<T>(param: T): T {  
  
}  
  
function<T>(param: T): T {  
  
}

Also, we can define function types with generics as follows:

<T>(param: T) => T

We can use generic function types with variables and parameters like:

let foo: <T>(param: T) => T = function(param: T): T {}function bar(callback: <T>(param: T) => T) {  
    
}

Generic Classes

To create generic classes, we can insert type placeholders into fields, method parameters, and the return type of methods.

For instance, we can define a generic class as follows:

class Foo<T> {  
  prop: T; constructor(param: T) {  
    this.prop = param;  
  } bar(): T {  
    return this.prop;  
  }  
}

Type Aliases

Generic types can also be added to type aliases. For example, we can write:

type Foo<T> = {  
  a: T,  
  v: T,  
};

Interfaces

Likewise, we can define interfaces with generics as follows:

interface Foo<T> {  
  a: T,  
  b: T,  
}

Passing in Type Arguments

For functions, we can pass in type arguments as follows:

function foo<T>(param: T): T {    
  return param;  
}foo<number>(1);

We can pass in type arguments to classes as well. When we instantiate a class, we can pass in a type argument as follows:

class Foo<T> {}  
const c = new Foo<number>();

One convenient feature of Flow generics is that we don’t have to add all the types ourselves. We can put an _ in place of a type to let Flow infer the type for us.

For instance, we can write the following:

class Foo<T, U, V>{}  
const c = new Foo<_, number, _>()

To let Flow infer the type of T and V.

Behavior

Generic are variables for types. We can use them in place of any data type annotations in our code.

Also, we can name them anything we like, so we can write something like:

function foo<Type1, Type2, Type3>(one: Type1, two: Type2, three: Type3) {  
    
}

Flow tracks the values of variables and parameters annotated with generic types so we can assign something unexpected to it.

For example, we’ll get an error if we write something like:

function foo<T>(value: T): T {    
  return "foo";  
}

Since we don’t know that if T is a string, we can always return a string when foo is called.

Also, Flow tracks the type of value we pass through a generic so we can use it later:

function foo<T>(value: T): T {  
  return value;  
}

let one: 1 = foo(1);

Notice that we didn’t pass in any generic type argument for it to identify the type as 1. Changing 1 to number also works:

let one: number = foo(1);

If we omit the generic type argument in a generic function, Flow will let us pass in anything:

function logBar<T>(obj: T): T {  
  if (obj && obj.bar) {  
    console.log(obj.bar);  
  }  
  return obj;  
}

logBar({ foo: 'foo', bar: 'bar' });    
logBar({ bar: 'bar' });

We can restrict what we can pass in by specifying restrictions for the type parameter:

function logBar<T: { bar: string }>(obj: T): T {  
  if (obj && obj.bar) {  
    console.log(obj.bar);  
  }  
  return obj;  
}

logBar({ foo: 'foo', bar: 'bar' });    
logBar({ bar: 'bar' });

After adding { bar: string } , then we know that anything passed in must have the stringbar property.

We can do the same for primitive values:

function foo<T: number>(obj: T): T {  
  return obj;  
}

foo(1);

If we try to pass in data of any other type, it’ll fail, so

foo('2');

will get us an error.

Generics lets us return a more specific type than we specify in the type parameter. For example, if we have a string function that returns the value that’s passed in:

function foo<T: string>(val: T): T {  
  return val;  
}

let f: 'foo' = foo('foo');

Instead of assigning something to a string variable, we can assign something that has the returned value of the function as the type.

Parameterized Generics

We can pass in types to generics like we pass arguments to a function. We can do this with type alias, functions, interfaces, and classes. This is called a parameterized generic.

For example, we can make a parameterized generic type alias as follows:

type Foo<T> = {  
  prop: T,  
}

let item: Foo<string> = {  
  prop: "value"  
};

Likewise, we can do the same for classes:

class Foo<T> {  
  value: T;  
  constructor(value: T) {  
    this.value = value;  
  }  
}

let foo: Foo<string> = new Foo('foo');

For interfaces, we can write:

interface Foo<T> {  
  prop: T,  
}

class Item {  
  prop: string;  
}(Item.prototype: Foo<string>);

Default Type for Parameterized Generics

We can add default values to parameterized generics. For example, we can write:

type Item<T: string = 'foo'> = {  
  prop: T,  
};

let foo: Item<> = { prop: 'foo' };

If the type isn’t specified, then prop is assumed to have the 'foo' type.

That means that any other value for prop won’t work if we leave the type argument blank. So something like:

let foo: Item<> = { prop: 'bar' };

won’t be accepted.

Variance Sigils

We can use the + sign to allow for broader types than the assigned value’s type when the casting types of generics. For example, we can write:

type Foo<+T> = T;let x: Foo<string> = 'foo';  
(x: Foo<number| string>);

As we can see, we can convert x from type Foo<string> to Foo<number| string> with the + sign on the generic Foo type.

With generics, we can abstract the type out of our code by replacing them with generic type markers. We can use them anywhere that has type annotations. Also, it works with any Flow constructs like interfaces, type alias, classes, variables, and parameters.

Categories
JavaScript TypeScript

Introduction to TypeScript Classes — More Access Modifiers

Classes in TypeScript, like JavaScript are a special syntax for its prototypical inheritance model that is a comparable inheritance in class-based object oriented languages. Classes are just special functions added to ES6 that are meant to mimic the class keyword from these other languages. In JavaScript, we can have class declarations and class expressions, because they are just functions. So like all other functions, there are function declarations and function expressions. This is the same with TypeScript. Classes serve as templates to create new objects. TypeScript extends the syntax of classes of JavaScript and then add its own twists to it. In this article, we’ll look at more access modifiers for class members in TypeScript.

Readonly modifier

With TypeScript, we can mark a class member as read only with the readonly keyword. This prevents a member from being modified once it’s been initialized. Also, they must be initialized at their declaration or in the constructor. For example, we can use it like in the following code:

class Person {  
  readonly name: string;  
  constructor(name: string) {  
    this.name = name;        
  }  
}

const person = new Person('Jane');

If we try assign another to it after a value has been set to name after initialization, then we would get an error. For example, if we write:

class Person {  
  readonly name: string;  
  constructor(name: string) {  
    this.name = name;        
  }  
}  
const person = new Person('Jane');  
person.name = 'Joe';

Then the TypeScript compiler won’t compile the code and we would get the error message “Cannot assign to ‘name’ because it is a read-only property.(2540)“

We can make the code above shorter by using parameter properties. With parameter properties, we can both declare a readonly member and assign it a value by just putting the member declaration in the signature of the constructor. Once we put it inside the parentheses, then we can both declare it and assign it a value at the same time. For example, instead of writing:

class Person {  
  readonly name: string;  
  constructor(name: string) {  
    this.name = name;        
  }  
}

const person = new Person('Jane');

We can instead write:

class Person {    
  constructor(readonly name: string) {      
  }  
}

const person = new Person('Jane');

Then when we run console.log on person , then we can see that the member name has been assigned the value ‘Jane’ without having to explicitly write code to assign it the value. We can also replace readonly , with public , private , or protected ; or combine readonly with public , private , or protected . For example, we can write the following code:

class Person {    
  constructor(private readonly name: string) {      
  } getName() {  
    return this.name;  
  }  
}  
const person = new Person('Jane');  
console.log(person.getName());

to get a private and readonly member called name , which retrieve the value of it in the getName method. We should get ‘Jane’ when we run the console.log line on the last line. Likewise, we can do the same with public or protected like in the following code:

class Person {    
  constructor(protected readonly name: string) {      
  }  
}

class Employee extends Person {    
  constructor(  
    public name: string,   
    private employeeCode: number  
  ){      
    super(name);  
  } 

  getEmployeeCode() {  
    return this.employeeCode;  
  }  
}  
const person = new Employee('Jane', 123);  
console.log(person.name);  
console.log(person.getEmployeeCode());

As we can see, we have parameter properties that with all kinds of access modifiers and the readonly keyword in both the Person and the Employee class. Then we can use the Employee constructor to assign all the values to all the members with the Employee constructor. Then when we get the values of the members either directly or through a method as in the case of the private employeeCode member, where we retrieved it through the getEmployeeCode method, then we can see that the values we expected are logged. We see that person.name is ‘Jane’, and person.getEmployeeCode() gets us 123.

Accessors

Like in JavaScript, we can add getter and setter methods into TypeScript classes. This prevents us from accidentally modifying public member values directly, and gives us more control for how member values are retrieved and set. To add getters and setters to a class, we can use the get and set keywords respectively. We put them in front of the method signature of the class to designate a method as a getter or a setter. For example, we can use it like in the following code:

class Person {  
  private _name: string = ''; get name(): string {  
    return this._name;          
  } 

  set name(newName: string) {  
    if (newName && newName.length < 5) {  
      throw new Error('Name is too short');        
    }  
    this._name = newName;  
  }  
}

let person = new Person();  
person.name = 'Jane Smith';  
console.log(person.name);  
person.name = 'Joe';

In the example above, we added a getter method with the get keyword. The name method is used for getting the _name field, which is private, so we can’t get the value of it without a getter method. To retrieve the value of this._name via our getter name method, we just use the person.name property to get it. Then we to set the value of this._name , we add a setter method with the set keyword and the method name name . In the name setter method, we pass in the parameter which let us assign a value to it with the assignment operator like we did in the third last line in the code above.

As we can see, we can put validation code in the set name method. This is one good reason to use getter and setter methods because we can control how values are set for individual class members. In the example above, if the value we assign has less than 5 characters, then we throw an errors which has the message ‘Name is too short’. This prevents us from assigning a value where the string is less than 5 characters. If we run the code, the first assignment expression:

person.name = 'Jane Smith';

Then we get ‘Jane Smith’ logged. When we try to assign it a value that has less than 5 characters like we did with:

person.name = 'Joe';

Then we get an error raised like we have indicated in the code.

Note that to use accessors, we have to compile our output to ES5 or higher. Compiling to ES3 isn’t supported, but this should be a problems with modern browsers. Also, accessors that has a get but no set are automatically inferred as readonly .

In TypeScript, we have the readonly modifier for class members so that they won’t be able to be set to a new value after they have been initialized. Also, TypeScript has the parameter properties features so that we don’t have to write code explicitly to assign values to variables via the constructor. If we add in the parameters in the constructor, then they’ll be set automatically when we instantiate the class with the new keyword. The accessor methods are useful for controlling how we get and set values of class members. We can designate getter and setter methods with the get and set keywords.

Categories
GraphQL JavaScript Nodejs

An Introduction to GraphQL

GraphQL is a query language for our API and a server-side runtime for running queries by using a type system for our data.

In this article, we’ll look at how to make basic queries to a GraphQL API.

Defining the API

We define an API by defining the types and fields for those types and provide functions for each field on each type.

For example, if we have the following type:

type Query {  
  person: Person  
}

Then we have to create a function for the corresponding type to return the data:

function Query_person(request) {  
  return request.person;  
}

Making Queries

Once we have a GraphQL service running, we can send GraphQL queries to validate and execute on the server.

For example, we can make a query as follows:

{  
  person {  
    firstName  
  }  
}

Then we may get JSON like the following:

{  
  "person": {  
    "firstName": "Joe"  
  }  
}

Queries and Mutations

Queries are for getting data from the GraphQL server and mutations are used for manipulating data stored on the server.

For example, the following is a query to get a person’s name:

{  
  person {  
    name  
  }  
}

Then we may get the following JSON from the server:

{  
  "data": {  
    "person": {  
      "name": "Joe"  
    }  
  }  
}

The field name returns a String type.

We can change the query as we wish if we want to get more data. For example, if we write the following query:

{  
  person {  
    name  
    friends {  
      name  
    }  
  }  
}

Then we may get something like the following as a response:

{  
  "data": {  
    "person": {  
      "name": "Joe",  
      "friends": [  
        {  
          "name": "Jane"  
        },  
        {  
          "name": "John"  
        }  
      ]  
    }  
  }  
}

The example above has friends being an array. They look the same from the query perspectively, but the server knows what to return based on the type specified.

Arguments

We can pass in arguments to queries and mutations. We can do a lot more with queries if we pass in arguments to it.

For example, we can pass in an argument as follows:

{  
  person(id: "1000") {  
    name      
  }  
}

Then we get something like:

{  
  "data": {  
    "person": {  
      "name": "Luke"  
    }  
  }  
}

from the server.

With GraphQL, we can pass in arguments to nested objects. For example, we can write:

{  
  person(id: "1000") {  
    name  
    height(unit: METER)  
  }  
}

Then we may get the following response:

{  
  "data": {  
    "person": {  
      "name": "Luke",  
      "height": 1.9  
    }  
  }  
}

In the example, the height field has a unit which is an enum type that represents a finite set of values.

unit may either be METER or FOOT.

Fragments

We can define fragments to let us reuse complex queries.

For example, we can define a fragment and use it as follows:

{  
  leftComparison: person(episode: FOO) {  
    ...comparisonFields  
  }  
  rightComparison: person(episode: BAR) {  
    ...comparisonFields  
  }  
}  
​  
fragment comparisonFields on Character {  
  name  
  appearsIn  
  friends {  
    name  
  }  
}

In the code above, we defined the comparisonFields fragment which has the list of fields we want to include in each query.

Then we have the leftComparison and rightComparison queries which include the fields of the comparisonFields fragment by using the ... operator.

Then we get something like:

{  
  "data": {  
    "leftComparison": {  
      "name": "Luke",  
      "appearsIn": [  
        "FOO",  
        "BAR"  
      ],  
      "friends": [  
        {  
          "name": "Jane"  
        },  
        {  
          "name": "John"  
        }  
      ]  
    },  
    "rightComparison": {  
      "name": "Mary",  
      "appearsIn": [  
        "FOO",  
        "BAR"  
      ],  
      "friends": [  
        {  
          "name": "Mary"  
        },  
        {  
          "name": "Alex"  
        }  
      ]  
    }  
  }  
}

Using variables inside fragments

We can pass in variables into fragments as follows:

query PersonComparison($first: Int = 3){  
  leftComparison: person(episode: FOO) {  
    ...comparisonFields  
  }  
  rightComparison: person(episode: BAR) {  
    ...comparisonFields  
  }  
}  
​  
fragment comparisonFields on Character {  
  name  
  appearsIn  
  friends(first: $first) {  
    name  
  }  
}

Then we may get something like:

{  
  "data": {  
    "leftComparison": {  
      "name": "Luke",  
      "appearsIn": [  
        "FOO",  
        "BAR"  
      ],  
      "friends": [  
        {  
          "name": "Jane"  
        },  
        {  
          "name": "John"  
        }  
      ]  
    },  
    "rightComparison": {  
      "name": "Mary",  
      "appearsIn": [  
        "FOO",  
        "BAR"  
      ],  
      "friends": [  
        {  
          "name": "Mary"  
        },  
        {  
          "name": "Alex"  
        }  
      ]  
    }  
  }  
}

as a response.

The operation type may either be a query, mutation, or subscription and describes what operator we’re intending to do. It’s required unless we’re using the query shorthand syntax. In that case, we can’t supply a name or variable definition for our operation.

The operation name is a meaningful and explicit name for our operation. It’s required in multi-operation documents. But its use is encouraged because it’s helpful for debugging and server-side logging.

It’s easy to identify the operation with a name.

Conclusion

GraphQL is a query language that lets us send requests to a server in a clear way. It works by sending nested objects with operation type and name along with any variables to the server.

Then the server will return us the response that we’re looking for.

Operation types include queries for getting data and mutations for making changes to data on the server.

Categories
JavaScript Rxjs

More Rxjs Transformation Operators — Group and Map

Rxjs is a library for doing reactive programming. Creation operators are useful for generating data from various data sources to be subscribed to by Observers.

In this article, we’ll look at some transformation operators like groupBy , map , mapTo and mergeMap .

groupBy

The groupBy operator takes values emitted by the source Observable and then group them by the criteria that we set for them.

It takes 4 arguments. The first is the keySelector , which is a function that extracts the key for each item.

The second is an optional argument for the elementSelector , which is a function that extracts the return element for each item.

The 3rd argument is the durationSelector , which is an optional function that returns an Observable to determine how long each group should exist.

Finally, the last argument is the subjectSelector , which is an optional function that returns a Subject.

For example, we can use it as follows:

import { of } from "rxjs";  
import { groupBy, reduce, mergeMap } from "rxjs/operators";
const observable = of(  
  { id: 1, name: "John" },  
  { id: 2, name: "Jane" },  
  { id: 2, name: "Mary" },  
  { id: 1, name: "Joe" },  
  { id: 3, name: "Don" }  
).pipe(  
  groupBy(p => p.id),  
  mergeMap(group$ => group$.pipe(reduce((acc, cur) => [...acc, cur], [])))  
);

observable.subscribe(val => console.log(val));

In the code above, we called groupBy(p => p.id) to group the items emitted from the of Observable by id .

Then we have:

mergeMap(group$ => group$.pipe(reduce((acc, cur) => [...acc, cur], [])))

to get the grouped items together to be emitted by one Observable.

map

The map operator lets us map the values emitted by the source Observable to the other values and emits the resulting values as an Observable.

It takes up to 2 arguments. The first argument is the project function, which is required. The function takes the emitted value of the source Observable and the index of it and then returns what we want by manipulating those.

The second argument is optional. It’s the thisArg , which is used to define the this value for the project function in the first argument.

For example, we can use it as follows:

import { of } from "rxjs";  
import { map } from "rxjs/operators";
const observable = of(1, 2, 3);  
const newObservable = observable.pipe(map(val => val ** 2));  
newObservable.subscribe(x => console.log(x));

The code above takes the emitted values from observable , then pass it to the map operator via the pipe operator. In the callback, we passed into the map operator, we exponentiate the originally emitted value to the power of 2.

Then we can subscribe to an Observable that emits the new values and we get:

1  
4  
9

logged.

mapTo

The mapTo operator emits the given constant value for any source Observable’s emitted value.

It takes one argument, which is the value to emit.

For example, we can use it as follows:

import { of } from "rxjs";  
import { mapTo } from "rxjs/operators";
const observable = of(1, 2, 3);  
const newObservable = observable.pipe(mapTo("foo"));  
newObservable.subscribe(x => console.log(x));

The code above maps all the values from observable to the value 'foo' , so we get 'foo' 3 times instead of 1, 2 and 3.

mergeMap

The mergeMap operator takes the values emitted from a source Observable and then lets us combine it with the values of another Observable.

It takes up to 3 arguments. The first is a project function to project the values and return a new Observable.

The second argument is an optional argument that takes an resultSelector . We can pass in a function to select the values to emit from the result.

The third argument is the concurrency , which is an optional argument that specifies the maximum number of input Observables to be subscribed to concurrently. The default is Number.POSITIVE_INFINITY .

For example, we can use it as follows:

import { of } from "rxjs";  
import { mergeMap, map } from "rxjs/operators";
const nums = of(1, 2, 3);  
const result = nums.pipe(mergeMap(x => of(4, 5, 6).pipe(map(i => x + i))));  
result.subscribe(x => console.log(x));

The code above will get the values from the 3 values emitted from the nums Observable, then pass the values to the mergeMap ‘s callback function via the pipe operator. x will have the values from nums .

Then in the callback, we have the of(4, 5, 6) Observable, which have the values from combined from the nums Observable. i has the values from the of(4, 5, 6) Observable, so we the values from both Observables added together. We get 1 + 4, 1 + 5, 1 + 6, 2 + 4, 2 + 5, 2 + 6 and so on.

In the end, we should get the following output:

5  
6  
7  
6  
7  
8  
7  
8  
9

The groupBy operator takes values emitted by the source Observable and then group them by the criteria that we set for them. We can use it in conjunction with the mergeMap to combined the results grouped by the groupBy operator into one Observable.

The map operator lets us map the values emitted by the source Observable to the other values and emits the resulting values as an Observable.

The mapTo operator emits the given constant value for any source Observable’s emitted value.

Finally mergeMap operator takes the values emitted from a source Observable and then lets us combine it with the values of another Observable. Then we get an Observable with the values of both Observables combined together.