<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Harshad Ranganathan</title>
    <description>The latest articles on Forem by Harshad Ranganathan (@harshadranganathan).</description>
    <link>https://forem.com/harshadranganathan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F209479%2F47296453-670b-474b-aca1-0146634fd845.png</url>
      <title>Forem: Harshad Ranganathan</title>
      <link>https://forem.com/harshadranganathan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/harshadranganathan"/>
    <language>en</language>
    <item>
      <title>Spark SQL - DataFrames &amp; Datasets</title>
      <dc:creator>Harshad Ranganathan</dc:creator>
      <pubDate>Tue, 05 Nov 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/harshadranganathan/spark-sql-dataframes-amp-datasets-356i</link>
      <guid>https://forem.com/harshadranganathan/spark-sql-dataframes-amp-datasets-356i</guid>
      <description>&lt;h2&gt;
  
  
  Spark SQL
&lt;/h2&gt;

&lt;p&gt;Spark SQL lets you query structured data inside Spark programs, using either SQL or a familiar DataFrame API.&lt;/p&gt;

&lt;p&gt;DataFrames and SQL provide a common way to access a variety of data sources, including Hive, Avro, Parquet, ORC, JSON, and JDBC. You can even join data across these sources.&lt;/p&gt;

&lt;p&gt;Spark SQL supports the HiveQL syntax as well as Hive SerDes and UDFs, allowing you to access existing Hive warehouses.&lt;/p&gt;

&lt;p&gt;Spark SQL includes a cost-based optimizer, columnar storage and code generation to make queries fast.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/11/spark-sql-overview.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AKzfSJ-s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/11/spark-sql-overview.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  DataFrames
&lt;/h2&gt;

&lt;p&gt;A DataFrame is a Dataset organized into named columns. It is conceptually equivalent to a table in a relational database or a data frame in R/Python but with richer optimizations under the hood.&lt;/p&gt;

&lt;p&gt;DataFrame is represented by a Dataset of Rows. In the Scala API, DataFrame is simply a type alias of Dataset[Row].&lt;/p&gt;

&lt;p&gt;DataFrames are untyped and the elements within DataFrames are Rows.&lt;/p&gt;

&lt;p&gt;Below is a sample dataframe which is composed of rows &amp;amp; columns -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scala&amp;gt; df.show
+---------------+--------+-----------+--------------------+--------+
| capital|currency|independent| name| region|
+---------------+--------+-----------+--------------------+--------+
| Canberra| AUD| true| Australia| Oceania|
| London| GBP| true| United Kingdom| Europe|
|Washington D.C.| USD| true|United States of ...|Americas|
| Paris| EUR| true| France| Europe|
| Tokyo| JPY| true| Japan| Asia|
| Dublin| EUR| true| Ireland| Europe|
+---------------+--------+-----------+--------------------+--------+

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Datasets
&lt;/h2&gt;

&lt;p&gt;A Dataset is a distributed collection of data. Dataset is a new interface added in Spark 1.6 that provides the benefits of RDDs (strong typing, ability to use powerful lambda functions) with the benefits of Spark SQL’s optimized execution engine.&lt;/p&gt;

&lt;p&gt;Datasets are typed distributed collections of data and it unifies DataFrame and RDD APIs.&lt;/p&gt;

&lt;p&gt;Datasets require structured/semi-structured data. Schemas and Encoders are part of Datasets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparisons
&lt;/h2&gt;

&lt;p&gt;RDD vs DataFrames vs Datasets&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;RDD&lt;/th&gt;
&lt;th&gt;DataFrames&lt;/th&gt;
&lt;th&gt;Datasets&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Functional transformations&lt;/td&gt;
&lt;td&gt;Relational&lt;/td&gt;
&lt;td&gt;Functional &amp;amp; Relational transformations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No type safety&lt;/td&gt;
&lt;td&gt;No type safety&lt;/td&gt;
&lt;td&gt;Type-safe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No built-in optimization&lt;/td&gt;
&lt;td&gt;Catalyst query optimization&lt;/td&gt;
&lt;td&gt;Catalyst query optimization**&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;In-memory JVM objects resulting in GC &amp;amp; java serialization overheads&lt;/td&gt;
&lt;td&gt;Tungsten execution engine - Off-Heap Memory Management using binary in-memory data representation&lt;/td&gt;
&lt;td&gt;Tungsten execution engine - Off-Heap Memory Management using binary in-memory data representation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;JIT code generation&lt;/td&gt;
&lt;td&gt;JIT code generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Sorting/suffling without deserialization&lt;/td&gt;
&lt;td&gt;Sorting/suffling without deserialization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;No encoders&lt;/td&gt;
&lt;td&gt;Encoders - generate byte code and provide on-demand access to attributes without the need for deserialization&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;DataFrames vs Datasets&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;DataFrames&lt;/th&gt;
&lt;th&gt;Datasets&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DataFrames are untyped - No type check by the compiler&lt;/td&gt;
&lt;td&gt;Datasets are typed distributed collections of data - compile-time type safety&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transformations on dataFrames are untyped&lt;/td&gt;
&lt;td&gt;Datasets include both untyped and typed transformations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Catalyst Optimizer
&lt;/h2&gt;

&lt;p&gt;At the core of Spark SQL is the Catalyst optimizer, which leverages advanced programming language features (e.g. Scala’s pattern matching and quasi quotes) in a novel way to build an extensible query optimizer.&lt;/p&gt;

&lt;p&gt;Catalyst supports both rule-based and cost-based optimization.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/11/catalyst-optmization-phases.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7Nw-nNCZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/11/catalyst-optmization-phases.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image from &lt;a href="https://databricks.com/wp-content/uploads/2018/05/Catalyst-Optimizer-diagram.png"&gt;https://databricks.com/wp-content/uploads/2018/05/Catalyst-Optimizer-diagram.png&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;On our sample dataset of countries and currencies, let’s find the most used currency. We will perform a group by operation on the currency column and then order it in descending order based on the counts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scala&amp;gt; df.groupBy("currency").count().orderBy(desc("count")).show()
+--------+-----+
|currency|count|
+--------+-----+
| EUR| 2|
| JPY| 1|
| AUD| 1|
| GBP| 1|
| USD| 1|
+--------+-----+

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let’s see the catalyst optimization for the above query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scala&amp;gt; df.groupBy("currency").count().orderBy(desc("count")).explain(extended=true)
== Parsed Logical Plan ==
'Sort ['count DESC NULLS LAST], true
+- Aggregate [currency#149], [currency#149, count(1) AS count#202L]
   +- Relation[capital#148,currency#149,independent#150,name#151,region#152] json

== Analyzed Logical Plan ==
currency: string, count: bigint
Sort [count#202L DESC NULLS LAST], true
+- Aggregate [currency#149], [currency#149, count(1) AS count#202L]
   +- Relation[capital#148,currency#149,independent#150,name#151,region#152] json

== Optimized Logical Plan ==
Sort [count#202L DESC NULLS LAST], true
+- Aggregate [currency#149], [currency#149, count(1) AS count#202L]
   +- Project [currency#149]
      +- Relation[capital#148,currency#149,independent#150,name#151,region#152] json

== Physical Plan ==
*(3) Sort [count#202L DESC NULLS LAST], true, 0
+- Exchange rangepartitioning(count#202L DESC NULLS LAST, 200)
   +- *(2) HashAggregate(keys=[currency#149], functions=[count(1)], output=[currency#149, count#202L])
      +- Exchange hashpartitioning(currency#149, 200)
         +- *(1) HashAggregate(keys=[currency#149], functions=[partial_count(1)], output=[currency#149, count#207L])
            +- *(1) FileScan json [currency#149] Batched: false, Format: JSON, Location: InMemoryFileIndex[file:/C:/Users/ranganathanh/Downloads/countries.json], PartitionFilters: [], PushedFilters: [], ReadSchema: struct&amp;lt;currency:string&amp;gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here,&lt;/p&gt;

&lt;p&gt;Analyzed Logical Plan - Resolved relations, references, aliases and updated attribute nullability.&lt;/p&gt;

&lt;p&gt;Optimized Logical Plan - Introduced a projection of column “currency” as we are interested in only that field for the rest of our operations.&lt;/p&gt;

&lt;p&gt;Physical Plan - In the final physical plan, notice the &lt;code&gt;ReadSchema&lt;/code&gt; operation as part of &lt;code&gt;FileScan&lt;/code&gt;. It only reads in the “currency” field and ignores the rest.&lt;/p&gt;

&lt;p&gt;Code references:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/apache/spark/blob/master/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala"&gt;https://github.com/apache/spark/blob/master/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/apache/spark/blob/master/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/Optimizer.scala"&gt;https://github.com/apache/spark/blob/master/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/Optimizer.scala&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/apache/spark/blob/master/sql/core/src/main/scala/org/apache/spark/sql/execution/SparkStrategies.scala"&gt;https://github.com/apache/spark/blob/master/sql/core/src/main/scala/org/apache/spark/sql/execution/SparkStrategies.scala&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tungsten Execution Engine
&lt;/h2&gt;

&lt;p&gt;Tungsten improves the efficiency of memory and CPU usage.&lt;/p&gt;

&lt;p&gt;Spark explicitly manages memory and converts most operations to operate directly against binary data.&lt;/p&gt;

&lt;p&gt;Columnar layout for memory data avoids unnecessary I/O and accelerates analytical processing performance on modern CPUs and GPUs.&lt;/p&gt;

&lt;p&gt;Vectorization allows the CPU to operate on vectors, which are arrays of column values from multiple records. This takes advantage of modern CPU designs, by keeping all pipelines full to achieve efficiency.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/11/columnar-memory-layout.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hMxGICqB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/11/columnar-memory-layout.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image from &lt;a href="https://arrow.apache.org/img/simd.png"&gt;https://arrow.apache.org/img/simd.png&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Tungsten also performs -&lt;/p&gt;

&lt;p&gt;[1] Whole-Stage Code-Generation - Instead of having each operator as an individual function, combine and compile all of those stages into single function.&lt;/p&gt;

&lt;p&gt;[2] Cache Locality - Tungsten uses algorithms and cache-aware data structures that exploit the physical machine caches at different levels - L1, L2, L3.&lt;/p&gt;

&lt;p&gt;[3] Loop unrolling and SIMD - Optimize Apache Spark’s execution engine to take advantage of modern compilers and CPUs’ ability to efficiently compile and execute simple for loops.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structured Streaming
&lt;/h2&gt;

&lt;p&gt;Structured Streaming is a scalable and fault-tolerant stream processing engine built on the Spark SQL engine.&lt;/p&gt;

&lt;p&gt;The key idea in Structured Streaming is to treat a live data stream as a table that is being continuously appended. This leads to a new stream processing model that is very similar to a batch processing model.&lt;/p&gt;

&lt;p&gt;For a word count example, when the query is started, Spark will continuously check for new data from the socket connection. If there is new data, Spark will run an “incremental” query that combines the previous running counts with the new data to compute updated counts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/11/structured-streaming-example-model.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fN8gj2e4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/11/structured-streaming-example-model.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image from &lt;a href="https://spark.apache.org/docs/latest/img/structured-streaming-example-model.png"&gt;https://spark.apache.org/docs/latest/img/structured-streaming-example-model.png&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can use the common entry point &lt;code&gt;SparkSession&lt;/code&gt; to create streaming DataFrames/Datasets from streaming sources, and apply the same operations on them as static DataFrames/Datasets.&lt;/p&gt;

&lt;p&gt;There are a few built-in sources, e.g. for files, supported file formats are text, csv, json, orc, parquet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Encoders
&lt;/h2&gt;

&lt;p&gt;Encoder is the fundamental concept in the serialization and deserialization (SerDe) framework.&lt;/p&gt;

&lt;p&gt;Encoders are used to convert a JVM object of type T to and from the internal Spark SQL representation.&lt;/p&gt;

&lt;p&gt;Encoders know the schema of the records. This is how they offer significantly faster serialization and deserialization (comparing to the default Java or Kryo serializers).&lt;/p&gt;

&lt;p&gt;So every data set has encoders to go along with it. And these encoder things are extremely specialized, optimized code generators that generate custom bytecode for serialization and deserialization of your data.&lt;/p&gt;

&lt;p&gt;And this special representation is Sparks internal Tungsten’s binary format which allows these operations to happen on already serialized data greatly improving the memory utilization.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to use ?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RDD&lt;/td&gt;
&lt;td&gt;You have unstructured data&lt;br&gt;If your unstructured data cannot to be reformulated to adhere to a schema&lt;br&gt;You need to manage low-level details of RDD computations&lt;br&gt;You have complex data types which can’t be serialized with Encoders&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DataFrames&lt;/td&gt;
&lt;td&gt;You have structured/semi-structured data&lt;br&gt;You need best performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Datasets&lt;/td&gt;
&lt;td&gt;You have structured/semi-structured data&lt;br&gt;You need type-safety&lt;br&gt;You need functional APIs&lt;br&gt;  You need good performance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;RDD, DataFrames &amp;amp; Datasets have their pros &amp;amp; cons. Let’s see with an example where we will utilize all three of them (Interoperability) to achieve the best performance.&lt;/p&gt;

&lt;p&gt;Our datasource is a json file where each line contains json data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"name":"Australia","independent":true,"currency":"AUD","capital":"Canberra","region":"Oceania"}
{"name":"United Kingdom","independent":true,"currency":"GBP","capital":"London","region":"Europe"}
{"name":"United States of America","independent":true,"currency":"USD","capital":"Washington D.C.","region":"Americas"}
{"name":"France","independent":true,"currency":"EUR","capital":"Paris","region":"Europe"}
{"name":"Japan","independent":true,"currency":"JPY","capital":"Tokyo","region":"Asia"}
{"name":"Ireland","independent":true,"currency":"EUR","capital":"Dublin","region":"Europe"}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We will make use of &lt;code&gt;Scala&lt;/code&gt; language for demonstrating Spark SQL here.&lt;/p&gt;

&lt;p&gt;Let’s read the json file using &lt;code&gt;DataFrameReader&lt;/code&gt; with the session available in our spark shell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Spark session available as 'spark'.
Using Scala version 2.11.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_202)

scala&amp;gt; val df = spark.read.json("C:\\Users\\ranganathanh\\Downloads\\countries.json")
df: org.apache.spark.sql.DataFrame = [capital: string, currency: string ... 3 more fields]

scala&amp;gt; df.printSchema()
root
 |-- capital: string (nullable = true)
 |-- currency: string (nullable = true)
 |-- independent: boolean (nullable = true)
 |-- name: string (nullable = true)
 |-- region: string (nullable = true)

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Notice that the schema is automatically inferred here. For better performance, specify the schema instead of using schema inference.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;DataFrameReader&lt;/code&gt; accepts a &lt;code&gt;StructType&lt;/code&gt; object for schema. So, we construct it as below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scala&amp;gt; import org.apache.spark.sql.types.{BooleanType, StringType, StructField, StructType}
import org.apache.spark.sql.types.{BooleanType, StringType, StructField, StructType}

scala&amp;gt; val schema = StructType(
     | Array(
     | StructField("name", StringType),
     | StructField("independent", BooleanType),
     | StructField("currency", StringType),
     | StructField("capital", StringType),
     | StructField("region", StringType)
     | )
     | )
schema: org.apache.spark.sql.types.StructType = StructType(StructField(name,StringType,true), StructField(independent,BooleanType,true), StructField(currency,StringType,true), StructField(capital,StringType,true), StructField(region,StringType,true))

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now specify the schema while reading the json file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scala&amp;gt; val df = spark.read.schema(schema).json("C:\\Users\\ranganathanh\\Downloads\\countries.json")
df: org.apache.spark.sql.DataFrame = [name: string, independent: boolean ... 3 more fields]

scala&amp;gt; df.printSchema()
root
 |-- name: string (nullable = true)
 |-- independent: boolean (nullable = true)
 |-- currency: string (nullable = true)
 |-- capital: string (nullable = true)
 |-- region: string (nullable = true)

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, we want to get the list of countries which have currency as “EUR”. We’ll explore how this can be achieved using both DataFrame &amp;amp; Dataset API’s.&lt;/p&gt;

&lt;p&gt;We already have our dataframe from our previous file reading operation, so, we’ll apply &lt;code&gt;filter&lt;/code&gt; and &lt;code&gt;select&lt;/code&gt; transformations on it to achieve our result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scala&amp;gt; df.filter(df("currency").contains("EUR")).select(df("name"), df("currency"), df("capital")).show()
+-------+--------+-------+
| name|currency|capital|
+-------+--------+-------+
| France| EUR| Paris|
|Ireland| EUR| Dublin|
+-------+--------+-------+

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can select and work with columns in three-ways:&lt;/p&gt;

&lt;p&gt;[1] Using &lt;code&gt;$&lt;/code&gt; notation e.g. &lt;code&gt;df.filter($"currency".contains("EUR"))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;[2] Referring to the dataframe e.g. &lt;code&gt;df.filter(df("currency").contains("EUR"))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;[3] Using SQL query string e.g. &lt;code&gt;df.filter("currency == 'EUR'")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let’s checkout the physical plan of our previous query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scala&amp;gt; df.filter(df("currency").contains("EUR")).select(df("name"), df("currency"), df("capital")).explain()
== Physical Plan ==
*(1) Project [name#245, currency#247, capital#248]
+- *(1) Filter (isnotnull(currency#247) &amp;amp;&amp;amp; Contains(currency#247, EUR))
   +- *(1) FileScan json [name#245,currency#247,capital#248] Batched: false, Format: JSON, Location: InMemoryFileIndex[file:/C:/Users/ranganathanh/Downloads/countries.json], PartitionFilters: [], PushedFilters: [IsNotNull(currency), StringContains(currency,EUR)], ReadSchema: struct&amp;lt;name:string,currency:string,capital:string&amp;gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We infer two things from our DataFrame approach:&lt;/p&gt;

&lt;p&gt;[1] Catalyst query optimization got applied - &lt;code&gt;ReadSchema&lt;/code&gt; was reading only the required fields from the file and not all the columns&lt;/p&gt;

&lt;p&gt;[2] There is no type checking on the column names we used in the filter and select operations. This will possibly cause runtime errors if the mentioned fields are not present.&lt;/p&gt;

&lt;p&gt;Let’s try the same query using Datasets.&lt;/p&gt;

&lt;p&gt;To convert the structured data from the file to a Dataset, you need to define case classes in scala.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scala&amp;gt; case class Country(
     | name: String,
     | independent: Boolean,
     | currency: String,
     | capital: String,
     | region: String
     | )
defined class Country

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We redefine our file read operation to return a Dataset of type [Country] using the &lt;code&gt;as&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scala&amp;gt; val ds = spark.read.schema(schema).json("C:\\Users\\ranganathanh\\Downloads\\countries.json").as[Country]
ds: org.apache.spark.sql.Dataset[Country] = [name: string, independent: boolean ... 3 more fields]

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, we try the same filter and select operations using the Dataset.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scala&amp;gt; ds.filter(country =&amp;gt; country.currency.equals("EUR")).select($"name", $"currency", $"capital").show()
+-------+--------+-------+
| name|currency|capital|
+-------+--------+-------+
| France| EUR| Paris|
|Ireland| EUR| Dublin|
+-------+--------+-------+

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And the corresponding query plan -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cala&amp;gt; ds.filter(country =&amp;gt; country.currency.equals("EUR")).select($"name", $"currency", $"capital").explain()
== Physical Plan ==
*(1) Project [name#297, currency#299, capital#300]
+- *(1) Filter &amp;lt;function1&amp;gt;.apply
   +- *(1) FileScan json [name#297,independent#298,currency#299,capital#300,region#301] Batched: false, Format: JSON, Location: InMemoryFileIndex[file:/C:/Users/ranganathanh/Downloads/countries.json], PartitionFilters: [], PushedFilters: [], ReadSchema: struct&amp;lt;name:string,independent:boolean,currency:string,capital:string,region:string&amp;gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can infer below when using a Dataset -&lt;/p&gt;

&lt;p&gt;[1] Catalyst query optimization isn’t applied - &lt;code&gt;ReadSchema&lt;/code&gt; is reading all the fields from the file. Reason for this is that catalyst cannot infer the operation being performed when you use lamba functions in your filter operations.&lt;/p&gt;

&lt;p&gt;[2] You get compile time type safety checks e.g. &lt;code&gt;ds.filter(country =&amp;gt; country.currency.equals("EUR"))&lt;/code&gt; utilizes the case class.&lt;/p&gt;

&lt;p&gt;[3] In your select query you are ending up having to specify column expressions. This is because, Dataset supports both typed and untyped transformations. While &lt;code&gt;filter&lt;/code&gt; is a typed transformation, &lt;code&gt;select&lt;/code&gt; is an untyped transformation so you lose the type information.&lt;/p&gt;

&lt;p&gt;Now we have seen both approaches, I would prefer to stick with the &lt;code&gt;DataFrame&lt;/code&gt; API’s for reading files and performing selective transformations to achieve read performance.&lt;/p&gt;

&lt;p&gt;I had earlier mentioned about interoperability. Let’s say you have your list of countries which have &lt;code&gt;EUR&lt;/code&gt; as their currency.&lt;/p&gt;

&lt;p&gt;You want to perform map operations to transform the data so that it can be inserted in a DB. In such a case, it makes sense to convert your &lt;code&gt;DataFrame&lt;/code&gt; to a &lt;code&gt;Dataset&lt;/code&gt; so that you can reap the benefits of having a case class (Encoders!!!).&lt;/p&gt;

&lt;p&gt;Encoders help to perform operations directly on the serialized data and they use internal Spark SQL representation.&lt;/p&gt;

&lt;p&gt;All you have to do is to convert your dataframe once you have projected your fields to a dataset using the &lt;code&gt;as&lt;/code&gt; method and import the spark implicits object available in your spark session.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import spark.implicits._&lt;/code&gt; automatically creates encoders for your case classes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df
  .filter(df("currency").contains("EUR"))
  .select(df("name"), df("currency"), df("capital")).as[Country]
  .map(country =&amp;gt; {
     // your map operation
  })

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, what will you return in your map function ?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Primitive types (Int, S tring, etc) and Product types (case classes) are supported by importing spark.implicits. Support for serializing other types will be added in future releases.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Returning case class with ciruclar dependency&lt;/td&gt;
&lt;td&gt;Will fail as there is no supported encoder for it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Returning case class with Option fields&lt;/td&gt;
&lt;td&gt;Will fail as there is no supported encoder for it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Return &lt;code&gt;null&lt;/code&gt; value&lt;/td&gt;
&lt;td&gt;You need to wrap it in a &lt;code&gt;Tuple1&lt;/code&gt; object and return it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can create custom encoders as well if there is no supported encoder for your return type. You can also create encoders from case class, java beans etc. and specify it in your transformations.&lt;/p&gt;

&lt;p&gt;Now, after you have done your transformations you might want to write to a DB and need low level access (or) you don’t have a suitable encoder.&lt;/p&gt;

&lt;p&gt;In that case, you can convert your Dataset to a RDD.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df
  .filter(df("currency").contains("EUR"))
  .select(df("name"), df("currency"), df("capital")).as[Country]
  .map(country =&amp;gt; {
     // your map operation
  })
  .rdd

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can also convert your RDD to Dataset / DataFrame using &lt;code&gt;toDS()&lt;/code&gt; and &lt;code&gt;toDF()&lt;/code&gt; methods.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://spark.apache.org/sql/"&gt;https://spark.apache.org/sql/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://spark.apache.org/docs/2.2.0/sql-programming-guide.html"&gt;https://spark.apache.org/docs/2.2.0/sql-programming-guide.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.coursera.org/learn/scala-spark-big-data/home/week/4"&gt;https://www.coursera.org/learn/scala-spark-big-data/home/week/4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mapr.com/ebook/getting-started-with-apache-spark-v2/assets/Spark2018eBook.pdf"&gt;https://mapr.com/ebook/getting-started-with-apache-spark-v2/assets/Spark2018eBook.pdf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mapr.com/blog/tips-and-best-practices-to-take-advantage-of-spark-2-x/"&gt;https://mapr.com/blog/tips-and-best-practices-to-take-advantage-of-spark-2-x/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://databricks.com/blog/2015/04/13/deep-dive-into-spark-sqls-catalyst-optimizer.html"&gt;https://databricks.com/blog/2015/04/13/deep-dive-into-spark-sqls-catalyst-optimizer.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://spoddutur.github.io/spark-notes/second_generation_tungsten_engine"&gt;https://spoddutur.github.io/spark-notes/second_generation_tungsten_engine&lt;/a&gt;&lt;/p&gt;

</description>
      <category>apachespark</category>
      <category>sparksql</category>
      <category>dataframes</category>
      <category>dataset</category>
    </item>
    <item>
      <title>Connecting to Github with SSH (Windows)</title>
      <dc:creator>Harshad Ranganathan</dc:creator>
      <pubDate>Sun, 22 Sep 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/harshadranganathan/connecting-to-github-with-ssh-windows-a6h</link>
      <guid>https://forem.com/harshadranganathan/connecting-to-github-with-ssh-windows-a6h</guid>
      <description>&lt;h2&gt;
  
  
  Pre-requisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Git Bash&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to use SSH keys instead of HTTPS
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Your corporate firewall blocks port 22, then even if you have set the remote origin to use HTTPS url, the authentication will fail as it is done via SSH.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remedy is to set up SSH keys and use SSH over HTTPS port 443.&lt;/p&gt;

&lt;p&gt;Below are some of the errors different applications throw when the port is blocked and HTTPS remote url is used.&lt;/p&gt;

&lt;h3&gt;
  
  
  Git Bash
&lt;/h3&gt;

&lt;p&gt;When you try to push your changes, both &lt;code&gt;Github Login&lt;/code&gt; and &lt;code&gt;OpenSSH&lt;/code&gt; prompt asking for your username and password will fail with error &lt;code&gt;remote no anonymous write access. fatal authentication failed for github&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Github Desktop Application
&lt;/h3&gt;

&lt;p&gt;You will be able to login to your github desktop application as the authentication is done via HTTPS. However, when you try to push your changes it will result in authentication failure.&lt;/p&gt;

&lt;p&gt;Below is the logs captured by the desktop application showing that the git push is trying to authenticate via SSH.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;2019-09-22T13:16:46.927Z - info: &lt;span class="o"&gt;[&lt;/span&gt;ui] Executing push: git &lt;span class="nt"&gt;-c&lt;/span&gt; credential.helper&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; protocol.version&lt;span class="o"&gt;=&lt;/span&gt;2 push origin master:master &lt;span class="nt"&gt;--progress&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;took 21.740s&lt;span class="o"&gt;)&lt;/span&gt;
2019-09-22T13:16:46.927Z - error: &lt;span class="o"&gt;[&lt;/span&gt;ui] &lt;span class="sb"&gt;`&lt;/span&gt;git &lt;span class="nt"&gt;-c&lt;/span&gt; credential.helper&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; protocol.version&lt;span class="o"&gt;=&lt;/span&gt;2 push origin master:master &lt;span class="nt"&gt;--progress&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; exited with an unexpected code: 128.
stderr:
ssh: connect to host github.com port 22: Connection timed out
fatal: Could not &lt;span class="nb"&gt;read &lt;/span&gt;from remote repository.

Please make sure you have the correct access rights
and the repository exists.

&lt;span class="o"&gt;(&lt;/span&gt;The error was parsed as 2: Authentication failed. Some common reasons include:

- You are not logged &lt;span class="k"&gt;in &lt;/span&gt;to your account: see File &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Options.
- You may need to log out and log back &lt;span class="k"&gt;in &lt;/span&gt;to refresh your token.
- You &lt;span class="k"&gt;do &lt;/span&gt;not have permission to access this repository.
- The repository is archived on GitHub. Check the repository settings to confirm you are still permitted to push commits.
- If you use SSH authentication, check that your key is added to the ssh-agent and associated with your account.&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Generate new SSH key
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Run Git Bash.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Generate SSH key with your email id as comment.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa &lt;span class="nt"&gt;-b&lt;/span&gt; 4096 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"your_email@example.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When you’re prompted to “Enter a file in which to save the key,” press Enter to save the key in the default location (/c/Users/username/.ssh/id_rsa). Your public key will also get saved here.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy the public key to clipboard.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;clip &amp;lt; ~/.ssh/id_rsa.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add public SSH key to your GitHub account
&lt;/h2&gt;

&lt;p&gt;Go to &lt;code&gt;Settings&lt;/code&gt; in your Github account to add the SSH public key.&lt;/p&gt;

&lt;p&gt;Under &lt;code&gt;SSH keys&lt;/code&gt; tab, select &lt;code&gt;New SSH key&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Give a title and paste the key in the text area.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/09/github-ssh-keys.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frharshad.com%2Fassets%2Fimg%2F2019%2F09%2Fgithub-ssh-keys.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Add private SSH key to the ssh-agent
&lt;/h2&gt;

&lt;p&gt;Git bash tool comes with a ssh-agent.&lt;/p&gt;

&lt;p&gt;Create a new &lt;code&gt;~/.profile&lt;/code&gt; (or) &lt;code&gt;~/.bashrc&lt;/code&gt; file by running below command in git bash.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;vi ~/.profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste below script into your &lt;code&gt;~/.profile&lt;/code&gt; (or) &lt;code&gt;~/.bashrc&lt;/code&gt; file to auto launch the ssh-agent whenever you run your git bash shell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;env=~/.ssh/agent.env

agent_load_env () { test -f "$env" &amp;amp;&amp;amp; . "$env" &amp;gt;| /dev/null ; }

agent_start () {
    (umask 077; ssh-agent &amp;gt;| "$env")
    . "$env" &amp;gt;| /dev/null ; }

agent_load_env

# agent_run_state: 0=agent running w/ key; 1=agent w/o key; 2= agent not running
agent_run_state=$(ssh-add -l &amp;gt;| /dev/null 2&amp;gt;&amp;amp;1; echo $?)

if [! "$SSH_AUTH_SOCK"] || [$agent_run_state = 2]; then
    agent_start
    ssh-add
elif ["$SSH_AUTH_SOCK"] &amp;amp;&amp;amp; [$agent_run_state = 1]; then
    ssh-add
fi

unset env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script will load the identities in the ssh agent from your default location &lt;code&gt;~/.ssh/id_rsa&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Identity added: /c/Users/username/.ssh/id_rsa &lt;span class="o"&gt;(&lt;/span&gt;your_email@example.com&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use SSH over HTTPS
&lt;/h2&gt;

&lt;p&gt;This step is required only if your corporate firewall is blocking port 22.&lt;/p&gt;

&lt;p&gt;Create a new config file in your &lt;code&gt;.ssh&lt;/code&gt; directory i.e. /c/Users/username/.ssh/config&lt;/p&gt;

&lt;p&gt;Paste below contents in the file to use port 443 for SSH connections to host &lt;code&gt;ssh.github.com&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host github.com
  Hostname ssh.github.com
  Port 443
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run below command in git bash to verify that the configuration is working. Ignore any authentication failures.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ssh &lt;span class="nt"&gt;-vT&lt;/span&gt; git@github.com

OpenSSH_8.0p1, OpenSSL 1.1.1c 28 May 2019
debug1: Reading configuration data /c/Users/username/.ssh/config
debug1: /c/Users/username/.ssh/config line 1: Applying options &lt;span class="k"&gt;for &lt;/span&gt;github.com
debug1: Connecting to ssh.github.com &lt;span class="o"&gt;[&lt;/span&gt;192.30.253.122] port 443.
debug1: Connection established.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that the SSH connection is now established via port 443.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;To check if everything works as expected perform below steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Run new git bash shell.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check if the identity has been added to the ssh agent.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ssh-add &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Check that the key is being used by trying to connect to &lt;a href="mailto:git@github.com"&gt;git@github.com&lt;/a&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ssh &lt;span class="nt"&gt;-vT&lt;/span&gt; git@github.com

OpenSSH_8.0p1, OpenSSL 1.1.1c 28 May 2019
debug1: Reading configuration data
debug1: Offering public key
debug1: Server accepts key
debug1: Authentication succeeded &lt;span class="o"&gt;(&lt;/span&gt;publickey&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Authenticated to ssh.github.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Clone the repo using the SSH url.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Going forward, every push/pull will use the SSH keys to authenticate with Github.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://help.github.com/en/articles/connecting-to-github-with-ssh" rel="noopener noreferrer"&gt;https://help.github.com/en/articles/connecting-to-github-with-ssh&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://help.github.com/en/articles/using-ssh-over-the-https-port" rel="noopener noreferrer"&gt;https://help.github.com/en/articles/using-ssh-over-the-https-port&lt;/a&gt;&lt;/p&gt;

</description>
      <category>github</category>
      <category>ssh</category>
      <category>gitbash</category>
    </item>
    <item>
      <title>Website Things To Do</title>
      <dc:creator>Harshad Ranganathan</dc:creator>
      <pubDate>Tue, 09 Jul 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/harshadranganathan/website-things-to-do-2ad2</link>
      <guid>https://forem.com/harshadranganathan/website-things-to-do-2ad2</guid>
      <description>&lt;h2&gt;
  
  
  Secure
&lt;/h2&gt;

&lt;p&gt;Make your site secure by enabling HTTPS on your website.&lt;/p&gt;

&lt;p&gt;HTTPS helps prevent intruders from tampering with the communications between your websites and your users’ browsers. Intruders include intentionally malicious attackers, and legitimate but intrusive companies, such as ISPs or hotels that inject ads into pages.&lt;/p&gt;

&lt;p&gt;Intruders exploit every unprotected resource that travels between your websites and your users. Images, cookies, scripts, HTML … they’re all exploitable. Intrusions can occur at any point in the network, including a user’s machine, a Wi-Fi hotspot, or a compromised ISP, just to name a few.&lt;/p&gt;

&lt;h3&gt;
  
  
  Github Pages
&lt;/h3&gt;

&lt;p&gt;If you have your website on Github, you can enable HTTPS in the Settings section of your repository.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/07/github-pages-https.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aF9FZSV0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/07/github-pages-https.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Let’s Encrypt
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://letsencrypt.org/"&gt;Let’s Encrypt&lt;/a&gt; is a free, automated, and open certificate authority which you can make use of to enable SSL for your website.&lt;/p&gt;

&lt;p&gt;You can make use of Certbot client to automate certificate issuance and installation with no downtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Site Audit
&lt;/h2&gt;

&lt;p&gt;It’s important to measure how your website performs in terms of performance, accessibility, best practices and SEO.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://web.dev/measure"&gt;https://web.dev/measure&lt;/a&gt; - analyses your website and provides useful guidance&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/web/tools/lighthouse/"&gt;https://developers.google.com/web/tools/lighthouse/&lt;/a&gt; - performs audits for performance, accessibility, progressive web apps, and more&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/speed/pagespeed/insights/"&gt;https://developers.google.com/speed/pagespeed/insights/&lt;/a&gt; - analyzes the content of a web page, then generates suggestions to make that page faster&lt;/p&gt;

&lt;p&gt;All of the above are tools offered by Google and use data from Lighthouse project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discoverable
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Google Search Console
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://search.google.com/search-console/about"&gt;Search Console&lt;/a&gt; provides tools and reports help you measure your site’s Search traffic and performance, fix issues, and make your site shine in Google Search results.&lt;/p&gt;

&lt;p&gt;You can submit your &lt;a href="https://en.wikipedia.org/wiki/Sitemaps"&gt;sitemap.xml&lt;/a&gt; to google so that it can be crawled and indexed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/07/google-search-console-sitemaps.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1mluS3CD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/07/google-search-console-sitemaps.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Analytics
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Google Analytics
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://analytics.google.com/analytics/web/"&gt;Google Analytics&lt;/a&gt; is a web analytics service offered by Google that tracks and reports website traffic.&lt;/p&gt;

&lt;p&gt;Inside the console, you need to create new analytics accounts and apps with the option of custom views.&lt;/p&gt;

&lt;p&gt;Once you have it set up, you can track your audience and real time views. You can also set include/exclude filters for your website.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/07/google-analytics-console.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AXMn741y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/07/google-analytics-console.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To enable analytics for your site, add below script to your webpage with the ID of your google analytics property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;);}&lt;/span&gt;
  &lt;span class="nx"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="nx"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GA_MEASUREMENT_ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Monetization
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Google AdSense Ads
&lt;/h3&gt;

&lt;p&gt;Google AdSense is a program run by Google through which website publishers in the Google Network of content sites serve text, images, video, or interactive media advertisements that are targeted to the site content and audience.&lt;/p&gt;

&lt;p&gt;You need to submit your site for review with Google. It will take few days to weeks to have your account activated.&lt;/p&gt;

&lt;p&gt;Once your account is activated, you can either choose enable auto ads or create custom ad units.&lt;/p&gt;

&lt;p&gt;I would suggest to create a custom ad unit so that you can place your ads at appropriate places without affecting the user experience.&lt;/p&gt;

&lt;p&gt;Auto ads issue: &lt;a href="https://support.google.com/adsense/thread/3490813?hl=en"&gt;Adsense auto ads showing on mobile but not on the desktop&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also make sure to add ads.txt file to your site at root domain level.&lt;/p&gt;

&lt;h2&gt;
  
  
  GDPR
&lt;/h2&gt;

&lt;p&gt;The General Data Protection Regulation (GDPR) and the ePrivacy Directive (ePR) affect how you as a website owner may use cookies and online tracking of visitors from the EU.&lt;/p&gt;

&lt;p&gt;One of the most tangible requirements of the GDPR is in the definition of what constitutes a proper cookie consent, meaning, that the consent has to be:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Informed: Why, how and where is the personal data used? It must be clear for the user, what the consent is given to, and it must be possible to opt-in and opt-out of the various types of cookies.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Given by means of an affirmative, positive action that cannot be misinterpreted.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Given prior to the initial processing of the personal data.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Withdrawable. It must be easy for the user to change his or her mind and withdraw the consent.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are several sites which offer GDPR compliant consent function for websites.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/07/cookie-consent-banner.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wTYkqsC1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/07/cookie-consent-banner.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Graph Tags
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://ogp.me/"&gt;Open graph&lt;/a&gt; meta tags allow you to control what content shows up when a page is shared on Facebook and Twitter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:title"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"The Rock"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:type"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"video.movie"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:url"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"http://www.imdb.com/title/tt0117500/"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:image"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"http://ia.media-imdb.com/images/rock.jpg"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Proxy Filters
&lt;/h2&gt;

&lt;p&gt;Lot of companies use proxy filters to restrict access to websites. Check if your website has been properly categorized to avoid being blocked.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sitereview.bluecoat.com/#/"&gt;https://sitereview.bluecoat.com/#/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>website</category>
      <category>thingstodo</category>
      <category>checklist</category>
      <category>analytics</category>
    </item>
    <item>
      <title>DynamoDB Local in Docker</title>
      <dc:creator>Harshad Ranganathan</dc:creator>
      <pubDate>Tue, 09 Jul 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/harshadranganathan/dynamodb-local-in-docker-2odj</link>
      <guid>https://forem.com/harshadranganathan/dynamodb-local-in-docker-2odj</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;DynamoDB local Docker image enables you to get started with DynamoDB local quickly by using a docker image with all the DynamoDB local dependencies and necessary configuration built in. The new Docker image also enables you to include DynamoDB local in your containerized builds and as part of your continuous integration testing.&lt;/p&gt;

&lt;p&gt;Image is available at: &lt;a href="https://hub.docker.com/r/amazon/dynamodb-local"&gt;https://hub.docker.com/r/amazon/dynamodb-local&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Compose
&lt;/h2&gt;

&lt;p&gt;We’ll create a docker compose file to launch dynamo db local as a container which can then be accessed by other containers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;dynamodb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amazon/dynamodb-local:latest&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8000:8000"&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-jar"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DynamoDBLocal.jar"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-sharedDb"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-inMemory"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here,&lt;/p&gt;

&lt;p&gt;image -&lt;/p&gt;

&lt;p&gt;We pull the latest version of amazon/dynamodb-local image&lt;/p&gt;

&lt;p&gt;ports -&lt;/p&gt;

&lt;p&gt;Expose the container port 8000 to local port 8000 as the local dynamo db runs on this port&lt;/p&gt;

&lt;p&gt;command -&lt;/p&gt;

&lt;p&gt;By default the local dynamo db starts with inMemory setting. We override the command here with an additional argument -sharedDb. This is to ensure that the dynamo db uses a single database file instead of separate files for each credential and region. If you didn’t enable this setting then if in case your app container and local CLI use different AWS creds then they won’t be accessing the same dynamo db state. Table created via your CLI won’t be visible to your app container.&lt;/p&gt;

&lt;p&gt;Run docker compose and access your local dynamo db by using the endpoint url option in your CLI commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up

aws dynamodb list-tables &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt; http://localhost:8000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you’re not using the default network mode and instead using bridge network mode, this is how your docker compose will look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;dynamodb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amazon/dynamodb-local:latest&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8000:8000"&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-jar"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DynamoDBLocal.jar"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-sharedDb"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-inMemory"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test-network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ipv4_address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;172.16.231.1&lt;/span&gt;
&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test-network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;
    &lt;span class="na"&gt;driver_opts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s"&gt;com.docker.network.enable_ipv6&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;false"&lt;/span&gt;
    &lt;span class="na"&gt;ipam&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
      &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;subnet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;172.16.231.0/24&lt;/span&gt;
          &lt;span class="na"&gt;gateway&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;172.16.231.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Accessing DynamoDB Local Container From Another Container
&lt;/h2&gt;

&lt;p&gt;Let’s say you have a webapp written in Java running in a tomcat container and you want to access the local dynamo db container for your integration tests.&lt;/p&gt;

&lt;p&gt;You can make use of the hostname identical to the container name to access the local dynamodb from your webapp container, in this case, the endpoint url will be &lt;a href="http://dynamodb:8000"&gt;http://dynamodb:8000&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In your webapp, when you want to access the local dynamo db container, construct the client by setting the endpoint url to &lt;a href="http://dynamodb:8000:"&gt;http://dynamodb:8000:&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;AmazonDynamoDB&lt;/span&gt; &lt;span class="nf"&gt;dynamoDbClient&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;AmazonDynamoDBClientBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;standard&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withClientConfiguration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;defaultClientConfiguration&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withCredentials&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DefaultAWSCredentialsProviderChain&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withRegion&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Returns Dynamo DB client to access local copy
 * where serviceEndpoint is http://dynamodb:8000 and region can be acceptable region names e.g. us-east-1
*/&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;AmazonDynamoDB&lt;/span&gt; &lt;span class="nf"&gt;dynamoDbClient&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;serviceEndpoint&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;AmazonDynamoDBClientBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;standard&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withClientConfiguration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;defaultClientConfiguration&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withCredentials&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DefaultAWSCredentialsProviderChain&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withRegion&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withEndpointConfiguration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EndpointConfiguration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serviceEndpoint&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then in your docker compose file, you need to set the AWS creds and region for your webapp container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;dynamodb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amazon/dynamodb-local:latest&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8000:8000"&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-jar"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DynamoDBLocal.jar"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-sharedDb"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-inMemory"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;tomcat&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;webapp:latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_ACCESS_KEY_ID=dummy&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_SECRET_ACCESS_KEY=dummy&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_REGION=us-east-1&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:8080"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here,&lt;/p&gt;

&lt;p&gt;image -&lt;/p&gt;

&lt;p&gt;Docker image of your web app running in tomcat&lt;/p&gt;

&lt;p&gt;environment -&lt;/p&gt;

&lt;p&gt;Set AWS_ACCESS_KEY_ID &amp;amp; AWS_SECRET_ACCESS_KEY to any dummy value otherwise the credential chain will fail mentioning that it couldn’t detect any credentials.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Tables
&lt;/h2&gt;

&lt;p&gt;Before your application can read/write any data to the local dynamodb, you would have to create the required tables.&lt;/p&gt;

&lt;p&gt;Either you can create the tables as part of your webapp code (or) you can bundle a script to your web app image which will create the tables using AWS CLI and start up your application.&lt;/p&gt;

&lt;p&gt;Your webapp docker build file can be something like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; openjdk:8-jre-alpine&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; TOMCAT_VERSION 8.0.36&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    wget https://archive.apache.org/dist/tomcat/tomcat-8/v&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOMCAT_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/bin/apache-tomcat-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOMCAT_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.gz &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;tar &lt;/span&gt;xvzf apache-tomcat-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOMCAT_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.gz &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;mv &lt;/span&gt;apache-tomcat-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOMCAT_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; tomcat &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; apache-tomcat-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOMCAT_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.gz

&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;awscli &lt;span class="nt"&gt;--upgrade&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH="$PATH:/root/.local/bin"&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; start.sh .&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; JPDA_ADDRESS 8000&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; JPDA_TRANSPORT dt_socket&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; JDPA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080 8000 8009&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["start.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And in your start up script, create the tables and run catalina.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/env bash
aws dynamodb create-table --table-name &amp;lt;table_name&amp;gt; --attribute-definitions &amp;lt;attribute_definitions&amp;gt; \
--key-schema &amp;lt;key_schema&amp;gt; --billing-mode &amp;lt;billing_mode&amp;gt; --endpoint-url http://dynamodb:8000 --region &amp;lt;region&amp;gt;

./tomcat/bin/catalina.sh jpda run
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>dynamodb</category>
      <category>docker</category>
    </item>
    <item>
      <title>Blockchain</title>
      <dc:creator>Harshad Ranganathan</dc:creator>
      <pubDate>Sun, 09 Jun 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/harshadranganathan/blockchain-4jef</link>
      <guid>https://forem.com/harshadranganathan/blockchain-4jef</guid>
      <description>&lt;h2&gt;
  
  
  Introduction to Blockchain
&lt;/h2&gt;

&lt;p&gt;Blockchain is a decentralized, distributed and public digital ledger that is used to maintain a continuously growing list of records, called blocks, which are linked using cryptography. Any involved record cannot be altered retroactively, without the alteration of all subsequent blocks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Blockchain
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Decentralized - Not owned by a single entity. Every participant in the network can access the history of transactions or confirm new transactions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Transparency - Public verifiability of transactions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Security - With no central point to be exploited the system is protected against hacking attacks and fraud.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Immutability - Blocks added to the blockchain can’t be tampered.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No Intermediary - Peer-to-Peer and business-to-business transactions are completed without the need for a third party.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How Blockchain Works
&lt;/h2&gt;

&lt;p&gt;Blockchain starts with a single block called genesis block.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/06/genesis-block.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1wqrCcUN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/06/genesis-block.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each block stores the following information:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Index&lt;/td&gt;
&lt;td&gt;Genesis block will have an index of 0.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timestamp&lt;/td&gt;
&lt;td&gt;A record of when the block was created. Timestamp helps to keep the blockchain in order.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hash&lt;/td&gt;
&lt;td&gt;Digital fingerprint of data. Hash is of fixed length, easy to compute and infeasible to convert back to data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Previous Hash&lt;/td&gt;
&lt;td&gt;Hash of the previous block.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data&lt;/td&gt;
&lt;td&gt;Each block can store data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nonce&lt;/td&gt;
&lt;td&gt;Number used to find a valid hash.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We’ll explain these with the mining of a new block.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/06/mined-block.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wCDsZg_y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/06/mined-block.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Index
&lt;/h3&gt;

&lt;p&gt;| Each new block is given an incremental index value, in this case, it’s 1. |&lt;/p&gt;

&lt;h3&gt;
  
  
  Timestamp
&lt;/h3&gt;

&lt;p&gt;Block creation timestamp in epoch time, 1560082985.203.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hash
&lt;/h3&gt;

&lt;p&gt;SHA256 hash of (index, previous hash, timestamp, data, nonce)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;CryptoJS.SHA256&lt;span class="o"&gt;(&lt;/span&gt;1 + &lt;span class="s1"&gt;'0000018035a828da0878ae92ab6fbb16be1ca87a02a3feaa9e3c2b6871931046'&lt;/span&gt; 
+ 1560082985.203 + &lt;span class="s1"&gt;'HarshadRanganathan'&lt;/span&gt; + 29877&lt;span class="o"&gt;)&lt;/span&gt;.toString&lt;span class="o"&gt;()&lt;/span&gt;

000056f493569b609d6484da94b9031238e80076a8b6a373ae76d3db2746c211
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Previous Hash
&lt;/h3&gt;

&lt;p&gt;Hash of the previous block which is ‘0000018035a828da0878ae92ab6fbb16be1ca87a02a3feaa9e3c2b6871931046’.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data
&lt;/h3&gt;

&lt;p&gt;Block data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nonce
&lt;/h3&gt;

&lt;p&gt;We start with a nonce of ‘1’ and keep incrementing it until we find a valid hash.&lt;/p&gt;

&lt;p&gt;A Hash is considered to be valid if the number of leading zeros matches the difficulty.&lt;/p&gt;

&lt;p&gt;For example, here we have set the difficulty as ‘4’. We will continue to re-generate the hash until it has 4 leading zeros.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5v6s3DSv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/Zzo4Ofa.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5v6s3DSv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/Zzo4Ofa.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As the difficulty increases, the number of possible valid hashes decreases, so it will take more processing power to find a valid hash.&lt;/p&gt;

&lt;p&gt;A new block is added to the blockchain only if it meets the following requirements:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;New block has a valid index i.e. block index should be greater than latest block index.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;New block’s previous hash is valid i.e. previous hash equals latest block hash.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;New block has valid hash i.e. it has been correctly calculated.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;New block’s hash meets difficulty requirement.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As the blocks are added and linked using cryptography, they continuously grow the blockchain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/06/blockchain.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8isEk_2s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/06/blockchain.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Immutability
&lt;/h3&gt;

&lt;p&gt;Blockchain provides immutability of records. If an adversary tampers with any of the data in the entire chain, it will invalidate all the subsequent blocks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fRGIeiVm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/41UnNFa.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fRGIeiVm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/41UnNFa.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s assume we have 3 blocks and we want to tamper the data in block #1.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;If you change the data in block #1 then the SHA256 hash value of the block changes as it is calculated based on the data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Block #2’s hash changes as it is based on block #1’s hash.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Block #3’s hash changes as it is based on block #2’s hash.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now, all the 3 blocks are invalid as they don’t meet the difficulty requirement of having 4 leading zeros.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You will then have to mine all the invalidated blocks again by finding new nonce values to make them valid.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This will be a compute intensive and unfeasible operation as the chain continuously grows with new blocks.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/blockchain-cli"&gt;https://www.npmjs.com/package/blockchain-cli&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blockchaindemo.io"&gt;https://blockchaindemo.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://anders.com/blockchain/blockchain.html"&gt;https://anders.com/blockchain/blockchain.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
    </item>
    <item>
      <title>Microsoft Teams Webhook Integration</title>
      <dc:creator>Harshad Ranganathan</dc:creator>
      <pubDate>Fri, 17 May 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/harshadranganathan/microsoft-teams-webhook-integration-4m3l</link>
      <guid>https://forem.com/harshadranganathan/microsoft-teams-webhook-integration-4m3l</guid>
      <description>&lt;p&gt;Originally published at &lt;a href="https://rharshad.com/microsoft-teams-webhook-integration/"&gt;rharshad.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Microsoft Team's &lt;code&gt;Incoming webhook&lt;/code&gt; connector allows you to publish messages to teams channel.&lt;/p&gt;

&lt;p&gt;You can even add actions to the content so that users can complete tasks within the channel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Webhook Connector to a Channel
&lt;/h2&gt;

&lt;p&gt;In Microsoft Teams, choose the &lt;code&gt;More options (⋯)&lt;/code&gt; button next to the channel name in the list of channels and then choose Connectors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2018/05/webhook-connector.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--80E1N-YU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2018/05/webhook-connector.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add &lt;code&gt;Incoming webhook&lt;/code&gt; connector to the channel.&lt;/p&gt;

&lt;p&gt;Now choose &lt;code&gt;configure&lt;/code&gt; button next to the &lt;code&gt;Incoming Webhook&lt;/code&gt; connector.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2018/05/incoming-webhook.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uELA_JlA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2018/05/incoming-webhook.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Provide a name and upload an image for the connector. Now select &lt;code&gt;Create&lt;/code&gt; button.&lt;/p&gt;

&lt;p&gt;Copy the webhook URL for later reference.&lt;/p&gt;

&lt;p&gt;We have now set up the webhook for the channel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending Messages to Webhook
&lt;/h2&gt;

&lt;p&gt;To send a message to the webhook connector we have to send it as a JSON payload. Refer &lt;a href="https://docs.microsoft.com/en-us/outlook/actionable-messages/message-card-reference"&gt;message card template&lt;/a&gt; on how the payload needs to be structured.&lt;/p&gt;

&lt;p&gt;Note: &lt;a href="https://docs.microsoft.com/en-us/outlook/actionable-messages/adaptive-card"&gt;Adaptive cards&lt;/a&gt; are the recommended card type for new Teams development. They are the new cross product specification for cards in Microsoft products including Bots, Cortana, Outlook, and Windows. However, they are not supported yet in teams connectors.&lt;/p&gt;

&lt;p&gt;You can experiment your card design at &lt;a href="https://messagecardplayground.azurewebsites.net/"&gt;Card Playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We will send below sample payload to the connector and check if it works.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Send this JSON payload as a POST request to the Webhook URL. &lt;/p&gt;

&lt;p&gt;You can either use &lt;code&gt;Send via WebHook&lt;/code&gt; option available in the &lt;a href="https://messagecardplayground.azurewebsites.net/"&gt;Card Playground&lt;/a&gt; (or) &lt;a href="https://docs.microsoft.com/en-us/outlook/actionable-messages/actionable-messages-via-connectors#send-the-message"&gt;Postman&lt;/a&gt; to send the message to the webhook.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2018/05/webhook-message.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XCjndxIQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2018/05/webhook-message.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is a python library which you can make use of for building the messages dynamically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pypi.org/project/ms-teams/"&gt;https://pypi.org/project/ms-teams/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending Messages to Webhook from Jenkins Build
&lt;/h2&gt;

&lt;p&gt;You can make use of the &lt;a href="https://wiki.jenkins.io/display/JENKINS/Office+365+Connector+Plugin"&gt;office 365 connector plugin&lt;/a&gt; to send build messages to the webhook. &lt;/p&gt;

&lt;p&gt;However, if you want to send a custom message as part of your jenkins job then we can't make use of this plugin.&lt;/p&gt;

&lt;p&gt;Below is a sample which sends a custom card message to the webhook using &lt;code&gt;httpRequest&lt;/code&gt; step in &lt;a href="https://rharshad.com/jenkins-pipeline-as-code/"&gt;jenkins pipeline code&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;This card shows the build parameter values with an action button to view the build job.&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/connectors"&gt;https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/connectors&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/outlook/actionable-messages/actionable-messages-via-connectors"&gt;https://docs.microsoft.com/en-us/outlook/actionable-messages/actionable-messages-via-connectors&lt;/a&gt;&lt;/p&gt;

</description>
      <category>microsoftteams</category>
      <category>webhook</category>
      <category>jenkins</category>
      <category>messagecards</category>
    </item>
    <item>
      <title>Getting started with PySpark on Windows and PyCharm</title>
      <dc:creator>Harshad Ranganathan</dc:creator>
      <pubDate>Mon, 13 May 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/harshadranganathan/getting-started-with-pyspark-on-windows-and-pycharm-26l3</link>
      <guid>https://forem.com/harshadranganathan/getting-started-with-pyspark-on-windows-and-pycharm-26l3</guid>
      <description>&lt;h2&gt;
  
  
  Pre-Requisites
&lt;/h2&gt;

&lt;p&gt;Both Java and Python are installed in your system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started with Spark on Windows
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://spark.apache.org/downloads.html"&gt;Download Apache Spark&lt;/a&gt; by choosing a Spark release (e.g. 2.2.0) and package type (e.g. Pre-built for Apache Hadoop 2.7 and later).&lt;/p&gt;

&lt;p&gt;Extract the Spark tar file to a directory e.g. C:\Spark\spark-2.2.0-bin-hadoop2.7&lt;/p&gt;

&lt;p&gt;GIT clone &lt;a href="https://github.com/steveloughran/winutils"&gt;winutils&lt;/a&gt; to your system e.g. cloned to directory C:\winutils&lt;/p&gt;

&lt;p&gt;Add below system environment variables where &lt;code&gt;HADOOP_HOME&lt;/code&gt; is set to the winutils hadoop binary location (depending on the version of pre-built chosen earlier) and &lt;code&gt;SPARK_HOME&lt;/code&gt; is set to the Spark location which we had extracted in step 2.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HADOOP_HOME=C:\winutils\hadoop-2.7.1
SPARK_HOME=C:\Spark\spark-2.2.0-bin-hadoop2.7
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Create a new folder &lt;code&gt;tmp/hive&lt;/code&gt; in your C: drive.&lt;/p&gt;

&lt;p&gt;Provide permissions for the folder &lt;code&gt;tmp/hive&lt;/code&gt; using &lt;code&gt;winutils.exe&lt;/code&gt; by running below command in your command prompt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C:\winutils\hadoop-2.7.1\bin\winutils.exe chmod 777 C:\tmp\hive
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now validate the setup by running &lt;code&gt;spark-shell&lt;/code&gt; from your &lt;code&gt;SPARK_HOME&lt;/code&gt; directory in your command prompt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C:\Spark\spark-2.2.0-bin-hadoop2.7&amp;gt;bin\spark-shell
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2018/05/spark-shell.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vvzPVvPA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2018/05/spark-shell.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  PyCharm Configuration
&lt;/h2&gt;

&lt;p&gt;Configure the python interpreter to support pyspark by following the below steps&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new virtual environment (File -&amp;gt; Settings -&amp;gt; Project Interpreter -&amp;gt; select &lt;code&gt;Create Virtual Environment&lt;/code&gt; in the settings option)&lt;/li&gt;
&lt;li&gt;In the &lt;code&gt;Project Interpreter&lt;/code&gt; dialog, select &lt;code&gt;More&lt;/code&gt; in the settings option and then select the new virtual environment. Now select &lt;code&gt;Show paths for the selected interpreter&lt;/code&gt; option.&lt;/li&gt;
&lt;li&gt;Add the paths for &lt;code&gt;Spark Python&lt;/code&gt; and &lt;code&gt;Spark Py4j&lt;/code&gt; to this virtual environment as shown in the screenshot below.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2018/05/pycharm-interpreter-paths.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ja44Vptb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2018/05/pycharm-interpreter-paths.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create a new run configuration for &lt;code&gt;Python&lt;/code&gt; in the dialog &lt;code&gt;Run\Debug Configurations&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;Python interpreter&lt;/code&gt; option select the interpreter which we had created in the first step. Also in the Environment variables option make sure &lt;code&gt;Include parent environment variables&lt;/code&gt; is checked.&lt;/p&gt;

&lt;p&gt;You can now add your pyspark script to the project and use this run configuration to execute it in a Spark context.&lt;/p&gt;

</description>
      <category>pyspark</category>
      <category>windows</category>
      <category>intellij</category>
      <category>pycharm</category>
    </item>
    <item>
      <title>Jenkins Pipeline as Code</title>
      <dc:creator>Harshad Ranganathan</dc:creator>
      <pubDate>Sun, 12 May 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/harshadranganathan/jenkins-pipeline-as-code-30b5</link>
      <guid>https://forem.com/harshadranganathan/jenkins-pipeline-as-code-30b5</guid>
      <description>&lt;p&gt;Originally published at &lt;a href="https://rharshad.com/jenkins-pipeline-as-code/" rel="noopener noreferrer"&gt;rharshad.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jenkins.io/doc/book/pipeline-as-code/" rel="noopener noreferrer"&gt;Jenkins Pipeline&lt;/a&gt; is a suite of plugins which supports implementing and integrating continuous delivery pipelines into Jenkins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Need for Jenkins Pipeline as Code
&lt;/h2&gt;

&lt;p&gt;Over time, Jenkins, like most other self-hosted CI/CD tools resulted in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accumulation/tendency to create vast number of jobs&lt;/li&gt;
&lt;li&gt;Hard and costly maintenance&lt;/li&gt;
&lt;li&gt;Heavy reliance on UI&lt;/li&gt;
&lt;li&gt;Lack of powerful ways to specify conditional logic&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Features of Pipeline as Code
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Pipelines are implemented as code and typically checked into source control, serving as a single source of truth enabling the team members to edit, review, and iterate upon their pipeline.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pipelines can optionally stop and wait for human input or approval before continuing the Pipeline run.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Pipelines support complex real-world requirements&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conditional executions&lt;/li&gt;
&lt;li&gt;Chaining jobs&lt;/li&gt;
&lt;li&gt;Linear/Complex flows&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;The Pipeline plugin supports custom extensions to its DSL and multiple options for integration with other plugins.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://imgur.com/WshehVd" rel="noopener noreferrer"&gt;https://imgur.com/WshehVd&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pipeline Autocompletion in IntelliJ
&lt;/h2&gt;

&lt;p&gt;To enable code completion follow below steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download Pipeline GDSL (http://(yourjenkinsurl)/job/(yourpipelinejob)/pipeline-syntax/gdsl) from your jenkins.&lt;/li&gt;
&lt;li&gt;Add &lt;a href="https://www.bonusbits.com/wiki/HowTo:Add_Groovy_SDK_to_IntelliJ_IDEA" rel="noopener noreferrer"&gt;Groovy SDK support&lt;/a&gt;  to Intellij.&lt;/li&gt;
&lt;li&gt;Place the GDSL file inside &lt;code&gt;src&lt;/code&gt; directory of your project and mark the &lt;code&gt;src&lt;/code&gt; folder as &lt;code&gt;Sources Root&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Open the GDSL file. Now, IntelliJ will show a message &lt;code&gt;DSL descriptor file has been changed and isn't currently executed.&lt;/code&gt;. Click &lt;code&gt;Activate&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Now create your &lt;code&gt;.groovy&lt;/code&gt; jenkins file and write the code, auto completion will work.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Snippet Generator
&lt;/h2&gt;

&lt;p&gt;You can also generate snippets of code for the pipeline steps using the &lt;code&gt;Snippet Generator&lt;/code&gt; feature available in Jenkins (http://(yourjenkinsurl)/job/(yourpipelinejob)/pipeline-syntax/) which you can then make use of in your pipeline.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2018/05/jenkins-snippet-generator.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frharshad.com%2Fassets%2Fimg%2F2018%2F05%2Fjenkins-snippet-generator.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Replay
&lt;/h2&gt;

&lt;p&gt;Replay feature allows you to test your pipeline code without having to commit the changes to your Repo. Once you have tested the code you can then push your changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2018/05/jenkins-replay.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frharshad.com%2Fassets%2Fimg%2F2018%2F05%2Fjenkins-replay.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Jenkins Declarative Pipeline Example
&lt;/h2&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.cloudbees.com/blog/need-jenkins-pipeline" rel="noopener noreferrer"&gt;https://www.cloudbees.com/blog/need-jenkins-pipeline&lt;/a&gt;&lt;/p&gt;

</description>
      <category>jenkins</category>
      <category>pipelineascode</category>
      <category>jenkinsfile</category>
    </item>
    <item>
      <title>Web Push Notifications for your React app using Firebase</title>
      <dc:creator>Harshad Ranganathan</dc:creator>
      <pubDate>Tue, 07 May 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/harshadranganathan/web-push-notifications-for-your-react-app-using-firebase-iab</link>
      <guid>https://forem.com/harshadranganathan/web-push-notifications-for-your-react-app-using-firebase-iab</guid>
      <description>&lt;h2&gt;
  
  
  Push Notifications
&lt;/h2&gt;

&lt;p&gt;Push notifications allow users to get updates and engage with your content. You can send push notifications via browsers (Desktop/Mobile) which support Push &amp;amp; Notification API.&lt;/p&gt;

&lt;p&gt;Below browsers support Push &amp;amp; Notification API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chrome Desktop and Mobile (version 50+)&lt;/li&gt;
&lt;li&gt;Firefox Desktop and Mobile (version 44+)&lt;/li&gt;
&lt;li&gt;Opera on Mobile (version 37+)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Let’s see what’s involved in setting up a push notification service for a site.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/03/push-notification-flow.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kb6aZrH2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/03/push-notification-flow.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Service Worker
&lt;/h3&gt;

&lt;p&gt;Push is based on service workers because service workers operate in the background. A service worker is a “special” JavaScript file.&lt;/p&gt;

&lt;p&gt;The browser can execute this JavaScript without your page being open. This means the only time code is run for a push notification (in other words, the only time the battery is used) is when the user interacts with a notification by clicking it or closing it.&lt;/p&gt;

&lt;p&gt;It’s inside the service worker’s ‘push’ event that you can perform any background tasks. You can make analytics calls,cache pages offline and show notifications.&lt;/p&gt;

&lt;p&gt;You must check if the browser supports service workers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Push Manager
&lt;/h3&gt;

&lt;p&gt;The PushManager interface of the Push API provides a way to receive notifications from third-party servers as well as request URLs for push notifications.&lt;/p&gt;

&lt;p&gt;You must check if the browser supports push manager.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service Worker Registration
&lt;/h3&gt;

&lt;p&gt;Once we know that the browser supports service worker and push manager we register our service worker javascript file.&lt;/p&gt;

&lt;p&gt;The browser will then run the file in a service worker environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  User Permission
&lt;/h3&gt;

&lt;p&gt;Once we register the service worker we need to get permission from the user to send push notifications.&lt;/p&gt;

&lt;p&gt;If the user blocks the permission request then they have to manually unblock the site in the browser settings panel.&lt;/p&gt;

&lt;h3&gt;
  
  
  User Subscription
&lt;/h3&gt;

&lt;p&gt;After registering the service worker and getting user permission, we need to subscribe the user.&lt;/p&gt;

&lt;p&gt;We need to generate VAPID keys and submit to the push service. These keys are used by the push service to identify the application subscribing the user and ensure that the same application is the one messaging the user.&lt;/p&gt;

&lt;p&gt;Once you subscribe you will receive an endpoint, associated with the app’s public key and an identifier (push subscription).&lt;/p&gt;

&lt;p&gt;Later, when you want to send a push message, you’ll need to create an Authorization header which will contain information signed with your application server’s private key and submit to that endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subscription Storage
&lt;/h3&gt;

&lt;p&gt;We need to store the push subscription details by sending it to our server so that we can use it to send messages to a user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Push Notification
&lt;/h3&gt;

&lt;p&gt;To send a push message we need to do a web push by sending a payload with an Authorization header signed with the private key.&lt;/p&gt;

&lt;p&gt;The push service will use the public key to decrypt the authorization header and verify that it is the same application that subscribed the user which is trying to send a message.&lt;/p&gt;

&lt;p&gt;It will then send the push message to the user’s device when the browser becomes active.&lt;/p&gt;

&lt;h2&gt;
  
  
  Firebase Cloud Messaging
&lt;/h2&gt;

&lt;p&gt;Firebase Cloud Messaging (FCM) is a cross-platform messaging solution that lets you reliably deliver messages at no cost.&lt;/p&gt;

&lt;p&gt;We’ll see how we can use FCM to send notification messages to the client.&lt;/p&gt;

&lt;p&gt;Below is how our application will work. We won’t go into the details of how to build and deploy a react app as it is outside the scope of this guide.&lt;/p&gt;

&lt;p&gt;Source code of the application can be found here -&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qF2jUiUG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-6a5bca60a4ebf959a6df7f08217acd07ac2bc285164fae041eacb8a148b1bab9.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/HarshadRanganathan"&gt;
        HarshadRanganathan
      &lt;/a&gt; / &lt;a href="https://github.com/HarshadRanganathan/gnib-visa-app"&gt;
        gnib-visa-app
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Find appointment slots for GNIB (Irish Residence Permit) and Re-Entry Visa without hassle
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="instapaper_body md"&gt;
&lt;p&gt;&lt;a href="https://travis-ci.com/HarshadRanganathan/gnib-visa-app" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/1ca383fee2785ccdaf1675f57380a3eb2a9d68c8/68747470733a2f2f7472617669732d63692e636f6d2f4861727368616452616e67616e617468616e2f676e69622d766973612d6170702e7376673f6272616e63683d6d6173746572" alt="Build Status"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
gnib-visa-app&lt;/h1&gt;
&lt;p&gt;Find appointment slots for GNIB (Irish Residence Permit) without hassle.&lt;/p&gt;
&lt;p&gt;App is hosted at &lt;a href="https://gnib-visa-app.rharshad.com/" rel="nofollow"&gt;https://gnib-visa-app.rharshad.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This App utilizes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://reactjs.org/" rel="nofollow"&gt;React&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/axios" rel="nofollow"&gt;axios&lt;/a&gt; for promise based http requests&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://redux.js.org/" rel="nofollow"&gt;Redux&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/redux-thunk" rel="nofollow"&gt;redux-thunk&lt;/a&gt; for middleware&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/express" rel="nofollow"&gt;Express&lt;/a&gt; minimalist web framework&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://webpack.github.io/" rel="nofollow"&gt;Webpack&lt;/a&gt; for bundling&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://babeljs.io/" rel="nofollow"&gt;Babel&lt;/a&gt; for transpiling&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Running the App locally&lt;/h2&gt;
&lt;p&gt;You'll need Node &amp;amp; NPM installed on your local development machine.&lt;/p&gt;
&lt;p&gt;Install the project dependencies by running below commands.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# install react project dependencies
npm install
# install notification project dependencies
cd notifications/
npm install
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;
Development mode&lt;/h3&gt;
&lt;h4&gt;
&lt;code&gt;npm run dev&lt;/code&gt;
&lt;/h4&gt;
&lt;p&gt;Runs the app in development mode with webpack recompiling and tests re-run whenever any files change.&lt;/p&gt;
&lt;p&gt;Open &lt;a href="http://localhost:8080" rel="nofollow"&gt;http://localhost:8080&lt;/a&gt; (defaults to port 8080) to view the app in the browser.&lt;/p&gt;
&lt;p&gt;The page will automatically reload if you make changes to the code.&lt;/p&gt;
&lt;h3&gt;
Production mode&lt;/h3&gt;
&lt;h4&gt;
&lt;code&gt;npm run build&lt;/code&gt;
&lt;/h4&gt;
&lt;p&gt;Builds the app for production to the &lt;code&gt;public&lt;/code&gt; folder.&lt;/p&gt;
&lt;p&gt;It bundles React in production mode, minifies the files and the…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/HarshadRanganathan/gnib-visa-app"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/03/push-notification-how-it-works.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K0CIOTgj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/03/push-notification-how-it-works.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sample notification page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/03/sample-push-notification-page.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H4GtCfdz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/03/sample-push-notification-page.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Client Side
&lt;/h2&gt;

&lt;p&gt;Push API relies on a few different pieces of technology, including Web App Manifests and Service Workers.&lt;/p&gt;

&lt;p&gt;Let’s see the steps involved in enabling Push API for your react app. We’ll use &lt;code&gt;Firebase SDK&lt;/code&gt; to facilitate instant messaging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Firebase to your app
&lt;/h3&gt;

&lt;p&gt;To add Firebase to your app, you’ll need a Firebase project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a firebase project in the &lt;a href="https://console.firebase.google.com/"&gt;Firebase console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Get your app config which we will use to initialize firebase in your react app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/03/firebase-app-config.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YKVdQKpB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/03/firebase-app-config.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install firebase npm module.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save firebase
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;firebase.js&lt;/code&gt; file where we will initialize our app with the configuration details we got from our firebase project and also export the &lt;code&gt;messaging&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;We will use the &lt;code&gt;messaging&lt;/code&gt; reference later to register our service worker and handle incoming push notifications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;firebase&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firebase/app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@firebase/messaging&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;messagingSenderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;your-app-messaging-sender-id&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;messaging&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// we need to check if messaging is supported by the browser&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messaging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSupported&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;messaging&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messaging&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;messaging&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Subscription Page
&lt;/h3&gt;

&lt;p&gt;Next, we will create a &lt;code&gt;notifications.js&lt;/code&gt; react component page which will check if the browser supports &lt;code&gt;Service Worker&lt;/code&gt; and &lt;code&gt;Push API&lt;/code&gt;. Based on it we will either display a message mentioning that push notifications are not supported or our subscription options.&lt;/p&gt;

&lt;p&gt;Here, we have used &lt;code&gt;material-ui&lt;/code&gt; for styling the page. Also, we import our &lt;code&gt;firebase.js&lt;/code&gt; file which we had created previously.&lt;/p&gt;

&lt;p&gt;We are following the passive approach here, which is to have a button or toggle switch that enables / disables push messages in a location on the page that is consistent throughout a site. Good UX design is to offer a way out for the user from the push messages and also not to ask for push permissions as soon as the user visits the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;PropTypes&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prop-types&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;withStyles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CardContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Switch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;FormControlLabel&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@material-ui/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;messaging&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../component/firebase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;marginTop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;marginBottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;marginLeft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;marginRight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;noteTextPos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;marginTop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Notifications&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;renderSubscriptionOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;serviceWorker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PushManager&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;noteTextPos&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    Notification feature is supported only in:&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;br&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
                    Chrome Desktop and Mobile (version 50+)&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;br&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
                    Firefox Desktop and Mobile (version 44+)&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;br&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
                    Opera on Mobile (version 37+)
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;FormControlLabel&lt;/span&gt; 
                        &lt;span class="na"&gt;control=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Switch&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                        &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s2"&gt;"Enable/Disable GNIB(IRP) Appointment Notifications"&lt;/span&gt;
                        &lt;span class="na"&gt;onChange=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gnibApptSubscriptionToggle&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                        &lt;span class="na"&gt;checked=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gnibApptSubscriptionToggleSwitch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;classes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CardContent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;renderSubscriptionOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;CardContent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;propTypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PropTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isRequired&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;withStyles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;Notifications&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Service Worker
&lt;/h3&gt;

&lt;p&gt;Next, we need to create a service worker file and register it.&lt;/p&gt;

&lt;p&gt;Create a new file &lt;code&gt;firebase-messaging-sw.js&lt;/code&gt; inside the &lt;code&gt;pwa&lt;/code&gt; directory with below contents.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// import firebase scripts inside service worker js script&lt;/span&gt;
&lt;span class="nx"&gt;importScripts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.gstatic.com/firebasejs/5.7.2/firebase-app.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;importScripts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.gstatic.com/firebasejs/5.7.2/firebase-messaging.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messagingSenderId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;your-app-messaging-sender-id&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messaging&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messaging&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We then register the service worker file by updating &lt;code&gt;firebase.js&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// register service worker&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;serviceWorker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;registration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/firebase-messaging-sw.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;updateViaCache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nx"&gt;messaging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useServiceWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;registration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/03/service-worker.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4dMMvX8h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/03/service-worker.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Few points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;We mentioned the path of the worker file as &lt;code&gt;/firebase-messaging-sw.js&lt;/code&gt; but we had placed the file inside &lt;code&gt;pwa&lt;/code&gt; directory. This is because, later, we will be updating webpack config to copy the files to the &lt;code&gt;public&lt;/code&gt; folder from where the static assets will be served. Based on your app design and bundler used you might have to update the path accordingly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We have set &lt;code&gt;updateViaCache&lt;/code&gt; to &lt;code&gt;none&lt;/code&gt;, so that the HTTP cache will not be consulted when making requests for either the top-level /service-worker.js or for any imported scripted. Prior to Chrome 68, the update request for /service-worker.js would be made via the HTTP cache. Starting in 68, the HTTP cache will be ignored when requesting updates to the service worker script, so existing web applications may see an increase in the frequency of requests for their service worker script. Requests for importScripts will still go via the HTTP cache.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Every time our page loads, the browser will download the latest copy of service worker and will do a byte-to-byte comparison to see if anything has changed. If so, it will activate the new copy otherwise it won’t perform the update.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Also, if it notices any difference with the latest copy it won’t immediately activate the new service worker. The browser will wait until the current service worker controlls zero clients. You can however, force an update to be done.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Notification Message
&lt;/h3&gt;

&lt;p&gt;Notification messages are handled differently depending on whether the page is in the foreground (has focus), or in the background, hidden behind other tabs, or completely closed.&lt;/p&gt;

&lt;h4&gt;
  
  
  Background Message Handling
&lt;/h4&gt;

&lt;p&gt;We had earlier defined &lt;code&gt;firebase-messaging-sw.js&lt;/code&gt; file which imported the firebase scripts and initialised the messaging component. That will take care of background message handling.&lt;/p&gt;

&lt;p&gt;Whenever, a push event is sent, the service worker will get activated. The JS script is then run and the messaging component will make use of the Notification API to display the message in a standard format.&lt;/p&gt;

&lt;p&gt;We will look at how the message is constructed with the content and action links in our server side section.&lt;/p&gt;

&lt;h4&gt;
  
  
  Foreground Message Handling
&lt;/h4&gt;

&lt;p&gt;When your page in focus, then you need to explicity handle how your message gets displayed. For example, we need to make use of the &lt;code&gt;onMessage&lt;/code&gt; function to handle the incoming message and show the notification using the service worker.&lt;/p&gt;

&lt;p&gt;We update &lt;code&gt;firebase.js&lt;/code&gt; file with the &lt;code&gt;onMessage&lt;/code&gt; handler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// register service worker &amp;amp; handle push events&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;serviceWorker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;registration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/firebase-messaging-sw.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;updateViaCache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nx"&gt;messaging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useServiceWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;registration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;messaging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fcmOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Book Appointment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="nx"&gt;registration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;showNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;           
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here, we get the message, icon, link and use the service worker to display the notification. We also create a notification action to handle click events.&lt;/p&gt;

&lt;p&gt;Refer:&lt;/p&gt;

&lt;p&gt;[1] &lt;a href="https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages"&gt;FCM message structure&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Update &lt;code&gt;firebase-messaging-sw.js&lt;/code&gt; with the &lt;code&gt;notificationclick&lt;/code&gt; listener:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;importScripts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.gstatic.com/firebasejs/5.7.2/firebase-app.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;importScripts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.gstatic.com/firebasejs/5.7.2/firebase-messaging.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messagingSenderId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;your-app-messaging-sender-id&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messaging&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messaging&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;notificationclick&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;openWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Whenever, the notification is clicked, it will open the link in a new window and will close the notification.&lt;/p&gt;

&lt;p&gt;Refer:&lt;/p&gt;

&lt;p&gt;[1] &lt;a href="https://notifications.spec.whatwg.org/#dom-notification-onclick"&gt;Notification Click Listener&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  User Permissions
&lt;/h3&gt;

&lt;p&gt;So far, we have seen how to register service workers, display notifications etc. Inorder to display push notifications the user has to accept it. If incase, the user rejects or blocks the site we won’t be able to send any notifications until the user manually revokes it.&lt;/p&gt;

&lt;p&gt;Let’s update &lt;code&gt;notifications.js&lt;/code&gt; file with logic to handle user permissions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Check if user has already given permission for sending notifications
 * If not, request permission from user, generate instance token and store it in firestore
 */&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;notificationPermission&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;permissionGranted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/* request permission if not granted */&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;permission&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;granted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;messaging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestPermission&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="cm"&gt;/* get instance token if not available */&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;INSTANCE_TOKEN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;permissionGranted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;messaging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// returns the same token on every invocation until refreshed by browser&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendTokenToDb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;INSTANCE_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;permissionGranted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasOwnProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messaging/permission-default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You need to allow the site to send notifications&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasOwnProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messaging/permission-blocked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Currently, the site is blocked from sending notifications. Please unblock the same in your browser settings&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unable to subscribe you to notifications&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;permissionGranted&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We do couple of things here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check if the user has already granted the permission to send push notifications. If not, we request for it.&lt;/li&gt;
&lt;li&gt;Also we check if the token is available in the local storage (we will store it later).&lt;/li&gt;
&lt;li&gt;Finally, if above conditions aren’t satisfied we request for a token. If you request for it multiple times you will only get the same instance token back. We then send this token to our server to have it stored in firestore (or any file/database) as we need it to send push notifications. Also, we store the token in localStorage to quickly identify if the user has subscribed for notifications or not and display appropriate toggle switches.&lt;/li&gt;
&lt;li&gt;If incase, the user has refused the permission then we can display the required messages to the user perhaps as a toast/snackbar.&lt;/li&gt;
&lt;li&gt;Registration token may change when: 

&lt;ul&gt;
&lt;li&gt;The app deletes Instance ID&lt;/li&gt;
&lt;li&gt;The app is restored on a new device&lt;/li&gt;
&lt;li&gt;The user uninstalls/reinstall the app&lt;/li&gt;
&lt;li&gt;The user clears app/site data&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You need to call ‘onTokenRefresh’ to send the refreshed token to your server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subscribe/Unsubscribe
&lt;/h3&gt;

&lt;p&gt;We need to provide subscription options to our user based on whether he has already subscribed / not.&lt;/p&gt;

&lt;p&gt;For example, in our &lt;code&gt;notifications.js&lt;/code&gt; we could do something like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ROOT_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// define your server subscription url (sample express server setup for handling subscriptions described at the end)&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Notifications&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;gnibApptSubscriptionToggleSwitch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// we set the toggle switch to false on component load&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gnibApptSubscriptionToggle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gnibApptSubscriptionToggle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribeGnibApptNotifications&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribeGnibApptNotifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unsubscribeGnibApptNotifications&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unsubscribeGnibApptNotifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notificationPermission&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notificationPermission&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * If registration token is available in localStorage we enable the subscription option to indicate that the user has 
     * already subscribed
     */&lt;/span&gt;
    &lt;span class="nx"&gt;componentDidMount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GNIB_APPT_NOTIFICATION_SUBSCRIBED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TRUE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gnibApptSubscriptionToggleSwitch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gnibApptSubscriptionToggleSwitch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
    * Send the subscription details (token and topic) to the server endpoint 
    */&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;subscriptionActions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ROOT_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;topic&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Subscribe app instance to notification topic if user permissions given
     */&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;subscribeGnibApptNotifications&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notificationPermission&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notificationPermission&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;notificationPermission&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isSubscribed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptionActions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SUBSCRIBE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;INSTANCE_TOKEN&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;GNIB_APPT_NOTIFICATIONS_TOPIC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isSubscribed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GNIB_APPT_NOTIFICATION_SUBSCRIBED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TRUE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gnibApptSubscriptionToggleSwitch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;displayMessage&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;GNIB(IRP) appointment notifications have been enabled for your device&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;displayMessage&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Unable to subscribe you to notifications&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Unsubscribe app instance from notification topic
     */&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;unsubscribeGnibApptNotifications&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isUnSubscribed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptionActions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UNSUBSCRIBE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;INSTANCE_TOKEN&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;GNIB_APPT_NOTIFICATIONS_TOPIC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isUnSubscribed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GNIB_APPT_NOTIFICATION_SUBSCRIBED&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deleteTokenFromDb&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gnibApptSubscriptionToggleSwitch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;displayMessage&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;You have been unsubscribed from notifications&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;displayMessage&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Unsubscribe failed&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;);&lt;/span&gt;   
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Subscribe/UnSubscribe appointment notifications
     */&lt;/span&gt;
    &lt;span class="nx"&gt;gnibApptSubscriptionToggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribeGnibApptNotifications&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unsubscribeGnibApptNotifications&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Initially, we set the subscription toggle switch state to &lt;code&gt;false&lt;/code&gt;. In our previous function, &lt;code&gt;notificationPermission&lt;/code&gt; we had set the token in localStorage.&lt;/p&gt;

&lt;p&gt;When the component mounts, we check if that token is available, if so we enable the toggle switch to indicate to the user that he has already subscribed.&lt;/p&gt;

&lt;p&gt;If incase, this is a new subscription, we first get the permissions from the user and generate the registration token. In our &lt;code&gt;notificationPermission&lt;/code&gt; function, we store the registration token in our database and also in localStorage.&lt;/p&gt;

&lt;p&gt;We are also subscribing the user to a topic in function call &lt;code&gt;subscriptionActions&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can either send notifications to a set of users using their registration tokens (or) subscribe them to a topic and just publish a message to the topic. If incase, of publish-subscribe model, the message will get sent to all subscribers of topic.&lt;/p&gt;

&lt;p&gt;We will look into them more in detail later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Manifest file
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;pwa&lt;/code&gt; directory in &lt;code&gt;src&lt;/code&gt; folder which will contain the &lt;code&gt;manifest.json&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"gcm_sender_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"103953800507"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This indicates that FCM is authorized to send messages to this app.&lt;/p&gt;

&lt;p&gt;Link the manifest in your index file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"manifest"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"manifest.json"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you are using webpack to generate your build files then you can use &lt;code&gt;copy-webpack-plugin&lt;/code&gt; to copy the manifest file to the build directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save copy-webpack-plugin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CopyPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;copy-webpack-plugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;srcPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buildPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;CopyPlugin&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;srcPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pwa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;buildPath&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you are using Nginx to serve your static assets then you can specify to gzip the manifest file and specify content expiry time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// virtual.conf
map $sent_http_content_type $expires {
    default off;
    application/json 2628000;
    application/manifest+json 2628000;
}

// gzip.conf
gzip on;
gzip_static on;
gzip_comp_level 6;
gzip_types
    text/plain
    text/css
    text/js
    text/javascript
    application/javascript
    application/manifest+json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;We now have everything wired up on our client side. So, how can we test everything works fine on our client side before proceeding to the server side of things.&lt;/p&gt;

&lt;p&gt;Run your react app in your local. In your subscription page, provide the user permissions and get the registration token from localStorage or your database.&lt;/p&gt;

&lt;p&gt;Once you have the registration token we can then publish a message to test if the notification gets displayed when the app is in focus or in background.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check Registration Token Status
&lt;/h3&gt;

&lt;p&gt;Send a curl request to Google IID API with the registration token in the url and &lt;code&gt;apiKey&lt;/code&gt; (from your firebase project configuration) in the Authorization header.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'https://iid.googleapis.com/iid/info/&amp;lt;instance_token&amp;gt;?details=true'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Authorization: key=&amp;lt;apiKey&amp;gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You will get back details like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"connectDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2019-03-04"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"application"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.android.chrome"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"topics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"notifications"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"addDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2019-02-23"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"connectionType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"WIFI"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BROWSER"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Sample Notification Message
&lt;/h3&gt;

&lt;p&gt;We then send a message to test if the notification gets shown to the user. We had previously used the project &lt;code&gt;apiKey&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But, for sending the message we will use the FCM v1 HTTP protocol instead of the legacy protocol which will require JWT access token to be passed along the request.&lt;/p&gt;

&lt;p&gt;To generate the access token you can make use of &lt;a href="https://github.com/HarshadRanganathan/fcm-http-oauth"&gt;fcm-http-oauth&lt;/a&gt;. Follow the steps in the README to generate a service account in your firebase project.&lt;/p&gt;

&lt;p&gt;Let’s now send the message to a particular user by providing firebase project id, jwt and the registration token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  https://fcm.googleapis.com/v1/projects/&amp;lt;firebase_projectId&amp;gt;/messages:send &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Authorization: Bearer &amp;lt;jwt_token&amp;gt;'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "message":{
        "token": "&amp;lt;registration_token&amp;gt;",
        "notification": {
                "title": "New Appointments",
                "body": "14 February 2019 - 15:00\n18 February 2019 - 11:00\n18 February 2019 - 12:00"
                },
                "webpush": {
                    "headers": {
                        "TTL": "0"
                    },
                    "notification": {
                        "icon": "https://img.icons8.com/color/96/e74c3c/ireland.png"
                    },
                    "fcm_options": {
                        "link": "https://gnib-visa-app.rharshad.com"
                    }
                }
        }
    }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Refer:&lt;/p&gt;

&lt;p&gt;[1] &lt;a href="https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages"&gt;FCM Message Structure&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Server Side
&lt;/h2&gt;

&lt;p&gt;We had looked so far on how to subscribe a user to receive push notifications. We will now look at storing the registration tokens in firestore and sending notification messages using firebase SDK.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/03/registration-tokens-firestore.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8WSFpzbu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/03/registration-tokens-firestore.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Firestore
&lt;/h3&gt;

&lt;p&gt;Cloud Firestore is a flexible, scalable database for mobile, web, and server development from Firebase and Google Cloud Platform.&lt;/p&gt;

&lt;p&gt;It keeps your data in sync across client apps through realtime listeners and offers offline support for mobile and web so you can build responsive apps that work regardless of network latency or Internet connectivity.&lt;/p&gt;

&lt;h4&gt;
  
  
  Creating New Database
&lt;/h4&gt;

&lt;p&gt;Create a new firestore database in test mode inside your firebase project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/03/firestore-new-database.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vCu6uIDq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/03/firestore-new-database.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;Rules&lt;/code&gt; tab add below rule to allow reads/writes only from your account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.auth.uid != null;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Storing Registration Token
&lt;/h4&gt;

&lt;p&gt;You can create a node js project which will be sending notifications to your users/use cloud functions/any other framework. In this guide, we will see how we can store tokens in firestore using node.js.&lt;/p&gt;

&lt;p&gt;Generate a service account by following these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In your firebase project, under project settings, choose &lt;code&gt;Service accounts&lt;/code&gt; tab.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Select &lt;code&gt;Generate new private key&lt;/code&gt; to download your service account file.&lt;/p&gt;

&lt;p&gt;We will use the service account to access firestore.&lt;/p&gt;

&lt;p&gt;Install &lt;code&gt;firebase-admin&lt;/code&gt; SDK to your node.js project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save firebase-admin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We will define &lt;code&gt;firebase.js&lt;/code&gt; file which will perform the required operations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;serviceAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./service-account.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firebase-admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/* initialise app */&lt;/span&gt;
&lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;serviceAccount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="cm"&gt;/* initialise firestore */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firestore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firestore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;firestore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;timestampsInSnapshots&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FIRESTORE_TOKEN_COLLECTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;instance_tokens&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;storeAppInstanceToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;firestore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FIRESTORE_TOKEN_COLLECTION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firestore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FieldValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serverTimestamp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error storing token [&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] in firestore`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;deleteAppInstanceToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deleteQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;firestore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FIRESTORE_TOKEN_COLLECTION&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;==&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;querySnapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;deleteQuery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;querySnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error deleting token [&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] in firestore`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;storeAppInstanceToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;deleteAppInstanceToken&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This script exports two functions -&lt;/p&gt;

&lt;p&gt;[1] storeAppInstanceToken - You will pass in the token which needs to be stored in a firestore collection. Also, adds a server timestamp to the document.&lt;/p&gt;

&lt;p&gt;[2] deleteAppInstanceToken - Gets the docs which match the token and deletes them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sending User Notifications
&lt;/h3&gt;

&lt;p&gt;We update the script &lt;code&gt;firebase.js&lt;/code&gt; to export below functions to be able to send push notifications -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messaging&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messaging&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;buildCommonMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;notification&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
* Builds message with platform specific options
* Link: https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages
*/&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;buildPlatformMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fcmMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;buildCommonMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;webpush&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;headers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TTL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;notification&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://img.icons8.com/color/96/e74c3c/ireland.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fcm_options&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://gnib-visa-app.rharshad.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nx"&gt;fcmMessage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;fcmMessage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpush&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;webpush&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fcmMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;sendFcmMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fcmMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;    
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;messaging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fcmMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;buildPlatformMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;storeAppInstanceToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;deleteAppInstanceToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;subscribeAppInstanceToTopic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;unsubscribeAppInstanceFromTopic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;sendFcmMessage&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can use &lt;code&gt;buildPlatformMessage&lt;/code&gt; to generate a message and then pass it on to &lt;code&gt;sendFcmMessage&lt;/code&gt; to notify the user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Topic Subscription
&lt;/h3&gt;

&lt;p&gt;You can also subscribe/unsubscribe users to topics by calling &lt;code&gt;subscribeToTopic&lt;/code&gt; &amp;amp; &lt;code&gt;unsubscribeFromTopic&lt;/code&gt; methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;subscribeAppInstanceToTopic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;messaging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribeToTopic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error subscribing token [&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] to topic: `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;unsubscribeAppInstanceFromTopic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;messaging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unsubscribeFromTopic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error unsubscribing token [&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] from topic: `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We had used firebase SDK for sending FCM messages. You can also make use of webpush or send the messages to the FCM HTTP App server endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  Express Server
&lt;/h3&gt;

&lt;p&gt;So far, we had defined subscription, firebase and fcm actions.&lt;/p&gt;

&lt;p&gt;We will use &lt;a href="https://expressjs.com/"&gt;Express&lt;/a&gt; to expose them as API’s in a web server so that our client app can access them.&lt;/p&gt;

&lt;p&gt;Install express as a dependency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save express
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Create a new file named &lt;code&gt;index.js&lt;/code&gt; and define below API’s.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;storeAppInstanceToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deleteAppInstanceToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;subscribeAppInstanceToTopic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unsubscribeAppInstanceFromTopic&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./firebase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; 
    &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;bodyParser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body-parser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bodyParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/storetoken&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;storeAppInstanceToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/deletetoken&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;deleteAppInstanceToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/subscribe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;subscribeAppInstanceToTopic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/unsubscribe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;unsubscribeAppInstanceFromTopic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1338&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Server is running&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can run the server with below command and access the endpoints via localhost e.g. send a POST request to &lt;a href="http://localhost:1338/subscribe"&gt;http://localhost:1338/subscribe&lt;/a&gt; with appropriate JSON body content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node index.js
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There are various cloud platforms available where you can deploy the node express server.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/web/fundamentals/push-notifications/"&gt;https://developers.google.com/web/fundamentals/push-notifications/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/web/fundamentals/primers/service-workers/#update-a-service-worker"&gt;https://developers.google.com/web/fundamentals/primers/service-workers/#update-a-service-worker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle"&gt;https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/update"&gt;https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/update&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://serviceworke.rs/"&gt;https://serviceworke.rs/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/instance-id/reference/server#get_information_about_app_instances"&gt;https://developers.google.com/instance-id/reference/server#get_information_about_app_instances&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Notification"&gt;https://developer.mozilla.org/en-US/docs/Web/API/Notification&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://firebase.google.com/docs/cloud-messaging/concept-options#collapsible_and_non-collapsible_messages"&gt;https://firebase.google.com/docs/cloud-messaging/concept-options#collapsible_and_non-collapsible_messages&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages"&gt;https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://firebase.google.com/docs/cloud-messaging/js/client"&gt;https://firebase.google.com/docs/cloud-messaging/js/client&lt;/a&gt;&lt;/p&gt;

</description>
      <category>pushnotifications</category>
      <category>webpush</category>
      <category>fcm</category>
      <category>react</category>
    </item>
    <item>
      <title>Windows Subsytem for Linux (WSL) on Windows 10</title>
      <dc:creator>Harshad Ranganathan</dc:creator>
      <pubDate>Tue, 26 Mar 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/harshadranganathan/windows-subsytem-for-linux-wsl-on-windows-10-4fi</link>
      <guid>https://forem.com/harshadranganathan/windows-subsytem-for-linux-wsl-on-windows-10-4fi</guid>
      <description>&lt;p&gt;WSL was introduced with Windows 10 Fall Creators Update and later (Windows build 16215 or later).&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-requisites
&lt;/h2&gt;

&lt;p&gt;Ensure that “Windows Subsystem for Linux” feature is enabled by running below command in &lt;code&gt;PowerShell&lt;/code&gt; as an administrator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also enable it via &lt;code&gt;Turn Windows features on or off&lt;/code&gt; control panel option by checking &lt;code&gt;Windows Subsystem for Linux&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Linux Distribution
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open the Microsoft Store and choose your favorite Linux distribution.&lt;/li&gt;
&lt;li&gt;From the distro’s page, select “Get” to install the distribution.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/03/ubuntu-windows-store.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frharshad.com%2Fassets%2Fimg%2F2019%2F03%2Fubuntu-windows-store.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You might get below error if incase your corporate policies prevent install from Microsoft store.&lt;/p&gt;

&lt;p&gt;In that case, &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/install-manual" rel="noopener noreferrer"&gt;download&lt;/a&gt; the distros and manually install them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/03/ubuntu-windows-store-install-error.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frharshad.com%2Fassets%2Fimg%2F2019%2F03%2Fubuntu-windows-store-install-error.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Running the .appx file will still fail due to the corporate policy. To overcome the issue follow below steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rename the extension of the .appx file to .zip.&lt;/li&gt;
&lt;li&gt;Extract all the contents.&lt;/li&gt;
&lt;li&gt;Run the .exe file inside the extracted folder.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once the installation is complete, you will be asked to create a new user account with password.&lt;/p&gt;

&lt;p&gt;When you launch your distro, you won’t be asked for a password unless you elevate your access using &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upgrade Distro
&lt;/h2&gt;

&lt;p&gt;Windows does not automatically update or upgrade your Linux distro(s). On Debian/Ubuntu, you can use apt to upgrade the packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update &amp;amp;&amp;amp; sudo apt upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Launch Distro
&lt;/h2&gt;

&lt;p&gt;You can launch the distribution in multiple ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Launch from windows store&lt;/li&gt;
&lt;li&gt;Running wsl command from command prompt &lt;code&gt;wsl [command]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Running wsl.exe which allows you to manage your distributions, set default distribution and uninstalling distributions.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Re-install Distro
&lt;/h2&gt;

&lt;p&gt;You will get an error &lt;code&gt;0x80070003&lt;/code&gt; if you manually delete the ubuntu folder and start WSL. It occurs when the path where you had installed your distro is not present/deleted/removed. E.g. when you move your distro folder to your C: drive.&lt;/p&gt;

&lt;p&gt;In that case, you will have to unregister the distro and re-install it.&lt;/p&gt;

&lt;p&gt;List the distros available to WSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C:\Users\harshad&amp;gt;wslconfig.exe /l
Windows Subsystem for Linux Distributions:
Ubuntu-18.04 (Default)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unregister the distro with the distribution name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C:\Users\harshad&amp;gt;wslconfig.exe /u Ubuntu-18.04
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Re-install the distro again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change Mount Path
&lt;/h2&gt;

&lt;p&gt;WSL mounts your machine’s fixed drives under the &lt;code&gt;/mnt/&amp;lt;drive&amp;gt;&lt;/code&gt; folder in your Linux distros.&lt;/p&gt;

&lt;p&gt;To change the mount path to &lt;code&gt;/&lt;/code&gt; so that you don’t have to change directory to your drive everytime you run wsl, create a &lt;code&gt;wsl.conf&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo nano /etc/wsl.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste below contents and save it. On your next restart, the changes should get reflected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[automount]
root = /
options = "metadata"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install Docker
&lt;/h2&gt;

&lt;p&gt;To install docker and docker compose in WSL run below script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# Update package lists&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# Updating package lists"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-add-repository &lt;span class="nt"&gt;-y&lt;/span&gt; ppa:git-core/ppa
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update

&lt;span class="c"&gt;# Ensure that CA certificates are installed&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;apt-transport-https ca-certificates

&lt;span class="c"&gt;# Add Docker repository key to APT keychain&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://download.docker.com/linux/ubuntu/gpg | &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-key add -

&lt;span class="c"&gt;# Update where APT will search for Docker Packages&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb [arch=amd64] https://download.docker.com/linux/ubuntu &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CODENAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; stable"&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/docker.list

&lt;span class="c"&gt;# Update package lists&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update

&lt;span class="c"&gt;# Verifies APT is pulling from the correct Repository&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-cache policy docker-ce

&lt;span class="c"&gt;# Install Docker&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# Installing Docker"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;docker-ce

&lt;span class="c"&gt;# Add user account to the docker group&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;whoami&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Install docker compose&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# Installing Docker-Compose"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/docker/compose/releases/download/1.13.0/docker-compose-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/local/bin/docker-compose
&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; +x /usr/local/bin/docker-compose

&lt;span class="c"&gt;# Print installation details for user&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Installation completed, versions installed are:'&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'Docker: '&lt;/span&gt;
docker &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'Docker Compose: '&lt;/span&gt;
docker-compose &lt;span class="nt"&gt;--version&lt;/span&gt;

&lt;span class="c"&gt;# Print reminder of need to logout in order for these changes to take effect!&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please logout then login before continuing."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable &lt;code&gt;Expose daemon on tcp://localhost:2375 without TLS&lt;/code&gt; in your &lt;code&gt;Docker for Windows&lt;/code&gt; settings.&lt;/p&gt;

&lt;p&gt;We then configure WSL to connect to the remote docker daemon running in &lt;code&gt;Docker for Windows&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo "export DOCKER_HOST=tcp://localhost:2375" &amp;gt;&amp;gt; ~/.bashrc &amp;amp;&amp;amp; source ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then run docker commands in WSL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Git, Node &amp;amp; Python
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Exit on any failure&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# Update package lists&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# Updating package lists"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-add-repository &lt;span class="nt"&gt;-y&lt;/span&gt; ppa:git-core/ppa
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update

&lt;span class="c"&gt;# Install Git&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# Installing Git"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; git

&lt;span class="c"&gt;# Install nvm dependencies&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# Installing nvm dependencies"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;build-essential libssl-dev

&lt;span class="c"&gt;# Execute nvm installation script&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# Executing nvm installation script"&lt;/span&gt;
curl &lt;span class="nt"&gt;-o-&lt;/span&gt; https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash

&lt;span class="c"&gt;# Set up nvm environment without restarting the shell&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;NVM_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.nvm"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NVM_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/nvm.sh"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NVM_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/nvm.sh"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NVM_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/bash_completion"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NVM_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/bash_completion"&lt;/span&gt;

&lt;span class="c"&gt;# Install node&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# Installing nodeJS"&lt;/span&gt;
nvm &lt;span class="nb"&gt;install &lt;/span&gt;8
nvm use 8

&lt;span class="c"&gt;# Ensure that CA certificates are installed&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;apt-transport-https ca-certificates

&lt;span class="c"&gt;# Install python v2 if required&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; +e
&lt;span class="nv"&gt;COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;python &lt;span class="nt"&gt;-V&lt;/span&gt; 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; 2.&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COUNT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; 1]
&lt;span class="k"&gt;then
   &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; python-minimal
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Print installation details for user&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Installation completed, versions installed are:'&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'Node: '&lt;/span&gt;
node &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'npm: '&lt;/span&gt;
npm &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'Python: '&lt;/span&gt;
python &lt;span class="nt"&gt;-V&lt;/span&gt;

&lt;span class="c"&gt;# Print reminder of need to logout in order for these changes to take effect!&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please logout then login before continuing."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/windows/wsl/about" rel="noopener noreferrer"&gt;https://docs.microsoft.com/en-us/windows/wsl/about&lt;/a&gt;&lt;/p&gt;

</description>
      <category>wsl</category>
      <category>windowssubsystemfo</category>
      <category>runlinuxonwindows</category>
      <category>windows10</category>
    </item>
    <item>
      <title>Building and Publishing your Messenger Bot</title>
      <dc:creator>Harshad Ranganathan</dc:creator>
      <pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/harshadranganathan/building-and-publishing-your-messenger-bot-3fea</link>
      <guid>https://forem.com/harshadranganathan/building-and-publishing-your-messenger-bot-3fea</guid>
      <description>&lt;p&gt;Originally published at &lt;a href="https://rharshad.com/building-publishing-facebook-messenger-bot/"&gt;rharshad.com&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Building your Bot
&lt;/h1&gt;

&lt;p&gt;In this example, we will use Node.js to build our bot which will respond to user's messages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a new Node.js project
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;dbei-bot
&lt;span class="nb"&gt;cd &lt;/span&gt;dbei-bot

// create package.json file
npm init
&lt;span class="nb"&gt;touch &lt;/span&gt;index.js

// &lt;span class="nb"&gt;install &lt;/span&gt;project dependencies
npm &lt;span class="nb"&gt;install &lt;/span&gt;express body-parser &lt;span class="nt"&gt;--save&lt;/span&gt; // http server
npm &lt;span class="nb"&gt;install &lt;/span&gt;axios &lt;span class="nt"&gt;--save&lt;/span&gt; // promise based http client
npm &lt;span class="nb"&gt;install &lt;/span&gt;dotenv &lt;span class="nt"&gt;--save&lt;/span&gt; // loads environment variables from .env file
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure an Environment File
&lt;/h2&gt;

&lt;p&gt;We will create a &lt;code&gt;.env&lt;/code&gt; file which will have below entries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SEND_API=https://graph.facebook.com/v3.0/me/messages
VERIFY_TOKEN=&amp;lt;VERIFY_TOKEN&amp;gt;
PAGE_ACCESS_TOKEN=&amp;lt;PAGE_ACCESS_TOKEN&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;VERIFY_TOKEN&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;It can be a random string. We will configure this token in the messenger platform at a later stage. &lt;/p&gt;

&lt;p&gt;Purpose - Messenger platform will send a challenge request to our webhook and we must verify whether it matches the configured token. Only then, our webhook will be subscribed.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;PAGE_ACCESS_TOKEN&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We will generate this token at a later stage in the messenger platform and replace it here. &lt;/p&gt;

&lt;p&gt;Purpose - We need this token to send API requests to messenger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an HTTP server
&lt;/h2&gt;

&lt;p&gt;We will create an HTTP server that listens for requests. In the &lt;code&gt;index.js&lt;/code&gt; file which we had created earlier add below code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// configure dotenv so that process.env has the keys and values you defined in your .env file&lt;/span&gt;
&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; 
    &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;bodyParser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body-parser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bodyParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1337&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webhook is listening&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Add webhook verification endpoint
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;index.js&lt;/code&gt; file we add below code to expose &lt;code&gt;/webhook&lt;/code&gt; GET endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Webhook challenge endpoint
 */&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/webhook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;VERIFY_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VERIFY_TOKEN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hub.mode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hub.verify_token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;challenge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hub.challenge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Checks the mode and token sent is correct&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;subscribe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;VERIFY_TOKEN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We will configure this endpoint in the messenger platform later which will be used for challenge verification using the &lt;code&gt;VERIFY_TOKEN&lt;/code&gt; we provide.&lt;/p&gt;

&lt;p&gt;If the token sent by the messenger platform matches the one we had configured then we need to send &lt;code&gt;200&lt;/code&gt; response otherwise &lt;code&gt;403&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On successful challenge verification, messenger platform will subscribe our webhook.&lt;/p&gt;

&lt;p&gt;Test your webhook by running below commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;node index.js // starts the express HTTP server to serve at port 1337

curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="s2"&gt;"localhost:1337/webhook?hub.verify_token=&amp;lt;YOUR_VERIFY_TOKEN&amp;gt;&amp;amp;hub.challenge=CHALLENGE_ACCEPTED&amp;amp;hub.mode=subscribe"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Above should return HTTP status code 200 with response as &lt;code&gt;CHALLENGE_ACCEPTED&lt;/code&gt; if everything was set up properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add webhook endpoint
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;index.js&lt;/code&gt; file we add below code to expose &lt;code&gt;/webhook&lt;/code&gt; POST endpoint which will be used by the Messenger platform to send webhook events.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Messenger webhook endpoint
 */&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/webhook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Checks this is an event from a page subscription&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Iterates over each entry - there may be multiple if batched&lt;/span&gt;
        &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// entry.messaging is an array, but will only ever contain one message&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;webhook_event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messaging&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="c1"&gt;// A person is assigned a unique page-scoped ID (PSID) &lt;/span&gt;
            &lt;span class="c1"&gt;// for each Facebook Page they start a conversation with. &lt;/span&gt;
            &lt;span class="c1"&gt;// The PSID is used by your Messenger bot to identify a person when sending messages.&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sender_psid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;webhook_event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;messenger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sender_psid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;webhook_event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EVENT_RECEIVED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Handler functions
&lt;/h2&gt;

&lt;p&gt;Previously we had mentioned &lt;code&gt;messenger.handleMessage(sender_psid, webhook_event.message);&lt;/code&gt; function call. We will define this function now to handle and respond to text messages from uers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Incoming Message Handler
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sender_psid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;received_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// check if the input is text message&lt;/span&gt;
    &lt;span class="c1"&gt;// users might send smiley which can be filtered out&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;received_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;received_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;callSendAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sender_psid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;    
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We will define &lt;code&gt;callSendAPI&lt;/code&gt; to send response messages via the SEND API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// access environment variables&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PAGE_ACCESS_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAGE_ACCESS_TOKEN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SEND_API&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SEND_API&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Calls the Messenger API to send the message
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;callSendAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;psid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recipient&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;psid&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; 
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; 
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SEND_API&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PAGE_ACCESS_TOKEN&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PSID: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;psid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Status code: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Response: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Request: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here,&lt;/p&gt;

&lt;p&gt;&lt;code&gt;recipient&lt;/code&gt; - Sets the intended message recipient. In this case, we identify the person by their PSID.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;message&lt;/code&gt; - Sets the details of the message to be sent.&lt;/p&gt;

&lt;p&gt;We are sending our message to the Send API whose URL we had defined previously in a &lt;code&gt;.env&lt;/code&gt; file. We are also appending &lt;code&gt;PAGE_ACCESS_TOKEN&lt;/code&gt; in the access_token parameter of the URL query string. &lt;/p&gt;

&lt;p&gt;If in case you are sending a subscription message, you need to include &lt;code&gt;NON_PROMOTIONAL_SUBSCRIPTION&lt;/code&gt; tag in your data request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recipient&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;psid&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; 
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;messaging_type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MESSAGE_TAG&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tag&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NON_PROMOTIONAL_SUBSCRIPTION&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; 
&lt;span class="p"&gt;};&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Sending structured message
&lt;/h2&gt;

&lt;p&gt;Messenger Platform provides a set of useful message templates, each designed to support a different, common message structure, including lists, receipts, buttons, and more. &lt;/p&gt;

&lt;p&gt;Below is sample &lt;code&gt;templates.js&lt;/code&gt; helper script which can be used to send structured messages to the Send API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * link https://developers.facebook.com/docs/messenger-platform/reference/template/generic
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;genericTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;attachment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;template&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;payload&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;template_type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;generic&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * link https://developers.facebook.com/docs/messenger-platform/reference/template/list
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;listTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;attachment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;template&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;payload&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;template_type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;top_element_style&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;compact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;buttons&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Donate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;web_url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.paypal.me/harshadranganathan&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * link https://developers.facebook.com/docs/messenger-platform/send-messages/quick-replies/
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;quickRepliesTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quickReplies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;quick_replies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;quickReplies&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;genericTemplate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;genericTemplate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;listTemplate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;listTemplate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;quickRepliesTemplate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;quickRepliesTemplate&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Messenger Quick Replies Sample:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/01/messenger-quick-replies.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8u1z4kWq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/01/messenger-quick-replies.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, to make use of &lt;code&gt;quickRepliesTemplate&lt;/code&gt;, import the templates script and pass in the reply options referring the template documentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lodash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;templates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./templates&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;quickReplyOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;quickReplies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Subscribe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unsubscribe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;option&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;quickReplies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;content_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;option&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;option&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;templates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quickRepliesTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quickReplies&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;   

&lt;span class="nx"&gt;callSendAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sender_psid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quickReplyOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hi! How can I help you today?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Deploy your webhook
&lt;/h1&gt;

&lt;p&gt;Now that we have built our sample bot, we have to deploy it on a server with a valid SSL certificate so that it can accept requests over HTTPS. &lt;/p&gt;

&lt;p&gt;HTTPS connection is a must have for integrating our bot with Messenger platform.&lt;/p&gt;

&lt;p&gt;We have couple of options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If you need to integrate and test it from your local you can make use of &lt;a href="https://www.npmjs.com/package/ngrok"&gt;ngrok&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;You can remix messenger quick start project on &lt;a href="https://glitch.com/edit/#!/messenger-platform-quick-start?path=README.md:1:0"&gt;glitch&lt;/a&gt; which will provide a public URL served over HTTPS for your webhook.&lt;/li&gt;
&lt;li&gt;You can host the node.js application in your linux instance with &lt;a href="https://www.nginx.com/"&gt;Nginx&lt;/a&gt; as a proxy and configure SSL certificates with &lt;a href="https://letsencrypt.org/"&gt;Let's Encrypt&lt;/a&gt;. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once you have deployed your app and configured HTTPS connection, your webhook url will be &lt;code&gt;https://&amp;lt;server-ip&amp;gt;/webhook&lt;/code&gt; (or) &lt;code&gt;https://&amp;lt;domain&amp;gt;/webhook&lt;/code&gt; if you had mapped a domain to your server IP. &lt;/p&gt;

&lt;p&gt;Proceed to next steps for integrating it with Messenger platform.&lt;/p&gt;

&lt;h1&gt;
  
  
  Set Up Your Facebook App
&lt;/h1&gt;

&lt;p&gt;Follow below steps to set up your facebook app for use with the Messenger Platform and subscribe your webhook to receive events.&lt;/p&gt;

&lt;h2&gt;
  
  
  Facebook Page
&lt;/h2&gt;

&lt;p&gt;A Facebook Page will be used as the identity of your bot. When people chat with your app, they will see the Page name and the Page profile picture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.facebook.com/pages/creation/"&gt;Create a facebook page&lt;/a&gt; for your bot first.&lt;/p&gt;

&lt;p&gt;For our bot, we are going to create a business page with category as &lt;code&gt;Internet Company&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/01/create-fb-page.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WS-PEdQ9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/01/create-fb-page.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can then choose a profile picture and cover page.&lt;/p&gt;

&lt;p&gt;Additional page configurations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Create Page Username&lt;/code&gt; Create a custom url which people can use to search and visit your page.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Visitor Posts&lt;/code&gt; You can disable posts by other people on the page. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Page Info&lt;/code&gt; You can provide a description and email address for contact.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Notifications&lt;/code&gt; Turn off notifications for messages so that you don't receive an alert for every message sent to the bot via messenger. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Add a Button to Your Page&lt;/code&gt; You can add a contact button so that people can send a message from your page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Whitelist Domain &amp;amp; Subscription Messaging
&lt;/h3&gt;

&lt;p&gt;We will be integrating your webhook URL with messenger platform soon. In order for your webhook to be invoked by messenger you need to whitelist your domain. &lt;/p&gt;

&lt;p&gt;Add your domain in the &lt;code&gt;Whitelisted Domains&lt;/code&gt; section under &lt;code&gt;Messenger Platform&lt;/code&gt; settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/01/messaging-platform-features.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KL-eGhc8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/01/messaging-platform-features.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your bot needs the ability to send regular content to a person on Messenger you need to raise &lt;code&gt;Subscription Messaging&lt;/code&gt; permission at a page level. &lt;/p&gt;

&lt;p&gt;You will able to send messages to the person outside &lt;code&gt;Standard Messaging&lt;/code&gt; only after this permission is reviewed and approved.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.facebook.com/docs/messenger-platform/policy/policy-overview/"&gt;https://developers.facebook.com/docs/messenger-platform/policy/policy-overview/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Facebook Developer Account
&lt;/h2&gt;

&lt;p&gt;You need a developer account to create new apps which serve as an integration between your page and bot.&lt;/p&gt;

&lt;p&gt;Create a developer account through &lt;a href="https://developers.facebook.com/"&gt;Facebook for Developers&lt;/a&gt; platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Facebook App
&lt;/h2&gt;

&lt;p&gt;Facebook app contains the settings for your Messenger bot, including access tokens.&lt;/p&gt;

&lt;p&gt;Create a new app using the &lt;a href="https://developers.facebook.com/apps"&gt;app dashboard&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Your new app will be created in &lt;code&gt;Development Mode&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Under &lt;code&gt;Development Mode&lt;/code&gt; only users who have been granted Administrator, Developer, or Tester role in the app will be able to interact with the bot once we complete the integration with messenger.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Messenger Platform to your App
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In the sidebar of your app settings under 'PRODUCTS', click '+ Add Product'.&lt;/li&gt;
&lt;li&gt;Hover over 'Messenger' to display options.&lt;/li&gt;
&lt;li&gt;Click the 'Set Up' button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/01/messenger-product-fb-app.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RD97PwGG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/01/messenger-product-fb-app.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup Webhook
&lt;/h3&gt;

&lt;p&gt;In the 'Webhooks' section of the Messenger settings console, click the 'Setup Webhooks' button.&lt;/p&gt;

&lt;p&gt;Configure the Callback URL and Verify Token.&lt;/p&gt;

&lt;p&gt;Here, the Callback URL will be &lt;code&gt;https://&amp;lt;server-ip-or-domain&amp;gt;/webhook&lt;/code&gt; and Verify Token will the value which you had given in the &lt;code&gt;.env&lt;/code&gt; file previously.&lt;/p&gt;

&lt;p&gt;The Messenger Platform will send a GET request to your webhook with the verify token you provided. If your webhook is valid and properly set up to respond to the verification request, your webhook settings will be saved.&lt;/p&gt;

&lt;p&gt;Also, in the subscription field choose &lt;code&gt;messages&lt;/code&gt; and save the webhook config.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/01/setup-webhook.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9nExH6az--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/01/setup-webhook.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Subscribe your Facebook Page
&lt;/h3&gt;

&lt;p&gt;In the 'Webhook' section of the Messenger settings console, click the 'Select a Page' dropdown and select the facebook page which you had created earlier.&lt;/p&gt;

&lt;p&gt;This will subscribe your app to receive events when people on Messenger chat with your page and forward them to your webhook.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/01/webhooks-section.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v-Xx0jsZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/01/webhooks-section.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Generate Page Access Token
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;In the 'Token Generation' section of the Messenger settings console, click the 'Select a Page' dropdown and select the Facebook Page that you had created before.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy the token that appears in the 'Page Access Token' field. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add this token to property PAGE_ACCESS_TOKEN which we had defined in the &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The generated token will NOT be saved in this UI. Each time you select a Page from the dropdown, a new token will be generated. If a new token is generated, previously created tokens will continue to function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test App Subscription
&lt;/h2&gt;

&lt;p&gt;Send a message to your Page from facebook.com or in Messenger. If your webhook receives a webhook event then everything has been wired up properly.&lt;/p&gt;

&lt;h1&gt;
  
  
  Publishing your App
&lt;/h1&gt;

&lt;p&gt;While in Development mode, apps are automatically approved for all login permissions, features, and product-specific features.&lt;/p&gt;

&lt;p&gt;Once you switch your app to Live Mode, however, your app can only use permissions and features that it has been approved for.&lt;/p&gt;

&lt;p&gt;Messenger platform requires below review and approval steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Permissions and Features Review&lt;/li&gt;
&lt;li&gt;Business Information and Verification Documents&lt;/li&gt;
&lt;li&gt;Accept Supplemental Terms and Sign Tech Provider Agreement&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Permissions and Features Review
&lt;/h2&gt;

&lt;p&gt;Once you are ready to publish your bot, ensure your app is in &lt;code&gt;Development Mode&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Provide basic information on the app console under Settings &amp;gt; Basic:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;App Icon&lt;/li&gt;
&lt;li&gt;Privacy Policy URL&lt;/li&gt;
&lt;li&gt;Category&lt;/li&gt;
&lt;li&gt;Business Use&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can generate a privacy policy using &lt;a href="https://www.messenger.com/t/privacypolicybot"&gt;this bot&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Select app permissions
&lt;/h3&gt;

&lt;p&gt;Request the pages_messaging permissions under App review section in messenger settings, which allows your bot to send messages in live mode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/01/app-review-messenger.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8tj9fd5Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/01/app-review-messenger.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Submit your bot for review once you have chosen all the required permissions.&lt;/p&gt;

&lt;p&gt;Your bot will then be reviewed by messenger test users who will post the command which you had mentioned while submitting your app for review. Your bot is expected to respond to their messages within 20 seconds.&lt;/p&gt;

&lt;p&gt;If no response is returned within the stipulated time then your app submission will be sent back.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2019/01/messenger-test-users.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P5Cp7Nt6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rharshad.com/assets/img/2019/01/messenger-test-users.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gotchas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I had sent a message to my page and received a response back from my bot. However, when the test users had sent a message they aren't getting any response.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This could be a &lt;a href="https://developers.facebook.com/support/bugs/"&gt;platform bug&lt;/a&gt; as I had faced the same issue. When the test user had sent their first message the webhook wasn't called however when the same user sent the message the subsequent time everything worked fine. I had raised a bug report and it was fixed by messenger team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Business Information and Verification Documents
&lt;/h2&gt;

&lt;p&gt;If you use the messenger API then your business needs to be verified.&lt;/p&gt;

&lt;p&gt;You need to create a &lt;a href="https://business.facebook.com"&gt;Business manager account&lt;/a&gt; and link it to your app.&lt;/p&gt;

&lt;p&gt;Once your bot has been reviewed for responsiveness, functionality, and policy compliance, you will receive a message asking you to complete business verification in the 'Alerts' tab of the app console.&lt;/p&gt;

&lt;p&gt;You then need to provide the proofs for verification. If you are not an actual business, you can submit your home utility bill (must match your name in business manager account) as proof.&lt;/p&gt;

&lt;p&gt;Business verification is required once per Business Manager account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accept Supplemental Terms and Sign Tech Provider Agreement
&lt;/h2&gt;

&lt;p&gt;After your business information and verification documents are submitted, you will receive an email from the Facebook legal team asking you to digitally sign the supplemental terms and apply for review.&lt;/p&gt;

&lt;p&gt;Once all the review is completed, your bot will be approved. You can then make it live and anyone will be able to message and get the response back from your page.&lt;/p&gt;

&lt;h2&gt;
  
  
  References:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developers.facebook.com/docs/messenger-platform/getting-started/webhook-setup"&gt;https://developers.facebook.com/docs/messenger-platform/getting-started/webhook-setup&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.facebook.com/docs/messenger-platform/getting-started/quick-start"&gt;https://developers.facebook.com/docs/messenger-platform/getting-started/quick-start&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.facebook.com/docs/messenger-platform/getting-started/app-setup"&gt;https://developers.facebook.com/docs/messenger-platform/getting-started/app-setup&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.facebook.com/docs/messenger-platform/policy/policy-overview/"&gt;https://developers.facebook.com/docs/messenger-platform/policy/policy-overview/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.facebook.com/docs/apps/review/"&gt;https://developers.facebook.com/docs/apps/review/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.facebook.com/docs/messenger-platform/app-review/"&gt;https://developers.facebook.com/docs/messenger-platform/app-review/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>facebook</category>
      <category>messenger</category>
      <category>bot</category>
    </item>
    <item>
      <title>Setting up Kubernetes on Windows with Minikube</title>
      <dc:creator>Harshad Ranganathan</dc:creator>
      <pubDate>Sun, 25 Nov 2018 00:00:00 +0000</pubDate>
      <link>https://forem.com/harshadranganathan/setting-up-kubernetes-on-windows-with-minikube-2pp6</link>
      <guid>https://forem.com/harshadranganathan/setting-up-kubernetes-on-windows-with-minikube-2pp6</guid>
      <description>&lt;p&gt;Originally published at &lt;a href="https://rharshad.com/kubernetes-minikube-windows-setup/" rel="noopener noreferrer"&gt;rharshad.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/kubernetes/minikube" rel="noopener noreferrer"&gt;Minikube&lt;/a&gt; is a tool that makes it easy to run Kubernetes locally. Minikube runs a single-node Kubernetes cluster inside a VM on your laptop for users looking to try out Kubernetes or develop with it day-to-day.&lt;/p&gt;

&lt;p&gt;Minikube can be installed in multiple operating systems (Linux, MacOS &amp;amp; Windows) and supports multiple drivers (&lt;a href="https://www.virtualbox.org/" rel="noopener noreferrer"&gt;VirtualBox&lt;/a&gt;, &lt;a href="https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/about/" rel="noopener noreferrer"&gt;Hyper-V&lt;/a&gt; in case of Windows).&lt;/p&gt;

&lt;p&gt;We are going to look at Windows setup of Minikube using both VirtualBox &amp;amp; Hyper-V drivers.&lt;/p&gt;

&lt;p&gt;If you have &lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; in your local system then you would have enabled Hyper-V. In that case, it makes sense to make use of Hyper-V driver instead of VirtualBox as the latter doesn't work with Hyper-V enabled.&lt;/p&gt;

&lt;p&gt;So, in such a case if you choose VirtualBox as your driver you might end up toggling Hyper-V and restarting your system based on whether you want to use Docker or VirtualBox for Kubernetes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Chocolatey
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://chocolatey.org/" rel="noopener noreferrer"&gt;Chocolatey&lt;/a&gt; is a package manager for Windows similar to how homebrew is for MacOS. It simplifies our installation process so we will use it here.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;PowerShell&lt;/code&gt; as an administrator and run below commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check the execution policy and ensure that it is not restricted&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Get-ExecutionPolicy&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# If execution policy is restricted then set the policy to All-Signed&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Set-ExecutionPolicy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AllSigned&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Install chocolatey&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;New-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;System.Net.WebClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DownloadString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://chocolatey.org/install.ps1'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install Minikube
&lt;/h2&gt;

&lt;p&gt;Once chocolately is installed, we run below command to install Minikube&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;choco&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;minikube&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;0.27&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-y&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Versions above 0.27 have an issue with &lt;a href="https://github.com/kubernetes/minikube/issues/2914" rel="noopener noreferrer"&gt;shutdown&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Install VirtualBox
&lt;/h2&gt;

&lt;p&gt;If you're planning to use VirtualBox as the driver for Minikube then run below command. Otherwise skip this step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;choco&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;virtualbox&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-y&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll look at running Kubernetes cluster in both VirtualBox and Hyper-V next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running Kubernetes Cluster in VirtualBox
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Start Minikube
&lt;/h3&gt;

&lt;p&gt;Run this command in PowerShell as an administrator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;minikube&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--alsologtostderr&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Minikube will perform below steps&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Downloads MinikubeISO and places it in .minikube folder in your user directory&lt;/li&gt;
&lt;li&gt;Connects to VirtualBox and runs a &lt;code&gt;minikube&lt;/code&gt; virtual machine&lt;/li&gt;
&lt;li&gt;Downloads the necessary files and moves them to the cluster&lt;/li&gt;
&lt;li&gt;Runs a single-node Kubernetes cluster inside the VM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Jump to Interacting With Your Cluster section next.&lt;/p&gt;

&lt;h3&gt;
  
  
  Troubleshooting
&lt;/h3&gt;

&lt;p&gt;If you face any issues during the setup process perform below steps:&lt;/p&gt;

&lt;p&gt;Delete the minikube VM&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;minikube&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;delete&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Delete .minikube and .kube folders in your home directory&lt;/p&gt;

&lt;p&gt;Run minikube start command with appropriate options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running Kubernetes Cluster in Hyper-V
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create vSwitch
&lt;/h3&gt;

&lt;p&gt;First step is to create a vSwitch in Hyper-V.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;Hyper-V Manager&lt;/code&gt; in your windows system. On the right pane, select &lt;code&gt;Virtual Switch Manager&lt;/code&gt; option.&lt;/p&gt;

&lt;p&gt;Select &lt;code&gt;External&lt;/code&gt; and choose &lt;code&gt;Create Virtual Switch&lt;/code&gt; option in the &lt;code&gt;Virtual Switch Manager&lt;/code&gt; window.&lt;/p&gt;

&lt;p&gt;Give a name for the virtual switch e.g. Minikube with connection type as &lt;code&gt;External&lt;/code&gt; and below option enabled&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allow management operating system to share this network adapter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now select &lt;code&gt;Apply&lt;/code&gt; to create a new vSwitch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2018/11/hyper-v-manager-vswitch.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frharshad.com%2Fassets%2Fimg%2F2018%2F11%2Fhyper-v-manager-vswitch.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you see an error message &lt;code&gt;Failed while adding virtual Ethernet switch connections&lt;/code&gt; it means that you have enabled sharing on your wifi connection which needs to be disabled.&lt;/p&gt;

&lt;p&gt;Go to &lt;code&gt;Network and Sharing Center&lt;/code&gt; and select your Wi-Fi connection. In the &lt;code&gt;Wi-Fi&lt;/code&gt; status window select &lt;code&gt;Properties&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;Wi-Fi&lt;/code&gt; properties window under &lt;code&gt;Sharing&lt;/code&gt; tab uncheck this option&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allow other network users to connect through this computer's Internet connection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://rharshad.com/assets/img/2018/11/windows-wifi-properties.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frharshad.com%2Fassets%2Fimg%2F2018%2F11%2Fwindows-wifi-properties.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Start Minikube
&lt;/h3&gt;

&lt;p&gt;Run this command in PowerShell as an administrator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;minikube&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--vm-driver&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hyperv"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--hyperv-virtual-switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Minikube"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--disk-size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;10g&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--memory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;4096&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--alsologtostderr&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Minikube will perform below steps&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Downloads MinikubeISO and places it in .minikube folder in your user directory&lt;/li&gt;
&lt;li&gt;Connects to Hyper-V and runs a &lt;code&gt;minikube&lt;/code&gt; virtual machine&lt;/li&gt;
&lt;li&gt;Downloads the necessary files and moves them to the cluster&lt;/li&gt;
&lt;li&gt;Runs a single-node Kubernetes cluster inside the VM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You will see below logs if everything went fine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Connecting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cluster...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Setting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;up&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kubeconfig...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;I1125&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;23:05:12.192115&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;4588&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config.go:125&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;Using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;kubeconfig:&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="n"&gt;Starting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;components...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;I1125&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;23:05:12.196100&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;4588&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ssh_runner.go:80&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;output:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/usr/bin/kubeadm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/var/lib/kubeadm.yaml&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--ignore-preflight-errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;DirAvailable--data-minikube&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="n"&gt;FileAvailable--etc-kubernetes-manifests-kube-scheduler.yaml&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="n"&gt;fests-kube-apiserver.yaml&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--ignore-preflight-errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FileAvailable&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nt"&gt;--ignore-preflight-errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FileAvailable--etc-kubernetes-manifests-etcd.yaml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;flight-errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CRI&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/usr/bin/kubeadm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;alpha&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;phase&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;addon&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kube-dns&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Kubectl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;configured&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cluster.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Loading&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;file.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Troubleshooting
&lt;/h3&gt;

&lt;p&gt;If you face any issues during the setup process perform below steps:&lt;/p&gt;

&lt;p&gt;Delete the minikube VM&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;minikube&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;delete&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Delete .minikube and .kube folders in your home directory&lt;/p&gt;

&lt;p&gt;Run minikube start command with appropriate options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interacting With Your Cluster
&lt;/h2&gt;

&lt;p&gt;To check if minikube is running use below command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;minikube&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which outputs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\WINDOWS\system32&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;minikube&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;minikube:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Running&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;cluster:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Running&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;kubectl:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Correctly&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Configured:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pointing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;minikube-vm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;at&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To ensure all the cluster components are running we use below command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\WINDOWS\system32&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kubectl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pods&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kube-system&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;NAME&lt;/span&gt;&lt;span class="w"&gt;                                    &lt;/span&gt;&lt;span class="nx"&gt;READY&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;STATUS&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;RESTARTS&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;AGE&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;coredns-c4cffd6dc-565mc&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="nx"&gt;1/1&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;Running&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;12m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;etcd-minikube&lt;/span&gt;&lt;span class="w"&gt;                           &lt;/span&gt;&lt;span class="nx"&gt;1/1&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;Running&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;11m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;kube-addon-manager-minikube&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="nx"&gt;1/1&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;Running&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;11m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;kube-apiserver-minikube&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="nx"&gt;1/1&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;Running&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;11m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;kube-controller-manager-minikube&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;1/1&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;Running&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;12m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;kube-dns-86f4d74b45-vxrr9&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="nx"&gt;3/3&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;Running&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;12m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;kube-proxy-fn5c9&lt;/span&gt;&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;1/1&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;Running&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;12m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;kube-scheduler-minikube&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="nx"&gt;1/1&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;Running&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;11m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;kubernetes-dashboard-6f4cfc5d87-hgdnq&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;1/1&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;Running&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;5&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;12m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;storage-provisioner&lt;/span&gt;&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="nx"&gt;1/1&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;Running&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;12m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If any of the above pods are in failed status you can get it's logs as follows for debugging&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;kubectl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;logs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kubernetes-dashboard-6f4cfc5d87-hgdnq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kube-system&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you notice any of the pods in &lt;code&gt;CrashLoopBackOff&lt;/code&gt; status then perform the steps as mentioned in the troubleshooting guide earlier.&lt;/p&gt;

&lt;p&gt;To access the Kubernetes dashboard run below command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;minikube&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dashboard&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>kubernetes</category>
      <category>minikube</category>
      <category>windows</category>
    </item>
  </channel>
</rss>
