Categories
Express JavaScript Nodejs

Storing User Sessions on the Server with Express-Session

To store confidential session data, we can use the express-session package. It stores the session data on the server and gives the client a session ID to access the session data.

In this article, we’ll look at how to use it to store temporary user data.

Installation

express-session is a Node package. We can install it by running:

npm install express-session

Then we can include it in our app by adding:

const session = require('express-session');

Usage

The session function returns a middleware to let us store sessions. It takes an option object with the following properties:

cookie

Settings object for the session ID cookie. The default value is:

{ path: '/', httpOnly: true, secure: false, maxAge: null }.

It takes the following properties:

cookie.domain

It specifies the value for the Domain Set-Cookie attribute. No domain is set by default. Most clients will consider the cookie to apply only to the current domain.

cookie.expires

This specifies the Date object of the value for the Expires Set-Cookie attribute. No expiration date is set by default. This means the cookie will be deleted when the session ends.

If both expires and maxAge are set, then the last one defined if what’s used. expires shouldn’t be set directly. We should set maxAge .

cookie.httpOnly

Specifies the boolean value for the HttpOnly Set-Cookie attribute. When it’s truthy, the HttpOnly attribute is set. Otherwise, it’s not. By default, only HttpOnly attribute is set.

cookie.maxAge

The number of milliseconds to use when calculating the Expires Set-Cookie attribute. This is calculated by taking the current server time and adding maxAge milliseconds to it to calculate the Expires datetime. No maximum age is set by default.

cookie.path

Sets the Path Set-Cookie attribute value. This is set to / by default which is the root path of the domain.

cookie.sameSite

A boolean or string for the value of the SameSite Set-Cookie attribute. It can be:

  • true — will set the SameSite attribute to Strict for strict same site enforcement
  • false — won’t set the SameSite attribute
  • 'lax' — set the SameSite attribute to Lax for lax same-site enforcement
  • 'strict' set the SameSite attribute to Strict for strict same-site enforcement

cookie.secure

Boolean value for the Secure Set-Cookie attribute. The Secure attribute is set. Otherwise, it’s not.

Cookies won’t be sent over HTTP connects if this is set to true .

However, it’s recommended. If the app is hosted behind a proxy, then trust proxy must be set if secure is set to true .

genid

A function to generate a new session ID. The default value is a function that uses the uid-safe library to generate IDs.

name

The name of the session ID cookie to set in the response. The default value is 'connect.sid' .

proxy

Trust the reverse proxy when setting cookies via the X-Forwarded-Proto header.

The default value is undefined . Other possibles include:

  • true — the X-Forwarded-Proto header will be used
  • false — all headers are ignored and the connection is considered secure only if it’s a direct SSL/TLS connection
  • undefined — uses the 'trust proxy' setting from Express

resave

Forces the session to be saved back to the session store even if the session was never modified during the request.

This may create race conditions when a client makes 2 parallel requests to our server and changes made to the session on one request may be overwritten when the other request ends.

Defaults to true , but the default may change in the future. false makes more sense as the option.

rolling

Force a session identifier cookie to be set on every response. The expiration is reset to the original maxAge , resetting the expiration countdown.

Defaults to false .

When this is set to true and saveUnintialized option is false , the cookie won’t be set on a response with uninitialized session

saveUnitialized

Force a session that’s uninitialized to be saved to the store. A session is uninitialized when it’s new but not modified. Setting this to false reduces server storage usage and comply with laws that require permission before storing cookies.

secret

This is a required option for the secret to sign the session ID cookie. It can be a string or an array of multiple string secrets.

store

The session store instance. It defaults to a new MemoryStore instance.

unset

Controls the result of unsetting req.session through delete , setting to null , etc.

Defaults to 'keep' . The possible values are:

  • 'destroy' — the session will be deleted when the response ends
  • 'keep' — the session will be kept but modifications made during the request aren’t saved.

req.session

We access the session data by using req.session . The data is typically JSON, so nested objects are fine.

req.session comes with the following methods:

regenerate(callback)

We call this to regenerate the session. Once it’s called, a new session ID and Session instance will be initialized at req.session and the callback will be called.

destroy(callback)

Destroys a session. req.session will be unset. Once it’s called, the callback will be called.

reload(callback)

Reloads the session data from the store and re-populates the req.session object. Once it’s done, the callback will be called.

save(callback)

Saves a session back to the store, replacing the contents of the store with the contents in memory.

This usually doesn’t need to be called.

touch()

Updates the maxAge property. This shouldn’t be called usually as this is done automatically.

id

We can get the session ID with the id property.

cookie

This property has the cookie content. This also lets us alter the cookie for the user.

Cookie.maxAge

We can set the maxAge property to change the expiry time in milliseconds.

Cookie.originalMaxAge

We can use this to get the original maxAge value in milliseconds.

req.sessionID

We can get the session ID of the loaded session with sessionId .

Implementing our Own Session Store

Session store must implement EventEmitter with additional methods.

store.all(callback)

An optional method to get all sessions in the store as an array. The callback signature is callback(error, sessions) and it’s called once the session is retrieved.

store.destroy(sid, callback)

A required method to delete a session from the store given the session ID. The callback has the signaturecallback(error) . It’s called once the session is deleted.

store.clear(callback)

An optional method to delete all sessions from the store. The callback signature is callback(error) and it called once the store is cleared.

store.length(callback)

An optional method to count all the sessions in the store. The callback has the signature callback(error, len).

store.get(sid, callback)

A required method to get the session data by the session ID (sid ). The callback has the signature callback(error, session) .

The session is returned if found, otherwise returns null or undefined .

store.set(sid, session, callback)

A required method to set the session given the sid , which is the session ID. The callback has the signature callback(error) once the session is set.

store.touch(sid, session, callback)

This method is suggested to be implemented. This is used to signal to the store that the given session is active. The callback has the signature callback(error) .

Example

An example would be storing the number of views and keep that data for 365 days. We can do that as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const session = require('express-session');  
const app = express();  
app.set('trust proxy', 1)  
app.use(session({  
  secret: 'secret',  
  resave: true,  
  cookie: {  
    maxAge: 24 * 60 * 60 * 365 * 1000  
  }  
}))
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
app.get('/', (req, res) => {  
  if (req.session.views) {  
    req.session.views++;  
  }  
  else {  
    req.session.views = 1;  
  }  
  res.send(`${req.session.views} views`);  
})
app.listen(3000);

In the code, we have the secret so that the cookie will be signed. Then in our route handler, we can get and set the data by getting and setting req.session.views , which we created.

Then we should see the number of views as we keep reloading the / route. It sets to expire in 365 days by setting maxAge, so the number will keep going up.

In the response header, we should get that the Set-Cookie response header has a value like:

connect.sid=s%3Ad6jfPu5awWj3EXPF-MdtU8cPzjOY5NRT.33nyXjKdShPAPyw9WWwY4sywP44IOX5SPSt2WUkH0cs; Path=/; Expires=Mon, 28 Dec 2020 00:47:31 GMT; HttpOnly

We have the value of the session ID and expiry time.

Conclusion

We can use the express-session package to keep session cookie data on the server-side.

There’re many options like the content of various cookie attributes and the time to expiry.

Other settings like the ID, whether to save cookie only in HTTPS and so on can be set.

The cookies will be stored in a session store. We can also implement our own store if we aren’t satisfied with the existing stores that are available for us to use.

Categories
JavaScript TypeScript

TypeScript Advanced Types — Nullable Types and Type Aliases

TypeScript has many advanced type capabilities and which make writing dynamically typed code easy. It also facilitates the adoption of existing JavaScript code since it lets us keep the dynamic capabilities of JavaScript while using the type-checking capability of TypeScript. There are multiple kinds of advanced types in TypeScript, like intersection types, union types, type guards, nullable types, and type aliases, and more. In this article, we look at nullable types and type aliases.

Nullable Types

To let us assign undefined to a property with the --strictNullChecks flag on, TypeScript supports nullable types. With the flag on, we can’t assign undefined to type members that don’t have the nullable operator attached to it. To use it, we just put a question mark after the member name for the type we want to use.

If we have the strictNullChecks flag on and we set a value of a property to null or undefined , then like we do in the following code:

interface Person {  
  name: string;  
  age: number;  
}

let p: Person = {  
  name: 'Jane',  
  age: null  
}

Then we get the following errors:

Type 'null' is not assignable to type 'number'.(2322)input.ts(3, 3): The expected type comes from property 'age' which is declared here on type 'Person'

The errors above won’t appear if we have strictNullChecks off and the TypeScript compiler will allow the code to be compiled.

If we have the strictNullChecks flag on and we want to be able to set undefined to a property as the value, then we can make the property nullable. For example, we can set a member of an interface to be nullable with the following code:

interface Person {  
  name: string;  
  age?: number;  
}

let p: Person = {  
  name: 'Jane',  
  age: undefined  
}

In the code above, we added a question mark after the age member in the Person interface to make it nullable. Then when we define the object, we can set age to undefined. We can’t still set age to null . If we try to do that, we get:

Type 'null' is not assignable to type 'number | undefined'.(2322)input.ts(3, 3): The expected type comes from property 'age' which is declared here on type 'Person'

As we can see, a nullable type is just a union type between the type that we declared and the undefined type. This also means that we can use type guards with it like any other union type. For example, if we want to only get the age if it’s defined, we can write the following code:

const getAge = (age?: number) => {  
  if (age === undefined) {  
    return 0  
  }  
  else {  
    return age.toString();  
  }  
}

In the getAge function, we first check if the age parameter is undefined . If it is, then we return 0. Otherwise, we can call the toString() method on it, which is available to number objects.

Likewise, we can eliminate null values with a similar kind of code, for instance, we can write:

const getAge = (age?: number | null) => {  
  if (age === null) {  
    return 0  
  }    
  else if (age === undefined) {  
    return 0  
  }  
  else {  
    return age.toString();  
  }  
}

This comes in handy because nullable types exclude null from being assigned with strictNullChecks on, so if we want null to be able to be passed in as a value for the age parameter, then we need to add null to the union type. We can also combine the first 2 if blocks into one:

const getAge = (age?: number | null) => {  
  if (age === null || age === undefined) {  
    return 0  
  }  
  else {  
    return age.toString();  
  }  
}

Type Aliases

If we want to create a new name for an existing type, we can add a type alias to the type. This can be used for many types, including primitives, unions, tuples, and any other type that we can write by hand. To create a type alias, we can use the type keyword to do so. For example, if we want to add an alias to a union type, we can write the following code:

interface Person {  
  name: string;  
  age: number;  
}

interface Employee {  
  employeeCode: number;  
}

type Laborer = Person & Employee;  
let laborer: Laborer = {  
  name: 'Joe',  
  age: 20,  
  employeeCode: 100  
}

The declaration of laborer is the same as using the intersection type directly to type the laborer object, as we do below:

let laborer: Person & Employee = {  
  name: 'Joe',  
  age: 20,  
  employeeCode: 100  
}

We can declare type alias for primitive types like we any other kinds of types. For example, we can make a union type with different primitive types as we do in the following code:

type PossiblyNumber = number | string | null | undefined;  
let x: PossiblyNumber = 2;  
let y: PossiblyNumber = '2';  
let a: PossiblyNumber = null;  
let b: PossiblyNumber = undefined;

In the code above, the PossiblyNumber type can be a number, string, null or undefined . If we try to assign an invalid to it like a boolean as in the following code:

let c: PossiblyNumber = false;

We get the following error:

Type 'false' is not assignable to type 'PossiblyNumber'.(2322)

just like any other invalid value assignment.

We can also include generic type markers in our type aliases. For example, we can write:

type Foo<T> = { value: T };

Generic type aliases can also be referenced in the properties of a type. For example, we can write:

type Tree<T> = {  
  value: T;  
  left: Tree<T>;  
  center: Tree<T>;  
  right: Tree<T>;  
}

Then we can use the Tree type as we do in the following code:

type Tree<T> = {    
  value: T,  
  left: Tree<T>;  
  center: Tree<T>;  
  right: Tree<T>;  
}

let tree: Tree<string> = {} as Tree<string>;  
tree.value = 'Jane';tree.left = {} as Tree<string>  
tree.left.value = 'Joe';  
tree.left.left = {} as Tree<string>;  
tree.left.left.value = 'Amy';  
tree.left.right = {} as Tree<string>  
tree.left.right.value = 'James';tree.center = {} as Tree<string>  
tree.center.value = 'Joe';tree.right = {} as Tree<string>  
tree.right.value = 'Joe';console.log(tree);

The console.log for tree on the last line should get us:

{  
  "value": "Jane",  
  "left": {  
    "value": "Joe",  
    "left": {  
      "value": "Amy"  
    },  
    "right": {  
      "value": "James"  
    }  
  },  
  "center": {  
    "value": "Joe"  
  },  
  "right": {  
    "value": "Joe"  
  }  
}

Nullable types are useful is we want to be able to assign undefined to a property when strictNullChecks flag is on when in our TypeScript compiler configuration. It’s simply a union type between whatever type you have and undefined . It’s denoted by a question mark after the property name. This means we can use type guards with it like any other union type. Note that nullable types don’t allow null values to be assigned to it since nullable types are only needed when strictNullChecks flag is on. Type alias let us create a new name for types that we already have. We can also use generics with type alias, but we can’t use them as standalone types.

Categories
JavaScript Rxjs

Some Useful Rxjs Creation Operators

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 creation operators from Rxjs.

Ajax

We can use the ajax() operator to fetch response objects returned from APIs.

For example, we can use it as follows:

const observable = ajax(`https://api.github.com/meta`).pipe()  
  map(response => {  
    console.log(response);  
    return response;  
  }),  
  catchError(error => {  
    console.log(error);  
    return of(error);  
  })  
);

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

We pipe the data from the response with the map operator. Also, we can catch HTTP errors with the catchError operator.

Also, we can use ajax.getJSON() to simplify the operation as follows:

import { ajax } from "rxjs/ajax";  
import { map, catchError } from "rxjs/operators";  
import { of } from "rxjs";

const observable = ajax.getJSON(`https://api.github.com/meta`).pipe()  
  map(response => {  
    console.log(response);  
    return response;  
  }),  
  catchError(error => {  
    console.log("error: ", error);  
    return of(error);  
  })  
);

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

Note that in both examples, we return the response in the callback of the map that we passed into the map operator.

It also works for POST requests:

import { ajax } from "rxjs/ajax";  
import { map, catchError } from "rxjs/operators";  
import { of } from "rxjs";

const observable = ajax({  
  url: "https://jsonplaceholder.typicode.com/posts",  
  method: "POST",  
  headers: {  
    "Content-Type": "application/json"  
  },  
  body: {  
    id: 1,  
    title: "title",  
    body: "body",  
    userId: 1  
  }  
}).pipe(  
  map(response => console.log("response: ", response)),  
  catchError(error => {  
    console.log("error: ", error);  
    return of(error);  
  })  
);

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

As we can see, we can set headers and body of the request, so ajax can deal with most HTTP requests.

Errors can also be caught with the catchError operator that we pipe in:

import { ajax } from "rxjs/ajax";  
import { map, catchError } from "rxjs/operators";  
import { of } from "rxjs";

const observable = ajax(`https://api.github.com/404`).pipe()  
  map(response => {  
    console.log(response);  
    return response;  
  }),  
  catchError(error => {  
    console.log(error);  
    return of(error);  
  })  
);

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

bindCallback

bindCallback converts a callback API to a function that returns an Observable.

It can convert a function with parameters to an Observable by emitting the parameters.

It takes 3 arguments. The first is a function, which takes a callback function as a parameter. Whatever is passed into the callback function will be emitted by the Observable.

The second argument is an optional resultSelector . We can pass in a function to select the emitted results here.

The last argument is an optional scheduler. We can pass in a scheduler if we want to change the way the callback function in the first argument is scheduled to be called.

import { bindCallback } from "rxjs";

const foo = fn => {  
  fn("a", "b", "c");  
};

const observableFn = bindCallback(foo);  
observableFn().subscribe(res => console.log(res));

Then we’ll see 'a' , 'b' and 'c' since we passed them into our fn callback function, which is a parameter of foo .

Then we return a function that returns an Observable with the bindCallback function. Then we can subscribe to the returned Observable.

defer

defer lets us create an Observable that are only created when a subscription is made.

It takes one argument, which is an Observable factory function. For example, we can write:

import { defer, of } from "rxjs";
const clicksOrInterval = defer(() => {  
  return Math.random() > 0.5 ? of([1, 2, 3]) : of([4, 5, 6]);  
});  

clicksOrInterval.subscribe(x => console.log(x));

Then we can have an Observable that either subscribes to of([1, 2, 3]) or of([4, 5, 6]) depending on whether Math.random() return 0.5 or less or bigger than 0.5.

empty

Creates an Observable that emits nothing to Observers except for complete notification.

It takes one optional argument, which is the scheduler that we want to use.

For example, we can use it as follows:

import { empty } from "rxjs";
const result = empty();  
result.subscribe(x => console.log(x));

Then we should see nothing logged.

Another example would be to emit the value 'odd' when odd numbers are emitted from the original Observable:

import { empty, interval, of } from "rxjs";  
import { mergeMap } from "rxjs/operators";const interval$ = interval(1000);  
const result = interval$.pipe(  
  mergeMap(x => (x % 2 === 1 ? of("odd") : empty()))  
);  
result.subscribe(x => console.log(x));

from

from creates an Observable from an array, array-like object, a promise, iterable object or Observable-like object.

It takes 2 arguments, which is an array, array-like object, a promise, iterable object or Observable-like object.

The other argument is an optional argument, which is a scheduler.

For example, we can use it as follows:

import { from } from "rxjs";
const array = [1, 2, 3];  
const result = from(array);result.subscribe(x => console.log(x));

We can also use it to convert a promise to an Observable as follows:

import { from } from "rxjs";

const promise = Promise.resolve(1);  
const result = from(promise);
result.subscribe(x => console.log(x));

This is handy for situations where we want to do that, like converting fetch API promises to Observables.

As we can see, the creation operators are pretty useful for turning various data sources to Observables.

We have the ajax operator for getting HTTP request responses. The bindCallback function turns callback arguments into Observable data. defer let us create Observables on the fly when something subscribes to the Observable returned by the defer operator.

Finally, we have the empty operator to create an Observable that emits nothing, and a from operator to create Observables from an array, array-like object, a promise, iterable object or Observable-like object.

Categories
JavaScript TypeScript

Introduction to TypeScript Classes- 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 how to define TypeScript classes and how they inherit from each other. In this article, we’ll look at the access modifiers for class members in TypeScript.

Public, private, and protected modifiers

Public

In TypeScript, class member can have access modifiers added to them. This lets us control the access of the members of class by different parts of the program outside of the class that the members are defined in. The default access modifier for class members in TypeScript is public . This means that class member that have no access modifiers will be designated as public members. For example, we can use the public modifier like in the following code:

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

  public getName(): string{  
    return this.name;  
  }  
}

const person = new Person('Jane');  
console.log(person.getName());  
console.log(person.name);

In the example above, we designated all the members in our Person class as public so that we can access them outside the Person class. We can call the getName method on the Person instance and also we can get the name field directly from outside the class. The public access modifier on the constructor method is extra because constructor should always be public so we can instantiate the class with it.

Private and Protected

When a member of a class is marked as private , then it can’t be accessed outside of its containing class. Protected members are only available from within sub-classes of the class that has the protected member and the class that has the member and is marked with the keyword protected. For example, if we have a private member in our class in like the following code:

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

  public getName(): string{  
    return this.name;  
  }  
}

const person = new Person('Jane');  
console.log(person.getName());  
console.log(person.name);

Then we get an error when we try to access it like we did with the member name in the Person class that we have above. If we try to compile and run the code above, the TypeScript compiler will not compile the code and gives the error message “Property ‘name’ is private and only accessible within class ‘Person’.(2341)“ like we expect for private members.

TypeScript compares type by their structure for public members. If the 2 types have the same public members listed, then they’re marked as being compatible by TypeScript. However, for private and protected members, this isn’t the case. For private and protected members, for 2 classes to be considered equal, then both classes must have the same private and protected members from the same origin for them to be considered to be the same type. For example, if we have the following code:

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

class Human {  
  private name: string;  
  constructor(name: string) {  
    this.name = name;  
  }  
}

const human: Human = new Person('Jane');

Then we would get the error “ Type ‘Person’ is not assignable to type ‘Human’. Types have separate declarations of a private property ‘name’.(2322)“. This means that because both the Person and Human have the same private member called name , that they can’t be considered the same type, so we can’t assign an instance of Person to a variable that’s of type Human . This is the same for protected members, so if we have the following code:

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

class Human {  
  protected name: string;  
  constructor(name: string) {  
    this.name = name;  
  }  
}

const human: Human = new Person('Jane');

We would get the same error. But if we change protected to public like we do in the code below, then it would work:

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

class Human {  
  public name: string;  
  constructor(name: string) {  
    this.name = name;  
  }  
}

const human: Human = new Person('Jane');  
console.log(human.name);

If we run the code above, we’ll see ‘Jane’ logged from the console.log statement on the last line.

If we have private members in our classes, then they must be in the super-class for both classes for both classes to be considered equal. For example, we can write the following code to make the Person and Human class to be considered the same while having a common private member name for each class:

class Animal {  
  private name: string;  
  constructor(name: string) {  
    this.name = name;        
  }  
}

class Person extends Animal{  
  constructor(name: string) {  
    super(name);      
  }  
}

class Human extends Animal{  
  constructor(name: string) {  
    super(name);      
  }  
}

const human: Human = new Person('Jane');  
console.log(human);

In the code above, we have the Animal class that the private name member, and both the Person and Human classes extends the Animal class, so that the Human and Person will be considered equal since they don’t have separate implementations of the private member name , but rather, a common name member in the Animal class instead which they both inherit from. When we run console.log on human in the last line, we would see the Person object being logged.

Likewise, for protected members, we can do the something similar like in the following code:

class Animal {  
  protected name: string;  
  constructor(name: string) {  
    this.name = name;        
  }  
}

class Person extends Animal{  
  constructor(name: string) {  
    super(name);      
  } 

  getName() {  
    return this.name;  
  }  
}

class Human extends Animal{  
  constructor(name: string) {  
    super(name);      
  } 

  getName() {  
    return this.name;  
  }  
}

const human: Human = new Person('Jane');  
console.log(human.getName());

In the code above, we have the protected member name which can be accessed by its sub-classes Human and Person , so we can return the value of the name member with a getName method on each class and the value of the name member in the Animal class. We need this method because protected members are only available from within sub-classes of the class that has the protected member and the class that has the member. If we run the code above, we would get ‘Jane’ from the console.log output from the last line of the code above.

In TypeScript, class members can have access modifiers applied to them. Public is the default access modifier for members if nothing is specified. 2 class are considered equal if they both have the same public members, or that they inherit protected and private members from the same source and have the same public members.

Categories
JavaScript Rxjs

More Rxjs Operators

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 join creation operators to combine data from multiple Observables into one Observable. We’ll look at the merge , race and zip join creation operators, and also the buffer and bufferCount transformation operators.

Join Creation Operators

These operators combine the values emitted from multiple Observers into one.

merge

The merge operator takes multiple Observables and concurrently emits all values from every given input Observable.

It takes one array of Observables or a comma-separated list of Observables as arguments.

For example, we can use it as follows:

import { merge, of } from "rxjs";
const observable1 = of(1, 2, 3);  
const observable2 = of(4, 5, 6);  
const combined = merge(observable1, observable2);  
combined.subscribe(x => console.log(x));

Another example would be combining multiple timed Observables as follows:

import { merge, interval } from "rxjs";
const observable1 = interval(1000);  
const observable2 = interval(2000);  
const combined = merge(observable1, observable2);  
combined.subscribe(x => console.log(x));

We’ll see that the first observable1 will emit a value first, then observable2 . Then observable1 will continue to emit values every second, and observable2 will emit values every 2 seconds.

race

The race operator takes multiple Observables and returns the Observable that emits an item from the arguments.

It takes a comma-separated list of Observables as arguments.

For example, we can use it as follows:

import { race, of } from "rxjs";
const observable1 = of(1, 2, 3);  
const observable2 = of(4, 5, 6);  
const combined = race(observable1, observable2);  
combined.subscribe(x => console.log(x));

We have observable1 , which emits data before observable2 . We should get the output:

1  
2  
3

since observable emits values first.

zip

The zip operator combines multiple Observables and returns an Observable whose values are calculated from the values, in order of each of its input Observables.

It takes a list of Observables as arguments. We can use it as follows:

import { zip, of } from "rxjs";
const observable1 = of(1, 2, 3);  
const observable2 = of(4, 5, 6);  
const combined = zip(observable1, observable2);  
combined.subscribe(x => console.log(x));

Then we get the following:

[1, 4]  
[2, 5]  
[3, 6]

We can also map them to objects as follows to make values from one Observable easier to distinguish from the other.

To do this, we can write the following:

import { zip, of } from "rxjs";  
import { map } from "rxjs/operators";
const age$ = of(1, 2, 3);  
const name$ = of("John", "Mary", "Jane");  
const combined = zip(age$, name$);  
combined  
  .pipe(map(([age, name]) => ({ age, name })))  
  .subscribe(x => console.log(x));

Transformation Operators

buffer

The buffer operator buffers the source Observable values until the closingNotifier emits.

It takes one argument, which is the closingNotifier . It’s an Observable that signals the buffer to be emitted on the output Observable.

For example, we can use it as follows:

import { fromEvent, timer } from "rxjs";  
import { buffer } from "rxjs/operators";
const observable = timer(1000, 1000);  
const clicks = fromEvent(document, "click");  
const buffered = observable.pipe(buffer(clicks));  
buffered.subscribe(x => console.log(x));

In the code above, we have an Observable created by the timer operator which emits numbers every second after 1 second of waiting. Then we pipe our results into the clicks Observable, which emits as clicks are made to the document.

This means that as we click the page, the emitted data that are buffered by the buffer operator will emit the data that was buffered. Also, this means that as we click our document, we’ll get anything from an empty array to an array of values that were emitted between clicks.

bufferCount

bufferCount is slightly different from buffer in that it buffers the data until the size hits the maximum bufferSize .

It takes 2 arguments, which are the bufferSize , which is the maximum size buffered, and the startBufferEvery parameter which is an optional parameter indicating the interval at which to start a new buffer.

For example, we can use it as follows:

import { fromEvent } from "rxjs";  
import { bufferCount } from "rxjs/operators";  
const clicks = fromEvent(document, "click");  
const buffered = clicks.pipe(bufferCount(10));  
buffered.subscribe(x => console.log(x));

The code above will emit the MouseEvent objects that are buffered into the array once we clicked 10 times since this is when we 10 MouseEvent objects are emitted by the originating Observable.

As we can see, the join creation operators lets us combine Observables’ emitted data in many ways. We can pick the first ones emitted, we can combine all the emitted data into one, and we can get them concurrently.

Also, we can buffer Observable’s emitted data and emit them when a given amount is buffered or a triggering event will emit the data in the buffer.