<?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>DEV Community: Adebola</title>
    <description>The latest articles on DEV Community by Adebola (@debosthefirst).</description>
    <link>https://dev.to/debosthefirst</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%2F321050%2F7a5fcc3e-3388-454b-8e00-eb53517b9c74.jpeg</url>
      <title>DEV Community: Adebola</title>
      <link>https://dev.to/debosthefirst</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/debosthefirst"/>
    <language>en</language>
    <item>
      <title>How to Pay Gas Fees for Users of Your dApp: Meta Transactions on Tezos</title>
      <dc:creator>Adebola</dc:creator>
      <pubDate>Wed, 03 Jan 2024 12:06:20 +0000</pubDate>
      <link>https://dev.to/debosthefirst/how-to-pay-gas-fees-for-users-of-your-dapp-meta-transactions-on-tezos-po5</link>
      <guid>https://dev.to/debosthefirst/how-to-pay-gas-fees-for-users-of-your-dapp-meta-transactions-on-tezos-po5</guid>
      <description>&lt;p&gt;This tutorial introduces Meta transactions on Tezos and covers how to use the Gas Station API to cover gas fees for dApp users.&lt;/p&gt;

&lt;p&gt;To onboard the next billion users on Web3, simplifying user onboarding and improving the user experience of dApps (decentralized applications) is extremely important. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VzbrRtFa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s2s1ojtvjkxn3rcg6hac.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VzbrRtFa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s2s1ojtvjkxn3rcg6hac.jpg" alt="Paying gas fees for your users" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For many users, the mindset shift from using Web2 applications to using Web3 dApps can be a steep slope. As such, dApp developers are always on the lookout for new ways to improve the UX for users in a way that abstracts away some of the complexities of using decentralized applications. &lt;/p&gt;

&lt;p&gt;One example of a major barrier to onboarding users to a dApp is the need to pay gas fees to interact with the dApp.&lt;br&gt;
 &lt;br&gt;
 &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a13jsd0J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cms.tzstaging.com/assets/decfe196-ee20-4784-889f-8bbc955ff0cd" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a13jsd0J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cms.tzstaging.com/assets/decfe196-ee20-4784-889f-8bbc955ff0cd" alt="gas fees flow on tezos" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Users of dApps built on Tezos will need to open a wallet and acquire XTZ(tez) to start using your dApp which can be a barrier to user onboarding. To get over this barrier, the idea of meta-transactions was developed. &lt;/p&gt;
&lt;h2&gt;
  
  
  What are Meta Transactions?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Meta transactions&lt;/strong&gt; allow dApp developers to improve the user experience of their applications by removing the need for users to hold tokens for paying gas fees. This is done by separating the transaction's sender (user) from the gas payer. The Gas Payer (usually another smart contract wallet) acts as the relayer for the transaction. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WFLWjr2D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cms.tzstaging.com/assets/c33baa6b-9909-4cc5-abda-e813c414720f" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WFLWjr2D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cms.tzstaging.com/assets/c33baa6b-9909-4cc5-abda-e813c414720f" alt="meta transactions on tezos" width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Let's look at why this is important;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In Web2 for example, an app like Instagram allows users to post pictures to their feed without the need to pay a fee each time they post. Users can also like posts, leave comments, send DMs, and more without the need to pay fees for each operation. Meta transactions allow for Web3 dApps to provide a similar experience to users making dApps more user-friendly and improving the overall experience. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There are 2 types of Meta transactions:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Gasless transactions&lt;/strong&gt;: Gasless or Zero-gas transactions allow users to interact with your dApp without the need to pay gas fees.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gas Abstraction&lt;/strong&gt;: Gas abstraction allows users of your dApp to pay gas fees using a token other than the native token of your application. Any token supported by the relayer can be used to cover the gas fees for the transaction. &lt;/p&gt;
&lt;h2&gt;
  
  
  TZIP-17 and Meta Transactions
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://medium.com/tqtezos/tzip-17-permit-497afd9b0e9e"&gt;TZIP-17&lt;/a&gt; (the permit proposal) introduced the standard for account abstraction. This standard allows pre-signing - a way to have transactions be signed and submitted separately. Read more &lt;a href="https://medium.com/tqtezos/tzip-17-permit-497afd9b0e9e"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TZIP-17&lt;/strong&gt; is the standard that allows meta transactions to work on Tezos. With this standard, we can have a relayer submit a user's pre-signed (meta) transaction and pay the tez fees on their behalf. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gas Stations&lt;/strong&gt;&lt;br&gt;
Gas stations are a good example of systems that can act as a relayer for transactions on a dApp. A Gas station - as the name implies, is simply a system that allows you to store tez (or any token supported by the station) to be used in paying for gas on behalf of your dApp users. &lt;/p&gt;

&lt;p&gt;In the next part of this tutorial, we'll build a simple dApp that interacts with a Gas Station. &lt;/p&gt;
&lt;h2&gt;
  
  
  Using the Gas Station API
&lt;/h2&gt;

&lt;p&gt;With everything we've covered in the previous sections, you should now be familiar with these concepts;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Relayer&lt;/li&gt;
&lt;li&gt;Gas Abstraction&lt;/li&gt;
&lt;li&gt;Gasless transactions&lt;/li&gt;
&lt;li&gt;Meta Transactions&lt;/li&gt;
&lt;li&gt;TZIP-17 proposal&lt;/li&gt;
&lt;li&gt;Gas stations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, we're ready to take all of this knowledge and build something in the wild. We'll be using the Gas Station API from Marigold to build a simple voting dApp that implements &lt;strong&gt;gasless transactions&lt;/strong&gt;. If you have never built a dApp on Tezos before, follow &lt;a href="https://spotlight.tezos.com/how-to-build-a-voting-dapp-on-tezos-part-1/"&gt;this&lt;/a&gt; tutorial to learn more. We'll be building on the &lt;a href="https://spotlight.tezos.com/how-to-build-a-voting-dapp-on-tezos-part-1/"&gt;voting dApp&lt;/a&gt; example in that tutorial here. &lt;/p&gt;

&lt;p&gt;The full code for the Voting dApp's front end can be found &lt;a href="https://github.com/onedebos/ballon-tz-or"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let's get started!&lt;/strong&gt;&lt;br&gt;
Head over to the Marigold &lt;a href="https://gas-station.gcp-npr.marigold.dev"&gt;Gas Station API&lt;/a&gt; environment and connect your wallet to the application using the Connect Wallet button in the top right. Connect your wallet on the Ghostnet network to the application.&lt;br&gt;
 &lt;br&gt;
Next, we'll need to &lt;strong&gt;add credits&lt;/strong&gt; to the gas station to be used to pay for transactions for users of our dApp. Click on &lt;strong&gt;My credits&lt;/strong&gt; in the menu. You should now be able to see your wallet balance. Enter the amount of tez you'd like to use within the gas station and click Add ꜩ to add the tez to your account. You can get test credits by using a faucet. &lt;/p&gt;

&lt;p&gt;Now, we need to make some changes to our &lt;a href="https://spotlight.tezos.com/how-to-build-a-voting-dapp-on-tezos-part-1/"&gt;Voting Smart Contract&lt;/a&gt; for it to still work correctly with our dApp and Gas station. We'll need to&lt;br&gt;
 &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Do away with &lt;code&gt;sp.sender&lt;/code&gt; because the transaction's &lt;code&gt;sender&lt;/code&gt; will always be the relayer (Gas Station API). As such, we'd need to get the actual &lt;code&gt;sender/user&lt;/code&gt; from the transaction parameters - more on this later. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Get the transaction's sender from &lt;code&gt;params&lt;/code&gt; instead. Make the following changes to the &lt;code&gt;increase_votes&lt;/code&gt; entrypoint.&lt;br&gt;
&lt;br&gt;
 &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@sp.entrypoint&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;increase_votes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;votersWalletAddresses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YouAlreadyVoted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PlayerIDNotFound&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
  &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;votes&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;votersWalletAddresses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We'll also need to change up our test scenarios to accommodate these changes. Your test scenarios should now look like the below;&lt;br&gt;
&lt;br&gt;
 &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Scenario 1: Increase votes when playerId Exists
&lt;/span&gt;    &lt;span class="n"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increase_votes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playerId&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="n"&gt;sender&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;votes&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Scenario 2: Increase votes when playerId Exists
&lt;/span&gt;    &lt;span class="n"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increase_votes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playerId&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="n"&gt;sender&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;charlie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;votes&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="c1"&gt;# Scenario 3: Fail if User already voted
&lt;/span&gt;    &lt;span class="n"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increase_votes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playerId&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="n"&gt;sender&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;charlie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YouAlreadyVoted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Scenario 4: Fail if playerID does not exist
&lt;/span&gt;    &lt;span class="n"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increase_votes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playerId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;adebola&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PlayerIDNotFound&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, go ahead to deploy your smart contract. &lt;/p&gt;

&lt;p&gt;See the full code for the updated smart contract &lt;a href="https://medium.com/r/?url=https%3A%2F%2Fsmartpy.io%2Fide%3Fcode%3DeJy1Vdtq3DAQffdXCPchNl1M4qR9CBgacqGhTSkNbSkhmFlLmwhkyUjaNKb03zuSZWe93m22kPpB2NLM0ZnbMa8bpS0xNWjbtAQMMU0UvTNNViu6FCyibEFq4DJJj6OI4NMIaJk2pW0bdkzcSgri7KG58QbuwW8u7Wz1W7NKaZoMW%406RUAdvYzWXd7PRactAbz99UJaZ7liCHY5S_3brV79UAowh35RFhASNT5W0GiqL8fQ%40Lsay5JLbskwME4tZH%40XMX6PNdxCC2RNKNTOGmRVfHx26ZBQsZMGto1WBsckAtJq3dIv7xssQbOP%40Fgy6rOsWfeI4igYTV1CGcbeNwrqMAuey0gwMK31C%40_BBQ70eJuaRYa9IZZ%40jnFWYZGwak3RAmWGSMp3OSPxDLU8EXkhbLAmj8aYbJvmc4HX7l9Qhfu7ezz4pe6GWcg1ygnWzBnGbdZ30uiAH_1KWDChdC2%40nfPv6%40DRv7SJvUvgCOiC8qESGNnHjUsRdK8epH02_H4BA8CqMk9suoaowHzbZ8wd7XdPN1XyTCW4Hg%40oetOAbccJRMATK5krAxgu7o2DoF1MxCZqrFfN%40K3H6ko7UBa1%40Dck5OF7Rjy4HH7mSTJArrASPZ14pijjfzw_jbmJNsZ8%40yUU%40BTjHQOQdeQ8gAFvmWYjDKcSHVnCQ5GoOTcOeRziaIlwDxYRcgdzB_c3U_VRzY5GCIl%40UBEHV31F%40r2hi0EBMs8t9FtRx0ConucwmN75xXP%405lr9N03ElcWJ6oK54r8h1f4Y1uwzCEpT65z2TpB86cv6I3M2ITLamRL1tkSMhP2AFdmnPJs30En9KI0bZA9N80SYD4mju837SC5z0dMI4_y%40Mw8i8DOt8yhr78gK4IHxBvqJ2ogZ4bfUR0Bch%404BNQIsLEIbNCHusWGNx%40IrNQj4lePREMNx4RqjCeNxvhLmc7sby7cAyiMtuLCc_hyBJ0R8zXc7I"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Connecting your Smart Contract to the Gas Station
&lt;/h2&gt;

&lt;p&gt;Back in the Gas Station UI, navigate to contracts in the menu. Copy and paste your smart contract address in, then click fetch entrypoints. Give your contract a name you can easily remember. &lt;/p&gt;

&lt;p&gt;Now, you can toggle on the entrypoints you'll like to use with the Gas Station API. For this example, we'll only use the &lt;code&gt;increase_votes&lt;/code&gt; entrypoint. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Co-GGYDB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cms.tzstaging.com/assets/9e1904ef-3d43-4923-b5f8-cc99d500dbfa" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Co-GGYDB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cms.tzstaging.com/assets/9e1904ef-3d43-4923-b5f8-cc99d500dbfa" alt="increase_votes entrypoint" width="800" height="733"&gt;&lt;/a&gt;&lt;br&gt;
Click &lt;strong&gt;Add&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Connecting the Frontend to the Gas Station
&lt;/h3&gt;

&lt;p&gt;With our Smart contract deployed, let's head to our application's Frontend to make some changes. The original code for this application is here. We'll be modifying the following files; src/pages/index.js and src/helpers/constants.js . We'll also be installing Axios which will allow us to connect to APIs/endpoints like the Gas station API that exists outside our application.&lt;/p&gt;

&lt;p&gt;Let's install axios. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm i axios&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Next, update the &lt;code&gt;src/helpers/constants.js&lt;/code&gt; file. We'll need to update the &lt;code&gt;CONTRACT_ADDRESS&lt;/code&gt; and &lt;code&gt;GAS_STATION_API_URL&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 &lt;/p&gt;
&lt;div class="highlight js-code-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;CONTRACT_ADDRESS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;KT1Aa2YS4RgQjDPDw7YQHpkXgtHvYwC7SYqo&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;GAS_STATION_API_URL&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://gas-station-api.gcp-npr.marigold.dev&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;p&gt;Don't forget to also export GAS_STATION_API_URL &lt;br&gt;
Next, head into &lt;code&gt;src/pages/index.js&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Import the GAS_STATION_URL with the rest of your constants like below;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;CONTRACT_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;RPC_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;GAS_STATION_API_URL&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&gt;@/helpers/constants&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;p&gt;We'll also need to keep tabs on the User's Wallet Address userAddress so we can send this alongside the rest of the transaction parameters to the API. To do that, we'll use React's useState hook. Your hooks section should now look like below with &lt;code&gt;const [userAddress, setUserAddress] = useState(null)&lt;/code&gt; added.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;players&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPlayers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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;reload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setReload&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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="kd"&gt;const&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="nx"&gt;setMessage&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUserAddress&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we'll update our &lt;code&gt;connectWallet&lt;/code&gt; function to store the userAddress state&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;connectWallet&lt;/span&gt; &lt;span class="o"&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="nf"&gt;setMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&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;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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ballon tz'or&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ghostnet&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;const&lt;/span&gt; &lt;span class="nx"&gt;wallet&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;BeaconWallet&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="nx"&gt;walletRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wallet&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;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestPermissions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;Tezos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;walletRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&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;userWalletAddress&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;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPKH&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setUserAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userWalletAddress&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&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="nf"&gt;setMessage&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll also create a button for the user to connect their wallet when the application loads. This button will run the connectWallet function when clicked. This allows us to make sure we have stored the userAddress before we attempt to send a transaction.&lt;br&gt;
&lt;br&gt;
 &lt;/p&gt;
&lt;div class="highlight js-code-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;button&lt;/span&gt;
        &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mt-4 rounded-full bg-green-500 p-3 hover:bg-green-700 transition-all ease-in-out&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;onClick&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="nf"&gt;connectWallet&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;Connect&lt;/span&gt; &lt;span class="nx"&gt;Wallet&lt;/span&gt;      
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Finally, we'll add a &lt;code&gt;callGasStation&lt;/code&gt; function to replace our &lt;code&gt;votePlayer&lt;/code&gt; function. i.e when a user clicks on Vote, our dApp will call &lt;code&gt;callGasStation&lt;/code&gt; instead of &lt;code&gt;votePlayer&lt;/code&gt; &lt;br&gt;
You'll notice that the &lt;code&gt;callGasStation&lt;/code&gt; function bypasses the need to have the user's wallet load up, show them the gas fees and have them approve and sign the transaction. Instead, all of that is handled by the gas station (relayer).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;callGasStation&lt;/span&gt; &lt;span class="o"&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;playerId&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;try&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;in call Gas station&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;contract&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;Tezos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CONTRACT_ADDRESS&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;op&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;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;methods&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increase_votes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userAddress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toTransferParams&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userAddress&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;response&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;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GAS_STATION_API_URL&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/operation&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;sender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;operations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parameter&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Gas Station 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;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;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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setMessage&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's walk through the code snippet above;&lt;/p&gt;

&lt;p&gt;We connect to the contract using Taquito as usual.&lt;br&gt;
&lt;br&gt;
 &lt;/p&gt;
&lt;div class="highlight js-code-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;contract&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;Tezos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CONTRACT_ADDRESS&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;op&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;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;methods&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increase_votes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userAddress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toTransferParams&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We tap into the &lt;code&gt;increase_votes&lt;/code&gt; entrypoint that has been updated to take 2 parameters of &lt;code&gt;playerId&lt;/code&gt; and &lt;code&gt;sender &lt;/code&gt;- which is the userAddress . Then, we call the &lt;code&gt;toTransferParams()&lt;/code&gt; method on it in order for us to access the operations object returned by the method. The operations object contains a number of objects including the &lt;code&gt;to&lt;/code&gt; and &lt;code&gt;parameter&lt;/code&gt; which we make use of when calling the Gas Station API.&lt;/p&gt;

&lt;p&gt;You can find out what the parameters expected by the Gas Station API are by reading the Swagger Docs/API documentation here. From the docs, we can see that the &lt;code&gt;/operation&lt;/code&gt; API expects a JSON object of sender and operation in the form below;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sender&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;string&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;operations&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, we pass &lt;code&gt;userAddress&lt;/code&gt; and the values obtained from op to the API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;response&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;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GAS_STATION_API_URL&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/operation&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;sender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;operations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parameter&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;See the full code &lt;a href="https://github.com/onedebos/ballon-tz-or/blob/main/src/pages/gasless.js"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, we can run our application to check that everything works correctly. &lt;/p&gt;

&lt;p&gt;We can also head to a smart contract explorer like BetterCallDev to check that our smart contract works correctly. For the smart contract used in this example, you can see use the &lt;a href="https://better-call.dev/ghostnet/KT1Aa2YS4RgQjDPDw7YQHpkXgtHvYwC7SYqo/storage"&gt;BCD explorer&lt;/a&gt; to see how it works.&lt;/p&gt;

</description>
      <category>tezos</category>
      <category>web3</category>
      <category>gas</category>
      <category>metatransactions</category>
    </item>
    <item>
      <title>Run Serverless Cron Jobs With Firebase Cloud Functions</title>
      <dc:creator>Adebola</dc:creator>
      <pubDate>Wed, 05 Jul 2023 01:44:29 +0000</pubDate>
      <link>https://dev.to/debosthefirst/run-serverless-cron-jobs-with-firebase-cloud-functions-26kb</link>
      <guid>https://dev.to/debosthefirst/run-serverless-cron-jobs-with-firebase-cloud-functions-26kb</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AfLXfXsZi6Mu5UDicyWWuhw.png%2520align%3D" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AfLXfXsZi6Mu5UDicyWWuhw.png%2520align%3D"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run a job every 2 minutes with Google Cloud Functions&lt;/p&gt;

&lt;p&gt;When a user’s subscription within your app expires, how do the systems you have built know to notify them? If you're building a fintech that is involved in lending, how do you send repayment reminders to users a few days to their repayment and on the repayment day? You may also need to backup your databases at the end of each business day, how do you spin up a service to do that? These and many more are why cron jobs matter. In this tutorial, rather than building out a service that runs cron jobs, we’ll hand all of the work over to a managed service like Google Cloud scheduler.&lt;/p&gt;

&lt;p&gt;Google Cloud scheduler combined with Firebase cloud functions allows us to write, deploy and run code that completes a task at any given time. In this tutorial, I’ll show you how we automate loan repayment reminders to our users using firebase functions and the google cloud scheduler.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;Set up a new Firebase project by following the &lt;strong&gt;Getting Started with Firebase&lt;/strong&gt; section in my article published &lt;a href="https://hackernoon.com/all-the-firebase-functions-you-need-to-build-your-next-serverless-application" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Once your firebase project is setup, you’ll need to upgrade from &lt;strong&gt;Spark&lt;/strong&gt; which is the free firebase plan to &lt;strong&gt;Blaze&lt;/strong&gt; — a paid plan. The scheduler function we’ll be using is only available on the paid plans of firebase. Follow the instructions &lt;a href="https://docs.firerun.io/getting-started/upgrading-from-the-firebase-spark-plan-to-the-blaze-plan-tled" rel="noopener noreferrer"&gt;here&lt;/a&gt; to upgrade your plan.&lt;/p&gt;

&lt;p&gt;Install firebase admin and firebase tools globally using the commands&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install firebase-functions@latest firebase-admin@latest — save
npm install -g firebase-tools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3. If you’re on a Mac or a UNIX based system, you may run into a permissions error during installation, prefix your command line commands with &lt;code&gt;sudo&lt;/code&gt; . With our libraries installed, let’s get into it!&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up your Functions
&lt;/h3&gt;

&lt;p&gt;Next, we need to log in to firebase from our command line to connect it to the project we created in Step 1. Run the command &lt;br&gt;&lt;br&gt;
&lt;code&gt;firebase login\&lt;/code&gt;. This will redirect you to your browser, prompting you to login via the UI. Login via the E-mail you connected to your firebase account to continue. Once in, return to the terminal&lt;/p&gt;

&lt;p&gt;Navigate into the directory where you’ll like to create your new functions project. Run the command,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;firebase init functions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Select the following options in the prompt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-&amp;gt; Use an existing project
-&amp;gt; Select the project you created in Step 1
-&amp;gt; Select JavaScript
-&amp;gt; When prompted to use ESlint, you can select Yes/N. For this example we'll go with N
-&amp;gt; Select Y to install dependencies
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By completing the steps above, you should now see a functions folder created for you in your directory. Let’s run some tests to check that everything works. We’ll build a quick app that tells us the time in the US, UK, and Nigeria.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Your Setup using Luxon
&lt;/h3&gt;

&lt;p&gt;Navigate into your functions folder and install the luxon package — a recommended alternative to moment. We’ll be using this package&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i luxon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In functions &amp;gt; index.js, write the following piece of code. I’ve added comments for clarity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;onRequest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase-functions/v2/https&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;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase-functions/logger&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// for logging to the terminal&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;DateTime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;luxon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// get the DateTime class from Luxon&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeInDiffPlaces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;onRequest&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;const&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timeInLondon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setZone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Europe/London&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLocaleString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATETIME_FULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timeInNewYork&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setZone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;America/New_York&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLocaleString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATETIME_FULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timeInLagos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setZone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Africa/Lagos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLocaleString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATETIME_FULL&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="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;success&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;9. Test out your code by running the command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;firebase emulators:start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create endpoints for each function in your index.js file within the functions folder. The endpoint will be displayed in your terminal window e.g &lt;a href="http://127.0.0.1:5001/adebola-reminder/us-central1/timeInDiffPlaces" rel="noopener noreferrer"&gt;&lt;strong&gt;http://127.0.0.1:5001/adebola-reminder/us-central1/timeInDiffPlaces&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, when you go to the endpoint (in your browser) that is displayed in the terminal, you should see a page that simply displays &lt;strong&gt;success&lt;/strong&gt; as we have specified in our &lt;code&gt;res.send&lt;/code&gt;. But take a look in your terminal window, the &lt;strong&gt;logger&lt;/strong&gt; library will log out the time in New York, London, and Lagos in your terminal like below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2Ahyw3ViS85QmltR89ZNB4yg.png%2520align%3D" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2Ahyw3ViS85QmltR89ZNB4yg.png%2520align%3D"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Scheduling E-mails to Users
&lt;/h3&gt;

&lt;p&gt;Now that we have everything working fine, let’s get started writing a function to send E-mails to our users. Later, we’ll set up a cloud scheduler to trigger the sending of these emails.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up Nodemailer and Gmail
&lt;/h3&gt;

&lt;p&gt;We’ll be using &lt;strong&gt;nodemailer&lt;/strong&gt; and &lt;strong&gt;Gmail&lt;/strong&gt; to send our E-mails. Follow my article &lt;a href="https://blog.logrocket.com/send-emails-nodejs-nodemailer/" rel="noopener noreferrer"&gt;here&lt;/a&gt; on setting up both.&lt;/p&gt;

&lt;p&gt;Since that article was written, Google has updated how apps can use your E-mail. Google now requires 2FA to be setup to connect it to Nodemailer. Setup 2FA on your E-mail by going &lt;a href="https://myaccount.google.com/u/4/security?pageId=none" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Once done, we’ll also need to set up an &lt;strong&gt;app password&lt;/strong&gt; to login to our gmail account remotely.&lt;/p&gt;

&lt;p&gt;Learn more about app passwords &lt;a href="https://support.google.com/accounts/answer/185833?visit_id=638238208014451629-1590373095&amp;amp;p=InvalidSecondFactor&amp;amp;rd=1" rel="noopener noreferrer"&gt;here&lt;/a&gt;. To setup App password,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go &lt;a href="https://myaccount.google.com/u/4/security?pageId=none" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Make sure you’re logged in to your Gmail account.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Under &lt;strong&gt;How you sign in with Google&lt;/strong&gt;, select 2-step verification.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You’ll be prompted to sign in to your E-mail again. Sign in&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scroll down to App passwords. Click App passwords&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter a name for your app. Then, click Generate. A modal will pop up with your app password.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The final code is below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;hbs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nodemailer-express-handlebars&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;nodemailer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nodemailer&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;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// initialize nodemailer&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;transporter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nodemailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createTransport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gmail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;youremail@gmail.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your_app_password&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="c1"&gt;// // point to the template folder&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handlebarOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;viewEngine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;partialsDir&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="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./views/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;defaultLayout&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="na"&gt;viewPath&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="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./views/&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;// // use a template file with nodemailer&lt;/span&gt;
&lt;span class="nx"&gt;transporter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;compile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;hbs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handlebarOptions&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendEmailToUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;onRequest&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="c1"&gt;// setup the E-mail options&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mailOptions&lt;/span&gt; &lt;span class="o"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;"Happpiness App" &amp;lt;youremail@gmail.com&amp;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;// sender address&lt;/span&gt;
    &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your_test_email@gmail.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// list of receivers&lt;/span&gt;
    &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// the name of the template file i.e email.handlebars&lt;/span&gt;
    &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Adebola&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// replace {{name}} with Adebola&lt;/span&gt;
      &lt;span class="na"&gt;company&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My Company&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// replace {{company}} with My Company&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// trigger the sending of the E-mail&lt;/span&gt;
  &lt;span class="nx"&gt;transporter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mailOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &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;info&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="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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 sent: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;info&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;success&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;Again, test that everything works by hitting the URL in your terminal window followed by &lt;code&gt;/sendEmailToUsers&lt;/code&gt; . Later on, we’ll extract this code into a function of its own rather than having it directly inside the serverless function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Now for the much-awaited Scheduler
&lt;/h3&gt;

&lt;p&gt;Now that we can trigger E-mails to users, we can setup a scheduler in Google cloud to trigger our function every so often. For this use case, we want to send reminders to users to repay their loan when 3 days to their repayment dates. I’ll skip the code for figuring out when a user’s repayment falls on and go ahead to show you how to set up your scheduler to call the &lt;strong&gt;sendEmailToUsers&lt;/strong&gt; serverless function.&lt;/p&gt;

&lt;p&gt;First, extract your E-mail sending logic into a new async function, like below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;sendEmailToUsers&lt;/span&gt; &lt;span class="o"&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;emails&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="c1"&gt;// past the code from Setting up Nodemailer and Gmail section above&lt;/span&gt;
&lt;span class="c1"&gt;// remember that E-mails would contain an array of object so adjust your code&lt;/span&gt;
&lt;span class="c1"&gt;// accordingly&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, setup the logic you need to get the names, emails and other details of the users you’ll like to E-mail. Remember to adjust the template in your &lt;strong&gt;views&lt;/strong&gt; folder from the &lt;strong&gt;setting up nodemailer and gmail&lt;/strong&gt; section to reflect your use case.&lt;/p&gt;

&lt;p&gt;We’ll also need to import the &lt;strong&gt;onSchedule&lt;/strong&gt; function from firebase into our code. Like below;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;onSchedule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase-functions/v2/scheduler&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;p&gt;Do this at the top of the index.js file where you have your other imports.&lt;/p&gt;

&lt;p&gt;Finally, write the scheduler function using the &lt;strong&gt;App Engine syntax&lt;/strong&gt; to trigger the &lt;strong&gt;sendEmailForRepayment&lt;/strong&gt; function &lt;strong&gt;every 2 minutes&lt;/strong&gt; to make sure everything works correctly. The App engine syntax reads like plain English e.g &lt;code&gt;every 2 minutes&lt;/code&gt; compared to the crontab syntax which looks like &lt;br&gt;&lt;br&gt;
&lt;code&gt;*/2 * * * *&lt;/code&gt; representing every 1 minute. You can play around with the crontab syntax &lt;a href="https://crontab.guru/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. The crontab guru tool will help you generate the appropriate crontab syntax for your scheduler.&lt;/p&gt;

&lt;p&gt;You may also use the crontab syntax which most people are familiar with.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendEmailsForRepayment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;onSchedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;every 2 minutes&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;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="c1"&gt;// get users whose repayments are due&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getUsersWithDueRepayments&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;emailsWithRepaymentDetails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getRepaymentDetailsForUsers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendEmailToUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emailsWithRepaymentDetails&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Schedule done 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;h3&gt;
  
  
  Deploying your Scheduled Cloud functions
&lt;/h3&gt;

&lt;p&gt;Make sure to run your code using the firebase emulator to test that everything works on your local before deploying. Deploy your code using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;firebase deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your scheduled cloud functions should now be live!&lt;/p&gt;

&lt;p&gt;Your schedule function should run every minute as specified in the &lt;strong&gt;onSchedule&lt;/strong&gt; callback.&lt;/p&gt;

&lt;h3&gt;
  
  
  Logging
&lt;/h3&gt;

&lt;p&gt;To monitor the performance of your serverless functions, head over to your &lt;a href="https://console.cloud.google.com/" rel="noopener noreferrer"&gt;google cloud console&lt;/a&gt;. Make sure you’re in the right firebase project by selecting your project name from the dropdown.&lt;/p&gt;

&lt;p&gt;Use the searchbar to search for the name of your function, in our case &lt;code&gt;sendEmailsForRepayment&lt;/code&gt; . From here you’ll be able to see all the information on how that function is performing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pub/Sub vs HTTPs
&lt;/h3&gt;

&lt;p&gt;In this example, we connected to our serverless functions via HTTP requests. In my next article, we’ll talk about another way to communicate with serverless functions using Pub/sub or publisher and subscriber. Pub/subs are event-driven.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>firebase</category>
      <category>firebasefunctions</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>How to Stream Data From Firebase to BigQuery Easily</title>
      <dc:creator>Adebola</dc:creator>
      <pubDate>Sat, 24 Jun 2023 23:39:56 +0000</pubDate>
      <link>https://dev.to/debosthefirst/how-to-stream-data-from-firebase-to-bigquery-easily-287d</link>
      <guid>https://dev.to/debosthefirst/how-to-stream-data-from-firebase-to-bigquery-easily-287d</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3tG60gpG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2ACkgZziaRS2AHRJI_nOqqEQ.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3tG60gpG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2ACkgZziaRS2AHRJI_nOqqEQ.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You must have installed the Stream Firestore to Big Query &lt;a href="https://extensions.dev/extensions/firebase/firestore-bigquery-export"&gt;Extension&lt;/a&gt; and maybe even read some of the docs but you still can’t quite figure out how to get everything to work. This guide will help you with that.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Have data in Firebase Firestore and need to bring them into Big Query? That’s a little straightforward. You simply export the data into a bucket and pull the data into your Big query console. There are a few articles online that explain this well, particularly&lt;/em&gt; &lt;a href="https://blog.coupler.io/firestore-to-bigquery/#How_to_automate_Firestore_to_BigQuery_export"&gt;&lt;em&gt;this&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But&lt;/strong&gt;, when it comes to streaming data in realtime from Firestore to BQ, I couldn’t find any good articles there that could get me from start to finish without errors — so, I wrote one.&lt;/p&gt;

&lt;p&gt;This guide assumes you have an existing Firestore instance setup and one or more collections in your Firestore DB. Next, within your Firebase console, navigate to the &lt;strong&gt;extensions&lt;/strong&gt; screen. Search for and install the &lt;strong&gt;Stream Firestore to BigQuery&lt;/strong&gt; extension.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mKYtlenx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AEsZdu2EIf61r7PxkzQM3BA.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mKYtlenx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AEsZdu2EIf61r7PxkzQM3BA.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once in, set up the extension by following the prompts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PldiWTRE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2A8qUgO25ePCiBKT5ZHhfDVQ.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PldiWTRE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2A8qUgO25ePCiBKT5ZHhfDVQ.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NB: You’ll only be able to use the extension if your workspace or account has a Credit card on file.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Keep hitting &lt;strong&gt;next&lt;/strong&gt; till you get to Step 4: Configure extension.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XIfQEhVa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2APUN15pL1juMTHmol1Qxs3A.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XIfQEhVa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2APUN15pL1juMTHmol1Qxs3A.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set your Project Id, Collection Path, Dataset ID and Table ID. Project ID should be the same as whatever you have named your firebase project. Collection path refers to the primary collection you would like to stream to BQ. In our case, we want the &lt;strong&gt;users&lt;/strong&gt; collection. Dataset ID and Table ID can be named as you wish. Keep all other configurations as default and hit the &lt;strong&gt;install&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;The extension will take around 3–5mins to install.&lt;/p&gt;

&lt;p&gt;At this point, there’s no new data in your BQ console just yet.&lt;/p&gt;

&lt;p&gt;While waiting on the extension’s installation to complete, you can begin the process of setting up &lt;strong&gt;gcloud CLI&lt;/strong&gt;. You’ll need to set up the CLI in order to authenticate when running the import script that will take your data from Firestore to BQ.&lt;/p&gt;

&lt;p&gt;Follow the instructions &lt;a href="https://cloud.google.com/sdk/docs/install"&gt;here&lt;/a&gt; to set up the CLI for your device. Don’t forget to make sure the CLI is in your &lt;code&gt;PATH&lt;/code&gt; by running (on a Mac) — in your HOME directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./google-cloud-sdk/install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart your terminal instance before continuing.&lt;/p&gt;

&lt;p&gt;With the CLI setup. Run the command below in your terminal window to authenticate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud auth application-default login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command will bring you to the Google sign-in screen. Authenticate using the E-mail that has access to the Firestore project you’re working with.&lt;/p&gt;

&lt;p&gt;You’ll see a screen like below if everything goes fine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G-XAbqGu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2A4jORDa-ItBYBSoH3Ukqt5Q.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G-XAbqGu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2A4jORDa-ItBYBSoH3Ukqt5Q.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your terminal should also have a message like below&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D4NZZxpY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AhDM-zT1w-krfIP9OfP7czA.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D4NZZxpY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AhDM-zT1w-krfIP9OfP7czA.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Google has an import script that will allow you to automatically bring all the data in your Firestore collection into BigQuery by running one command. This script works alongside the &lt;strong&gt;Stream Firestore to BigQuery&lt;/strong&gt; extension. Run the command below in your terminal to trigger the script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx @firebaseextensions/fs-bq-import-collection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;P.S The command above will only work if you have Node/NPM installed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WUJJxMA_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AqPmWLwzD0BbademAlcI1HQ.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WUJJxMA_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AqPmWLwzD0BbademAlcI1HQ.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow the prompts starting with entering your &lt;strong&gt;project ID&lt;/strong&gt;. You only need to enter the custom information we set earlier — project ID, Dataset ID, Table prefix and Collection Path. See screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TcmyFSsC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AdUsudyOmHVkaoqtpaysZaw.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TcmyFSsC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AdUsudyOmHVkaoqtpaysZaw.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The script will create a &lt;code&gt;raw_changelog&lt;/code&gt; file in the format &lt;code&gt;table_name_raw_changelog&lt;/code&gt;. Once you navigate to your &lt;a href="https://console.cloud.google.com/bigquery"&gt;BigQuery console&lt;/a&gt;, you would be able to see this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bW2hnBiv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AxmI6Z5i65ePgjZqGqReLkA.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bW2hnBiv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AxmI6Z5i65ePgjZqGqReLkA.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also run queries on your data using the &lt;strong&gt;Compose a new query&lt;/strong&gt; button&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KY_zpBG3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2A3C3kFYpD9Inch0yyYx-y3A.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KY_zpBG3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2A3C3kFYpD9Inch0yyYx-y3A.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, you can add a new &lt;strong&gt;document&lt;/strong&gt; to the &lt;strong&gt;users&lt;/strong&gt; collection, refresh your table within BigQuery and observe that the new document reflects in your BigQuery query!&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>bigquery</category>
      <category>googlecloud</category>
      <category>firestore</category>
    </item>
    <item>
      <title>How to Use a Local SMS Service Provider Other than Twilio With Customer.io</title>
      <dc:creator>Adebola</dc:creator>
      <pubDate>Sat, 24 Jun 2023 23:35:23 +0000</pubDate>
      <link>https://dev.to/debosthefirst/how-to-use-a-local-sms-service-provider-other-than-twilio-with-customerio-bdd</link>
      <guid>https://dev.to/debosthefirst/how-to-use-a-local-sms-service-provider-other-than-twilio-with-customerio-bdd</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DTMPjaLC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2Ars_Nxid4ptotRXa6dtCmig.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DTMPjaLC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2Ars_Nxid4ptotRXa6dtCmig.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re running a Business in Africa, you know that every cent counts.&lt;/p&gt;

&lt;p&gt;While I love &lt;a href="http://twilio.com"&gt;Twilio&lt;/a&gt;, and I’m always impressed by their delivery rates, the truth is that at &lt;a href="https://www.twilio.com/sms/pricing/ng"&gt;$0.1982&lt;/a&gt; per SMS, it’s expensive to use for early-stage startups whose revenues aren’t based in USD.&lt;/p&gt;

&lt;p&gt;I’m a big fan of &lt;a href="http://customer.io"&gt;customer.io&lt;/a&gt; and the power it packs and as such it’s something we use a lot at &lt;a href="https://moni.africa"&gt;Moni&lt;/a&gt; for automating our Marketing campaigns and running in-app and push notifications. I still think that African Startups pay too much for tools — especially startups whose revenues aren’t based in USD, but this is a topic for another day.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://Customer.io"&gt;Customer.io&lt;/a&gt; has a direct integration with Twilio and it’s easy to set up that integration. If that’s something you’re interested in, follow the &lt;a href="http://customer.io"&gt;customer.io&lt;/a&gt; docs &lt;a href="https://customer.io/docs/twilio/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll be using a local Nigerian SMS Service provider &lt;a href="https://sleengshort.com/"&gt;Sleengshort&lt;/a&gt; to send SMS’ to users on our platform. You can follow along with this tutorial without a Sleengshort account. Simply make sure that you have the following;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;A registered sender ID with your SMS provider&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An API Gateway you can connect to, to trigger the SMS&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A &lt;a href="http://customer.io"&gt;customer.io&lt;/a&gt; account and basic knowledge of &lt;a href="http://customer.io"&gt;customer.io&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s get into it.&lt;/p&gt;

&lt;p&gt;First, log in to your Sleengshort account to get your Sender ID and API Keys. You can find both from your Sleengshort Dashboard by going to Settings &amp;gt; Sender ID and Settings &amp;gt; API Keys.&lt;/p&gt;

&lt;p&gt;In the Settings &amp;gt; API Keys section, create a new API Key by clicking &lt;strong&gt;Add new Key&lt;/strong&gt;. Give it a name e.g &lt;a href="http://customer.io"&gt;customer.io&lt;/a&gt;. Feel free to leave the callback URL and Route/bind options blank. To learn more about both, you can refer to the sleengshort docs &lt;a href="https://sleengshort.gitbook.io/sleengshort-sms-api-documentation/"&gt;here&lt;/a&gt;. Once your API keys and sender ID are set up, head over to &lt;a href="http://customer.io"&gt;customer.io&lt;/a&gt;. We’ll be using the &lt;a href="http://customer.io"&gt;customer.io&lt;/a&gt; Webhooks feature for this integration. To learn more, &lt;a href="http://customer.io"&gt;customer.io&lt;/a&gt; has some great documentation &lt;a href="https://customer.io/docs/webhooks-action/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Within &lt;a href="http://Customer.io"&gt;Customer.io&lt;/a&gt;, head to Campaigns &amp;gt; Create Campaign &amp;gt; Give the Campaign a name. I believe these steps would also work for a broadcast. Setup your Campaign with your triggers &amp;gt; Settings &amp;gt; Goal &amp;amp; Exit. The real magic begins in the &lt;strong&gt;workflow&lt;/strong&gt; section. You should end up on a screen like below&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UlKY7_Cl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AyyIV62vhJS2mOTUc8VsyNg.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UlKY7_Cl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AyyIV62vhJS2mOTUc8VsyNg.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click, hold, and drag the &lt;strong&gt;send and receive data&lt;/strong&gt; block on the left into the large &lt;strong&gt;Drag your first block here&lt;/strong&gt; section. Your screen should look like below;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XWy3Gf1S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AfybKPIakyVIsDkDcce8OqQ.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XWy3Gf1S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AfybKPIakyVIsDkDcce8OqQ.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now make changes to the &lt;strong&gt;Webhook 1&lt;/strong&gt; section by clicking it. Click &lt;strong&gt;Add request&lt;/strong&gt; in the slider that pops up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U1c92BDc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2ALaGPTez1BggUk6fXqEb9sQ.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U1c92BDc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2ALaGPTez1BggUk6fXqEb9sQ.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Add request&lt;/strong&gt; button will bring you to the screen below. The screenshot below already contains the URL to the Sleengshort APIs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WBhZcmUU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2A52GJgIP_vvBU1QSVCEa5Fw.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WBhZcmUU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2A52GJgIP_vvBU1QSVCEa5Fw.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You also need to add the &lt;strong&gt;headers&lt;/strong&gt;. The headers will contain the authorization keys Sleengshort’s APIs expect to receive. Click the &lt;strong&gt;Add header&lt;/strong&gt; button and add the following fields.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Name&lt;/strong&gt; -&amp;gt; X-Api-Key&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Value&lt;/strong&gt; -&amp;gt; your_api_key_from_sleensghort_starting_with_APPKEY&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---_wKQGd7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AmJtLRLsH60BG7yApYYhPgA.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---_wKQGd7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AmJtLRLsH60BG7yApYYhPgA.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sleengshort and &lt;a href="http://Customer.io"&gt;Customer.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once done, use the fields above in the editor. I’ve also repeated them below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
"sender_id" : "Your Sender ID",
"recipients" : {{customer.phone_number}},
"msg" : "Hello {{customer["First Name"]}}, welcome to our Company",
"type": 0
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure that each key and value is wrapped in a double quote &lt;code&gt;" "&lt;/code&gt; to avoid errors in your messages sent. Also, use the &lt;strong&gt;+&lt;/strong&gt; button next to each field that your users in your segment already have to add the recipient’s &lt;strong&gt;phone number&lt;/strong&gt; and to add details like the customer’s name and other custom fields to the &lt;strong&gt;“msg”&lt;/strong&gt; field. Click the &lt;strong&gt;send test&lt;/strong&gt; button at the top to test that everything works fine. Make sure the test message is being delivered to a phone number you have access to.&lt;/p&gt;

&lt;p&gt;If you get anything other than a &lt;strong&gt;200&lt;/strong&gt; and &lt;strong&gt;ok&lt;/strong&gt; response, you’ll need to make sure that you have setup everything correctly as show from the beginning of this tutorial.&lt;/p&gt;

&lt;p&gt;Finally, click done and review your workflow before starting the campaign. Engineering teams can easily set up this workflow for their marketing teams. In the future, if the marketing team needs to start another campaign, they can simply duplicate the webhook settings by clicking the &lt;strong&gt;copy from workflow&lt;/strong&gt; button and searching for the previous campaign that contains the webhooks setting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ckFGdl1s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AWjs8z-9jF0jP0WAmbujexQ.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ckFGdl1s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AWjs8z-9jF0jP0WAmbujexQ.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that’s how you save your company thousands of every month on Marketing SMS by using a local SMS provider other than Twilio!&lt;/p&gt;

</description>
      <category>twilio</category>
      <category>smsapi</category>
      <category>javascript</category>
      <category>customerio</category>
    </item>
    <item>
      <title>Firebase Functions to Build a Serverless CRUD App</title>
      <dc:creator>Adebola</dc:creator>
      <pubDate>Sat, 24 Jun 2023 23:28:47 +0000</pubDate>
      <link>https://dev.to/debosthefirst/firebase-functions-to-build-a-serverless-crud-app-34kl</link>
      <guid>https://dev.to/debosthefirst/firebase-functions-to-build-a-serverless-crud-app-34kl</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1mIAsscJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AsYhkip0nV3VKJjYaGeBQFA.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1mIAsscJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AsYhkip0nV3VKJjYaGeBQFA.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://firebase.google.com/"&gt;Firebase&lt;/a&gt; is a powerful backend service provided by Google that speeds up Web Development by providing a BaaS (Backend-as-a-service) infrastructure. Firebase allows Engineering teams to quickly build out and prototype applications without the need to worry about common backend services like authentication, authorization, database provision, task schedulers (thank you cloud functions!), and more! Some of the features listed above are different services within the Firebase console — e.g Firestore is Firebase’s database service.&lt;/p&gt;

&lt;p&gt;Firebase can be integrated with a web or mobile application using any of the available SDKs. In this project, we’ll be using the Firebase JavaScript SDK.&lt;/p&gt;

&lt;p&gt;In this article, we’ll look at some of the most common functions you’ll need to build out a Backend using Firebase’s Firestore database and auth service. Think of this as a Cheat sheet to avoid reading the firebase docs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Started with Firebase
&lt;/h3&gt;

&lt;p&gt;To get started setting up your firebase service, simply head over to &lt;a href="https://firebase.google.com/"&gt;https://firebase.google.com/&lt;/a&gt;, make sure you’re signed in with your Google account, and hit the &lt;strong&gt;Get started&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;On the &lt;strong&gt;Welcome to Firebase&lt;/strong&gt; screen, click &lt;strong&gt;Create Project&lt;/strong&gt; to spin up a new firebase project. Give your project a name, accept the terms and conditions and let’s dance! You’ll be brought to the firebase console. Next, select your project from the list of projects and click &lt;strong&gt;Firestore Database&lt;/strong&gt; to the left of your dashboard. From here, we can manage everything to do with our Databases.&lt;/p&gt;

&lt;p&gt;By default, Firebase allows anyone to read and write from your database for a period of 30 days — as long as they have your keys. You can see your database rules by going under the &lt;strong&gt;rules&lt;/strong&gt; tab on the Firestore page in your firebase console. Make sure to edit these rules when you’re ready to go to PROD.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Firebase to your Project
&lt;/h3&gt;

&lt;p&gt;To add Firebase to your project, you’ll need a set of keys. Click on the gear icon next to &lt;strong&gt;Project Overview&lt;/strong&gt; to access the keys for your project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ii9m4KM9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/0%2A-T3SMkkXsKJV6xrY.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ii9m4KM9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/0%2A-T3SMkkXsKJV6xrY.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ll be integrating firebase into our React project. To set up Firebase in your project, run the command;&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This will add the Firebase SDK to your project. Create a &lt;code&gt;.env&lt;/code&gt; file in the root directory of your project and set the following variables. You can get the values from the Project Settings section above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiKey= YOUR_KEY
authDomain= YOUR_KEY
projectId=YOUR_KEY
storageBucket=YOUR_KEY
messagingSenderId=YOUR_KEY
appId=YOUR_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With your environment variables set up, we’re ready to start adding functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup Firebase in Your Codebase
&lt;/h3&gt;

&lt;p&gt;We’ll now run through getting Firebase setup within our app. In the root directory of your project, create a folder named utils and a file called &lt;strong&gt;firebase.js&lt;/strong&gt;. You can follow whatever directory structure you prefer but I like to setup my projects like this.&lt;/p&gt;

&lt;p&gt;In your &lt;strong&gt;firebase.js&lt;/strong&gt; file, add the following lines. We start off by importing all the functions we need from within the firebase module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// initialize firebase
import { initializeApp } from 'firebase/app'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// firebase auth service
import {
  getAuth,
  signInWithEmailAndPassword,
  onAuthStateChanged,
  updateProfile,
  signOut,
createUserWithEmailAndPassword
} from 'firebase/auth'

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// firebase DB service
import {
  collection,
  getFirestore,
  addDoc,
  getDocs,
  updateDoc,
  doc,
  getDoc,
  setDoc,
  arrayUnion,
  deleteDoc
} from 'firebase/firestore'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const firebaseConfig = {
  apiKey: process.env.apiKey,
  authDomain: process.env.authDomain,
  projectId: process.env.projectId,
  storageBucket: process.env.storageBucket,
  messagingSenderId: process.env.messagingSenderId,
  appId: process.env.appId,
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// initialize firebase
export const app = initializeApp(firebaseConfig)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// connect to firebase auth with your credentials
export const auth = getAuth(app)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// connect to firebase db with your credentials
const db = getFirestore(app)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On to the next!&lt;/p&gt;

&lt;h3&gt;
  
  
  Auth
&lt;/h3&gt;

&lt;p&gt;What’s one thing every app must have? You guessed it! Auth!&lt;/p&gt;

&lt;p&gt;Firebase allows you to use sign-in providers like Google, Twitter, Microsoft or Facebook to allow your users sign in with their accounts on other platforms. To set this up, simply head over to the &lt;strong&gt;Build&lt;/strong&gt; tab on your Firebase console, click &lt;strong&gt;Authentication&lt;/strong&gt; and then the &lt;strong&gt;sign in&lt;/strong&gt; method tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Qy5WbEqJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/0%2A20FGMbNrdnt_i13P.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Qy5WbEqJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/0%2A20FGMbNrdnt_i13P.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article, we’ll be keeping it simple and using the E-mail and password auth provided by Google. To get started, under the &lt;strong&gt;Users&lt;/strong&gt; tab, click &lt;strong&gt;Add user&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--STxDDRe4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/0%2AEMVQfEMP50CoCdWb.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--STxDDRe4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/0%2AEMVQfEMP50CoCdWb.png%2520align%3D%2522left%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This allows you to create a login for users on your platform from inside Firebase itself. To allow users sign up from within your UI, use the following block of code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const register = async ({ email, password }) =&amp;gt; {
  try {
    const { user } = await createUserWithEmailAndPassword(auth, email, password)
    return user
  } catch (e) {
    return false
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For your login flow, call the &lt;code&gt;signInWithEmailAndPassword&lt;/code&gt; provided by Firebase, like below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const login = async ({ email, password }) =&amp;gt; {
  try {
    const { user } = await signInWithEmailAndPassword(auth, email, password)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    return user
  } catch (e) {
    return false
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check if a user is signed in, use;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const isUserSignedIn = async () =&amp;gt; {
  onAuthStateChanged(auth, (user) =&amp;gt; {
    if (user) {
      // get signedIn user
      const uid = user.uid
      console.log(user)
      return uid
    } else {
      return false
    }
  })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To sign out a user from your service;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const signOutUser = async () =&amp;gt; {
  await signOut(auth)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To update a user’s metadata, use;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const updateUserProfile = async ({ displayName, photoURL }) =&amp;gt; {
  try {
    await updateProfile(auth.currentUser, {
      displayName,
      photoURL,
    })
  } catch (error) {
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding new Collections and Documents to Firestore
&lt;/h3&gt;

&lt;p&gt;Firebase is a No-SQL database. This means that the Database structure is a little bit different from conventional SQL databases. For example, while SQL databases are based off the idea of Tables and Columns, No-SQL databases use documents and collections to structure data. Collections can be likened to tables and documents to columns — they’re not like for like though and shouldn’t be considered as such.&lt;/p&gt;

&lt;p&gt;Let’s create a User’s collection in our DB. To create a new collection, we’ll simply add a document to the collection in our codebase. Firebase is intelligent enough to create the collection and a document at the same time even if the collection does not already exist.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// take in the user's details from the UI
const addNewUser = async ({ phoneNo, name, email, address, cars = '' }) =&amp;gt; {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  try {
    // add a new Doc to the User's Collection
    const userRef = await addDoc(collection(db, 'users'), {
      phoneNo,
      name,
      email,
      address,
      cars
    })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    console.log('Document written with ID: ', userRef.id)
  } catch (e) {
    console.error('Error adding document: ', e)
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Getting all the Data in a Firestore Collection
&lt;/h3&gt;

&lt;p&gt;If we want to get all the users in our database for example, we can use the &lt;code&gt;getDocs()&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const getCollection = async () =&amp;gt; {
  try {
    const users = []
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    const querySnapshot = await getDocs(collection(db, 'users'))
    querySnapshot.forEach((doc) =&amp;gt; {
      users.push({ user: doc.data(), docId: doc.id })
    })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    return users
  } catch (error) {
    console.log(error)
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update a Record in Firestore
&lt;/h3&gt;

&lt;p&gt;If you have a user in the DB already, you can update their records in the DB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Update the user's details
const updateUserPhoneNo = async ({ phoneNo, user }) =&amp;gt; {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  try {
    const userRef = doc(db, 'users', [USER_DOCUMENT_ID])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // Update the User's Collection
    const userRef = await updateDoc(userRef, {
      phoneNo,
    })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    console.log('Document written with ID: ', userRef.id)
  } catch (e) {
    console.error('Error adding document: ', e)
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the block of code above to work, you’ll need to know the &lt;strong&gt;USER_DOCUMENT_ID&lt;/strong&gt; to look up the user and update the user’s records.&lt;/p&gt;

&lt;p&gt;Firebase will automatically generate a &lt;strong&gt;Document ID&lt;/strong&gt; for data written to the database. While writing to the DB, this ID is generated after the document has been written which may make it a little difficult to update the record in the future. To prevent this, I prefer to use a unique ID to identify unique users in my database and subsequently use that ID to look up a user’s records in my database. You can use the popular &lt;a href="https://www.npmjs.com/package/uuid"&gt;UUID&lt;/a&gt; npm package to generate these IDs.&lt;/p&gt;

&lt;p&gt;For example, we can change our function to add a user to the following;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// take in the user's details from the UI
const addNewUser = async ({ phoneNo, name, email, address, cars = '' }) =&amp;gt; {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const userId = uuid_v4() // generate the userId
  const userRef = doc(db, 'users', userId) // use the userId generated to create a docId
  const data = {
      phoneNo,
      name,
      email,
      address,
      cars,
      userId // save the userId to the user record
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  try {
    // add a new Doc to the User's Collection
    await setDoc(userRef, data, { merge: true })
    console.log('Document written with ID: ', userRef.id)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  } catch (e) {
    console.error('Error adding document: ', e)
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would mean that our update function from earlier will look like;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Update the user's details
const updateUserPhoneNo = async ({ phoneNo, user }) =&amp;gt; {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  try {
    const userRef = doc(db, 'users', user.userId) // use the userId to find the user in the DB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // Update the User's Collection
    const userRef = await updateDoc(userRef, {
      phoneNo,
    })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    console.log('Document written with ID: ', userRef.id)
  } catch (e) {
    console.error('Error adding document: ', e)
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that &lt;strong&gt;addDoc()&lt;/strong&gt; and &lt;strong&gt;setDoc()&lt;/strong&gt; are slightly different in the way they work. &lt;code&gt;addDoc&lt;/code&gt; will automatically generate a Document ID for your document while writing to the DB while &lt;code&gt;setDoc&lt;/code&gt; allowing you to specify the Document ID you’ll like to use. For writing data that you’ll want to look up later, using &lt;code&gt;setDoc&lt;/code&gt; is advisable to allow for easier look-up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update an array Record in Firestore
&lt;/h3&gt;

&lt;p&gt;If you have a record in your database that takes in an array and you need to update that record, use the &lt;code&gt;array_union&lt;/code&gt; function. For example, imagine that our user has some cars in his Garage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Update the user's details
const updateUserPhoneNo = async ({ phoneNo, carName }) =&amp;gt; {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  try {
    const userRef = doc(db, 'users', [USER_DOC_ID]) // USER_DOC_ID is the userId if you followed the setDoc example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // Update the User's Collection
    const userRef = await updateDoc(userRef, {
      cars: arrayUnion({
          carName: carName,
      }),
    })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    console.log('Document written with ID: ', userRef.id)
  } catch (e) {
    console.error('Error adding document: ', e)
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deleting a Document in Firestore
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const deleteUser = async ({ user }) =&amp;gt;{
  try {
    const userRef = doc(db, 'users', user.userId)
    await deleteDoc(doc(db, "users", userRef));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  } catch (e) {
    console.error('Error adding document: ', e)
  }  
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rounding Up
&lt;/h3&gt;

&lt;p&gt;With the functions above, you can create your own application by using Firebase as a backend service. The functions above list all the CRUD operations you need to create your own backend service. To summarise;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;setDoc&lt;/code&gt; or &lt;code&gt;addDoc&lt;/code&gt; allows you to CREATE a new firestore document/collection.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;getDoc&lt;/code&gt; allows you to READ the data within your firestore collection&lt;/p&gt;

&lt;p&gt;&lt;code&gt;updateDoc&lt;/code&gt; allows you to UPDATE a document within a firestore collection&lt;/p&gt;

&lt;p&gt;&lt;code&gt;deleteDoc&lt;/code&gt; allows you to DELETE a document within a firestore collection.&lt;/p&gt;

&lt;p&gt;For more information, the &lt;a href="https://firebase.google.com/docs/firestore"&gt;Firebase docs&lt;/a&gt; provide a complete resource for all firestore functions and features.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>firebase</category>
      <category>crud</category>
    </item>
    <item>
      <title>A day in the life of a remote Software Engineer from Nigeria</title>
      <dc:creator>Adebola</dc:creator>
      <pubDate>Thu, 06 May 2021 17:19:01 +0000</pubDate>
      <link>https://dev.to/debosthefirst/a-day-in-the-life-of-a-remote-software-engineer-from-nigeria-1bj6</link>
      <guid>https://dev.to/debosthefirst/a-day-in-the-life-of-a-remote-software-engineer-from-nigeria-1bj6</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was originally published on the &lt;a href="https://www.aeeiee.com/blog/2021/01/21/everyday-life-at-aeeiee/" rel="noopener noreferrer"&gt;Aeeiee Blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;January 2021 Update: This article was originally written in 2020. It’s now 2021 and thankfully,  many more companies are learning to cope better with COVID. Let’s hope that this year brings in an effective Vaccine and things go back to some sort of normalcy.&lt;/em&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;2020 has been a year! There has been enough significant events in this year to take up at least 5 years. With the COVID-19 global pandemic hitting businesses this year, it has been a difficult time for many businesses. It has also forced many businesses to transition to remote work and a widespread acceptance of remote work as the future of many white collar jobs. &lt;/p&gt;

&lt;p&gt;Transitioning a company to one that allows for remote work is a difficult process on its own but making that change during a pandemic is even more difficult. Luckily for us at Aeeiee, as a software company, making that transition has not been too arduous and I believe the key to this has been in the recruitment and training process.&lt;/p&gt;

&lt;p&gt;With staff members across the US, India, and Nigeria, having a process that works and allows everyone to collaborate effectively is key to ensuring high quality output within a remote development team.&lt;/p&gt;

&lt;h1&gt;
  
  
  Starting at Aeeiee
&lt;/h1&gt;

&lt;p&gt;I joined Aeeiee in September of 2020 as the company started to ramp up its staff strength and transition to remote work. As someone who has been through several recruitment processes, I particularly liked the one at Aeeiee. The entire process from application to getting an offer took about 2 weeks – meaning that if you apply here, you aren’t left in limbo trying to figure out whether or not you’ll be offered a role and how quickly you will get a response to your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  The interview process
&lt;/h2&gt;

&lt;p&gt;The first stage was a timed online exam. It contained an aptitude test, an English test, and a couple of coding challenges. The second stage was to write an essay summarising an excerpt from the book The Falsification of Afrikan Consciousness, by Dr. Amos Wilson – a text I found very insightful, making it more than just a part of the recruitment process for me.&lt;/p&gt;

&lt;p&gt;The final interview was a 15 min presentation on any topic of choice with the rest of the Aeeiee team present. They got to ask me both technical and career related questions. I was impressed that other members of the team got a say in selecting the teammates they may potentially work with which clearly showed that the opinion of each person on the team was highly valued – another reason to join the team at Aeeiee.&lt;/p&gt;

&lt;h1&gt;
  
  
  A day in the life of an Aeeiee Dev
&lt;/h1&gt;

&lt;p&gt;At Aeeiee we are an Agile company, utilizing the Scrum methodology with “sprints” lasting 2 weeks. This means that we break down our projects into small tasks, weigh them, in other words – determine how long or difficult a piece of work is and then focus intensely on completing those tasks within the sprint. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here’s a quick breakdown of what a typical day at Aeeiee looks like for me. All times below are in Nigerian time (WAT).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7:00am&lt;/strong&gt; – I typically wake up around this time. Toss and turn for a bit, get up, grab my running shoes and head out the door for my every-other-day run. I’m training with the Nike run app and loving that. I’m training to hopefully run a 10K at the next Lagos City Marathon and if I don’t hit that goal, I will run at a 5k meet instead.&lt;/p&gt;

&lt;p&gt;Once done with my run, I’d typically get in a strawberry smoothie with a dash of &lt;a href="https://www.forbes.com/sites/nomanazish/2018/09/22/what-are-tiger-nuts-and-why-you-should-eat-them/" rel="noopener noreferrer"&gt;tigernut milk&lt;/a&gt; (considered a superfood) or a salad – getting all the nutrients you can get in is important for your mind to run optimally as a dev.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.aeeiee.com%2Fwp-content%2Fuploads%2F2021%2F01%2FRectangle-29-2-1024x1024.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.aeeiee.com%2Fwp-content%2Fuploads%2F2021%2F01%2FRectangle-29-2-1024x1024.jpg" alt="Buying Tigernuts"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Afterwards, I’d typically get some rest, read tech blogs or spend time writing down ideas on open source projects I’d like to build or contribute to. Or maybe just run any errands I need to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;12:00pm – 12:15pm:&lt;/strong&gt; Clock in for the day. At this time, the team in the US is still asleep while the team in India has gotten through a couple of hours of their workday already.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.aeeiee.com%2Fwp-content%2Fuploads%2F2021%2F01%2FRectangle-29-4-1024x1024.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.aeeiee.com%2Fwp-content%2Fuploads%2F2021%2F01%2FRectangle-29-4-1024x1024.jpg" alt="My work setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We use &lt;a href="https://www.tsheets.com/" rel="noopener noreferrer"&gt;Tsheets&lt;/a&gt; to track our time and tasks – I love the app and totally recommend it. We track our time in 15 min increments. &lt;/p&gt;

&lt;p&gt;After clocking in, I check my emails and slack messages first to see if there are any urgent requests or information I need to know. &lt;/p&gt;

&lt;p&gt;Once done with that, I head over to our &lt;strong&gt;Jira board&lt;/strong&gt; (we manage our workflow using Jira and use Confluence for documentation) to check if my team lead has left any new comments for me on the tickets I’m working on. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;12:15pm – 2:30pm:&lt;/strong&gt; Start work on a new ticket or continue work on a ticket I’ve been working on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2:30pm – 2:45pm:&lt;/strong&gt; Jump on our daily stand-up call. On this call we talk about what we did the previous day and what we plan to get done for the day. We also bring up any blockers we may be having. This is an interesting call because while it’s already afternoon here in Lagos, it’s late in the evening in India and it’s the early hours of the morning in Arizona.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2:45pm – 3:00pm:&lt;/strong&gt; I’d typically take a short break at this point. Protect your eyes! They’re the only ones you got! I also make sure to get in about 4-5L of water each day&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3:00pm – 4:30pm:&lt;/strong&gt; Continue work on the ticket I’m working on. I may hop on a call with teammates to get help or help out anyone else with their ticket. In between these times, I’m thinking about lunch and what to have – Jollof rice, Plantains or &lt;a href="https://en.wikipedia.org/wiki/Amala_(food)" rel="noopener noreferrer"&gt;Àmàlà&lt;/a&gt;? More recently, since its emergence as a social media sensation on TikTok, I’ve been considering &lt;a href="https://en.wikipedia.org/wiki/Fufu" rel="noopener noreferrer"&gt;Fufu&lt;/a&gt;.  I’m also attempting to make a mental decision on whether to order from a restaurant nearby or cook something.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4:30pm – 5:30pm:&lt;/strong&gt; At this point, I’m usually a little jittery and so I take a 1-hour break. I’d eat some lunch and maybe take a walk down the street. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropbox.com%2Fs_557CCA51859A82E8A0022D245A20F56A752EA9FFBF44B908C4E9DE58E2238BAB_1620321014965_image.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropbox.com%2Fs_557CCA51859A82E8A0022D245A20F56A752EA9FFBF44B908C4E9DE58E2238BAB_1620321014965_image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5:30pm – 5:45pm:&lt;/strong&gt; Get back to work. Daydream a little while mentally trying to get my mind back into work mode.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5:45 – 8:00pm:&lt;/strong&gt; I may be on any one of Google/Stackexchange/YouTube or the Php docs looking for anything that may help me get my ticket over the line. If I’m able to complete the ticket by the end of the day, I’ll move the ticket to in review to trigger a code review. &lt;/p&gt;

&lt;p&gt;Code reviews are something we take seriously at Aeeiee as it helps us ensure everyone is writing code in a similar way that makes it easy for any new developer joining the project to maintain the code. It also ensures that we’re architecting code in a streamlined manner and following best practices. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8:00pm:&lt;/strong&gt; I clock out for the day and make mental notes of questions or thoughts I may have had during the day that I need to speak to anyone on the team about at the next standup. &lt;/p&gt;

&lt;h2&gt;
  
  
  The most important things at Aeeiee
&lt;/h2&gt;

&lt;p&gt;Our CEO always says there’s one thing that’s the most important thing at Aeeiee – and that is correctly tracking time. But, I think there are 2 things. And those 2 things are what helps us get better each day – &lt;strong&gt;Time tracking and Training&lt;/strong&gt;. In my first month at Aeeiee, I spent the entire month learning the Aeeiee development stack – PHP and WordPress while also learning Jira and Agile. &lt;/p&gt;

&lt;p&gt;The productivity gains from those training are immense. As every developer will agree, a software training course cannot cover every use case and you may still need a number of resources to complete tasks but it’s always a good way to lay a solid foundation for learning more advanced concepts in the future.&lt;/p&gt;

&lt;p&gt;The best thing about training at Aeeiee? You get to do them on company time which means you’re paid to do them!&lt;/p&gt;

&lt;h1&gt;
  
  
  Team bonding at Aeeiee
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Gaming
&lt;/h2&gt;

&lt;p&gt;On Wednesdays, we have a one hour gaming session. So far it has been a great way to bond with everyone else on the team. We’ve played games like Among us, Call of Duty, and Charades amongst others! Everyone gets to suggest a new game to play every now and then.&lt;/p&gt;

&lt;h2&gt;
  
  
  Journeylines
&lt;/h2&gt;

&lt;p&gt;Every 2nd Friday after we complete our Sprint retrospective, we do this thing where one or two members of the team basically tell us their life stories. We haven’t gotten through to hearing everyone’s story yet but I’ve found these stories incredibly fascinating and I really believe that they’ve made us a closer knit team. It makes you see your teammates as people with stories and experiences and not just developers who write code. &lt;/p&gt;

&lt;h2&gt;
  
  
  African Leadership Training
&lt;/h2&gt;

&lt;p&gt;Something else that’s very important to us at Aeeiee is the African Leadership Training that all the West African developers can take. This training is delivered by our CEO with the goal being to help us understand our history as African people and using that knowledge to transform the way we think about Africa, how we approach the unique challenges we face as developing countries, and the way we see ourselves as people of African descent. It has helped us become more aware of the world around us.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping it all up!
&lt;/h2&gt;

&lt;p&gt;Working remotely as a developer at Aeeiee has been great! The lifestyle is certainly something I’d choose over and over again. Luckily for us at Aeeiee, we’ve been able to find a balance around the usual downsides that come with remote work; not being able to disconnect from work, being overworked amongst others. We try to make sure our projects are well scoped out and planned ahead of time to ensure that developers do not have to work overtime unless absolutely necessary. Also, between our daily standups, journeylines and gaming sessions, we’ve been able to find a system that works and allows us deliver high quality work for clients.&lt;/p&gt;

&lt;p&gt;What are you waiting for? Come join the team!&lt;/p&gt;

</description>
      <category>nigeria</category>
      <category>software</category>
      <category>remote</category>
    </item>
    <item>
      <title>How to to Upload files to S3 with the AWS JavaScript SDK and WordPress</title>
      <dc:creator>Adebola</dc:creator>
      <pubDate>Fri, 23 Apr 2021 08:36:14 +0000</pubDate>
      <link>https://dev.to/debosthefirst/how-to-to-upload-files-to-s3-with-the-aws-javascript-sdk-and-wordpress-11h7</link>
      <guid>https://dev.to/debosthefirst/how-to-to-upload-files-to-s3-with-the-aws-javascript-sdk-and-wordpress-11h7</guid>
      <description>&lt;p&gt;This article was first published on the &lt;a href="https://www.aeeiee.com/blog/2021/02/10/how-to-to-upload-files-to-s3-with-the-aws-javascript-sdk-and-wordpress/" rel="noopener noreferrer"&gt;Aeeiee Blog&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AWS?
&lt;/h2&gt;

&lt;p&gt;AWS has a ton of amazing services to help in the software development process. From services that help you cache static assets like CloudFront, to services that scale based on the traffic to your application like ElasticBeanstalk (this comes with a load balancer by default). At Aeeiee, we're huge fans of AWS. Our servers run on EC2 instances and we even use AWS CodeCommit for version control. AWS provides high availability and has been the go-to cloud solutions provider for millions of developers for sometime now.&lt;/p&gt;

&lt;p&gt;In this article,  we'll explore one of AWS' most popular services - AWS S3. You'll learn how to use AWS's S3 to store and manage digital assets on your WordPress site.&lt;/p&gt;

&lt;p&gt;Let's jump into it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating an AWS account
&lt;/h2&gt;

&lt;p&gt;Creating an AWS account is pretty straightforward. Follow the link &lt;a href="https://portal.aws.amazon.com/billing/signup#/start" rel="noopener noreferrer"&gt;here&lt;/a&gt; to sign up. You'll need a Credit Card to get started. Amazon uses it to verify your identity and keeps it on file for when they need to bill you. The steps needed in this tutorial are covered under the free tier. You can also destroy the bucket you've created when done to ensure your credit card is not charged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting your S3 Credentials
&lt;/h2&gt;

&lt;p&gt;On your AWS Dashboard, search for S3 in the search bar.&lt;/p&gt;

&lt;p&gt;Click the first option - S3.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.aeeiee.com%2Fwp-content%2Fuploads%2F2021%2F01%2FScreenshot-2021-01-19-at-12.56.49.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.aeeiee.com%2Fwp-content%2Fuploads%2F2021%2F01%2FScreenshot-2021-01-19-at-12.56.49.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, Click the Create bucket button.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.aeeiee.com%2Fwp-content%2Fuploads%2F2021%2F02%2FScreenshot-2021-02-01-at-13.46.55.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.aeeiee.com%2Fwp-content%2Fuploads%2F2021%2F02%2FScreenshot-2021-02-01-at-13.46.55.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give your bucket a name. We'll call ours &lt;strong&gt;aeeiee-test&lt;/strong&gt;. You can leave the default region. Ours is currently set to EU (London) - EU West 2. You can leave the other options as they are.&lt;/p&gt;

&lt;p&gt;To get your &lt;strong&gt;Access Key ID&lt;/strong&gt; and &lt;strong&gt;Secret Access Key&lt;/strong&gt;, click on your username in the top right of the dashboard.&lt;/p&gt;

&lt;p&gt;Then, select the &lt;strong&gt;my security credentials&lt;/strong&gt; option. Scroll down to the Access keys (Access Key ID and Secret Access Key) section and click to expand it. Click the &lt;strong&gt;create new access key&lt;/strong&gt; button to create a new access key. You'll be able to see your Secret Access Key here. You can download the file containing your keys to your computer or simply copy and paste the keys somewhere secure for safe keeping. Once you exit this popup, you may be unable to retrieve your secret access key again.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.aeeiee.com%2Fwp-content%2Fuploads%2F2021%2F02%2FScreenshot-2021-02-01-at-13.51.28.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.aeeiee.com%2Fwp-content%2Fuploads%2F2021%2F02%2FScreenshot-2021-02-01-at-13.51.28.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting up the AWS JS SDK for use in WordPress
&lt;/h2&gt;

&lt;p&gt;Next, we're going to create a Plugin to handle our uploads. To avoid running into issues, we've found that the best way to load the AWS S3 SDK is to link to the version hosted by AWS rather than self-hosting a copy of it on our own servers.&lt;/p&gt;

&lt;p&gt;In your plugins directory, create a new folder - &lt;strong&gt;aeeiee-s3-uploader&lt;/strong&gt;. Inside this folder, create a new file. We'll call ours &lt;strong&gt;index.php&lt;/strong&gt;. Also, create two additional files: &lt;strong&gt;aeeiee-s3.js&lt;/strong&gt; and &lt;strong&gt;aeeiee-s3-views.php&lt;/strong&gt;. The JavaScript file will hold all the JS code to handle uploading files to our S3 bucket while the &lt;strong&gt;aeeiee-s3-views.php&lt;/strong&gt; file will handle displaying HTML content on the page.&lt;/p&gt;

&lt;p&gt;We will create a plugin file with the information below. This ensures that WordPress can correctly detect and load our plugins and make it available to us on the Plugins page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
/**
 * Aeeiee S3 Plugin
 *
 * @author              Aeeiee Inc.
 *
 * @wordpress-plugin
 * Plugin Name:        Aeeiee S3 Plugin
 * Description:        This plugin allows us upload files to an Amazon S3 bucket.
 * Author:            Aeeiee Inc.
 * Author URI:        https://www.aeeiee.com
 * Version:            1.0
 * Requires PHP:     7.2
 */
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we enqueue the AWS JS SDK script using the link provided by AWS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;add_action('admin_enqueue_scripts', function () {
    // loads the AWS SDK
    wp_enqueue_script('aeeiee-aws-sdk', 'https://sdk.amazonaws.com/js/aws-sdk-2.828.0.min.js');

});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Walkthrough
&lt;/h2&gt;

&lt;p&gt;With our scripts enqueued, we would need to create a new page when our plugin is activated. When a user navigates to this page, they'll be presented with an upload form. Once they hit the upload button, the file they've selected will be uploaded to our S3 bucket.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.aeeiee.com%2Fwp-content%2Fuploads%2F2021%2F01%2FScreenshot-2021-01-20-at-17.26.40.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.aeeiee.com%2Fwp-content%2Fuploads%2F2021%2F01%2FScreenshot-2021-01-20-at-17.26.40.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Page and enqueuing Scripts
&lt;/h2&gt;

&lt;p&gt;First, we'll create a new page when the plugin is activated using the &lt;code&gt;add_menu_page&lt;/code&gt; API provided by WordPress. See code below.&lt;/p&gt;

&lt;p&gt;We want to ensure that our JavaScript files are enqueued only on the pages we need them, to avoid conflicts with other plugins. To do that, we use the &lt;code&gt;$hook_suffix&lt;/code&gt; variable that's automatically passed to the admin_enqueue_scripts hook callback. The &lt;code&gt;$hook_suffix&lt;/code&gt; tells us what page we're on and we can use that to activate our JavaScript file only on the pages we want.&lt;/p&gt;

&lt;p&gt;We also use &lt;code&gt;wp_localize_script&lt;/code&gt; API in WordPress to pass our AWS keys as variables to our JavaScript file using PHP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NB: Replace YOUR ACCESS KEY ID and YOUR SECRET ACCESS KEY with the relevant values you obtained when you created your AWS account.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;add_action('admin_menu', 'aeeiee_s3_uploader');

function aeeiee_s3_uploader()
{
    // add a new page to the menu
    add_menu_page('Aeeiee S3 Page', 'Aeeiee S3 Uploader', 'manage_options', 'aeeiee-s3-uploader', 'aeeiee_s3_callback', 'dashicons-chart-pie');

    // enqueue the JS scripts in the admin page
    add_action('admin_enqueue_scripts', 'aeeiee_s3_enqueue_scripts');
}

function aeeiee_s3_enqueue_scripts($hook_suffix)
{
    // if on the uploader page in the admin section, load the JS file
    if ($hook_suffix === 'toplevel_page_aeeiee-s3-uploader') {
        wp_enqueue_script('aeeiee-js', plugins_url('/aeeiee-s3.js', __FILE__));

        $aws_vars = array(
            'accessKeyId' =&amp;gt; "YOUR ACCESS KEY ID",
            'secretAccessKey' =&amp;gt; "YOUR SECRET ACCESS KEY",
        );

        // pass AWS Keys from the server to the client
        wp_localize_script('aeeiee-js', 'vars', $aws_vars);
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With our JavaScript file enqueued. Time to finally hook up our views too. Still within the index.php file, add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function aeeiee_s3_callback(){
    include_once 'aeeiee-s3-views.php';
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the aeeiee-s3-views.php file, add the code to display the HTML on the frontend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;section&amp;gt;
    &amp;lt;label for="s3-uploader"&amp;gt;&amp;lt;strong&amp;gt;Upload a file to our S3 Bucket! &amp;lt;/strong&amp;gt;&amp;lt;/label&amp;gt;
    &amp;lt;div&amp;gt;
        &amp;lt;input type="file" id="file-uploader" /&amp;gt;
        &amp;lt;button id="start-upload"&amp;gt;Start Upload&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;
        &amp;lt;p class="message"&amp;gt; &amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've also added a paragraph tag with a class of message that we can use to inform users about the status of their upload.&lt;/p&gt;

&lt;h2&gt;
  
  
  Uploading the Files to S3
&lt;/h2&gt;

&lt;p&gt;Finally, for the main bit. We will write this part in JQuery. Head into your aeeiee-s3.js file. First step is to initialize the SDK with our Keys.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// initialize AWS SDK
var s3 = new AWS.S3({
 accessKeyId: aws_vars.accessKeyId, 
secretAccessKey: aws_vars.secretAccessKey, 
region: 'eu-west-2'
});
const bucketName = "aeeiee-test";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Here's how the upload is going to work.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When a user selects a file, we will display a &lt;strong&gt;"Starting your file Upload to AWS S3...."&lt;/strong&gt; message.&lt;/p&gt;

&lt;p&gt;When the upload is done, we will once again inform the user by displaying a &lt;strong&gt;"File successfully uploaded to S3"&lt;/strong&gt; message.&lt;/p&gt;

&lt;p&gt;Here's the complete code for the JavaScript upload process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jQuery(document).ready(function ($) {
  const fileUploadInput = $("#file-uploader");
  const messageSection = $(".message");

  $("#start-upload").on("click", function () {
    const file = fileUploadInput[0].files[0];

    messageSection.html("Starting your file Upload to AWS S3....");

    var upload = new AWS.S3.ManagedUpload({
      service: s3,
      params: {
        Body: file,
        Bucket: "aeeiee-test",
        Key: file.name,
      },
    });

    //  start the upload
    upload.send(function (err, data) {
      if (err) {
        console.log("Error", err.code, err.message);
        alert("There was an error uploading the file, please try again");
      } else {
        messageSection.html("File successfully uploaded to S3");
      }
    });
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Listing files in the bucket and downloading files
&lt;/h2&gt;

&lt;p&gt;The AWS SDK provides high level APIs that allow us to perform a number of actions on buckets. There's a &lt;code&gt;listObjects&lt;/code&gt; API that allows us list the objects in a bucket. &lt;/p&gt;

&lt;p&gt;In our case, we will also be using the &lt;code&gt;getSignedURL&lt;/code&gt; API to generate URLs that expire in 2mins. These URLs will be attached to objects from our buckets when displaying them on the frontend. This way, if a user clicks on that link within 2mins, the file(object) will be downloaded to their machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Call S3 to obtain a list of the objects in the bucket
 s3.listObjects({ Bucket: bucketName }, function (err, data) {
   if (err) {
     console.log("Error", err);
   } else {
     console.log("Success", data.Contents);
     data.Contents.map((content) =&amp;gt; {
       objectsSection.append(
         `&amp;lt;li&amp;gt;&amp;lt;a href="${getPresignedURL(content.Key)}"&amp;gt;${
           content.Key
         }&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;`
       );
     });
   }
 });

 function getPresignedURL(key) {
   return s3.getSignedUrl("getObject", {
     Bucket: bucketName,
     Key: key,
     Expires: 120,
   });
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;** Final Result **&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.aeeiee.com%2Fwp-content%2Fuploads%2F2021%2F01%2FScreenshot-2021-01-20-at-19.54.27.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.aeeiee.com%2Fwp-content%2Fuploads%2F2021%2F01%2FScreenshot-2021-01-20-at-19.54.27.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling CORS errors
&lt;/h2&gt;

&lt;p&gt;You'll most likely run into CORS errors when trying to connect to your AWS bucket from your local machine/server. To resolve this problem, head back to your AWS Admin console and head to the Buckets section of the Admin area. Click on the name of your Bucket  - in our case, we've called it aeeiee-test. Then Click &lt;strong&gt;permissions&lt;/strong&gt;. Scroll down to the CORS section. Add the following JSON code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.aeeiee.com%2Fwp-content%2Fuploads%2F2021%2F01%2FScreenshot-2021-01-20-at-18.47.05.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.aeeiee.com%2Fwp-content%2Fuploads%2F2021%2F01%2FScreenshot-2021-01-20-at-18.47.05.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our case, we're giving permissions to our local development server - &lt;a href="https://aeeieetests.local" rel="noopener noreferrer"&gt;https://aeeieetests.local&lt;/a&gt; to read from our bucket (GET) and upload objects to our bucket (POST). You will need to change the &lt;code&gt;AllowedOrigins&lt;/code&gt; to use your local host environment. Our dev server can also make PUT or PATCH requests. In production, you may want to alter the permissions given to users accessing your buckets from different environments.&lt;/p&gt;

&lt;p&gt;You can read more on CORS in the AWS documentation &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;p&gt;AWS is able to protect your bucket from unauthorised access by enforcing that buckets that do not have public access, must have explicit permissions given to hosts/domains to access them.&lt;/p&gt;

&lt;p&gt;On the other hand, with our current setup, our AWS credentials will be exposed to the Client and can be accessed from the console. &lt;a href="https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-browser.html" rel="noopener noreferrer"&gt;AWS frowns on this&lt;/a&gt;. The way we solve this problem internally is by creating a temporary bucket using the process described in this article and then moving the files from there to a permanent bucket using PHP on the server. This way we are able to keep our credentials from being exposed on the client side. We'll explain the exact process for this in a future article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Versioning
&lt;/h2&gt;

&lt;p&gt;When the same object is uploaded to your bucket more than once, AWS will automatically overwrite the previous object in the bucket. To avoid this, you can ensure that your files have unique file names before starting the upload to your S3 bucket.&lt;/p&gt;

&lt;p&gt;The complete version of the code used in this example is available &lt;a href="https://github.com/Aeeiee-Team/aws-wordpress" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to collect payments with PayPal in your React application</title>
      <dc:creator>Adebola</dc:creator>
      <pubDate>Mon, 22 Feb 2021 14:50:02 +0000</pubDate>
      <link>https://dev.to/debosthefirst/how-to-collect-payments-with-paypal-in-your-react-application-4a87</link>
      <guid>https://dev.to/debosthefirst/how-to-collect-payments-with-paypal-in-your-react-application-4a87</guid>
      <description>&lt;p&gt;I recently built a project that required integrating with Paypal to collect payments from users. After spending hours trying to implement Paypal payments using the Paypal JavaScript SDK, I realized that this was going to be a uphill task. Thankfully, I found an NPM package that already abstracted the SDK into React components for us to use. In this article, I’ll show you exactly how to collect payments using Paypal in your React application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started - setup your Paypal account
&lt;/h2&gt;

&lt;p&gt;First steps first. Head over to &lt;a href="https://paypal.com" rel="noopener noreferrer"&gt;paypal&lt;/a&gt; to create an account. Once done, head to the &lt;a href="https://developer.paypal.com/developer/applications/" rel="noopener noreferrer"&gt;paypal developer screen&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Getting your credentials
&lt;/h2&gt;

&lt;p&gt;Next step is to grab your credentials i.e your clientId. &lt;/p&gt;

&lt;p&gt;Navigate to &lt;strong&gt;Dashboard &amp;gt; My Apps &amp;amp; Credentials .&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropbox.com%2Fs_DC6E8A8E4922E20EC90B04A7B753483339F9486803BF4597878F1DB1072D2D9F_1613308310936_Screenshot%2B2021-02-14%2Bat%2B14.11.08.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropbox.com%2Fs_DC6E8A8E4922E20EC90B04A7B753483339F9486803BF4597878F1DB1072D2D9F_1613308310936_Screenshot%2B2021-02-14%2Bat%2B14.11.08.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;Sandbox&lt;/strong&gt; tab. Then click the on the &lt;strong&gt;Default Application&lt;/strong&gt; link. It will bring you to a page containing your clientId. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropbox.com%2Fs_DC6E8A8E4922E20EC90B04A7B753483339F9486803BF4597878F1DB1072D2D9F_1613308460311_Screenshot%2B2021-02-14%2Bat%2B14.13.23.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropbox.com%2Fs_DC6E8A8E4922E20EC90B04A7B753483339F9486803BF4597878F1DB1072D2D9F_1613308460311_Screenshot%2B2021-02-14%2Bat%2B14.13.23.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your &lt;strong&gt;sandbox account&lt;/strong&gt; will be an email address that you can use to make test payments while your &lt;strong&gt;client ID&lt;/strong&gt; is what Paypal uses to connect your Application to your paypal account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup your react project
&lt;/h2&gt;

&lt;p&gt;For this example, our React project will be built using &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;NextJS&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you’ll like to follow along, you can skip the next couple of steps by simply cloning my repo. Run the &lt;code&gt;git clone git@github.com:onedebos/nextjs-paypal-example.git&lt;/code&gt; command to do so. Then checkout to the starter branch with &lt;code&gt;git checkout starter&lt;/code&gt; . If you clone the starter repo, you can skip to the &lt;strong&gt;Setup project structure&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;Otherwise, here are the steps to follow. &lt;/p&gt;

&lt;p&gt;We’ll be using one of the NextJS example projects with &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;tailwindcss&lt;/a&gt; already configured. &lt;/p&gt;

&lt;p&gt;Run the command &lt;code&gt;yarn create next-app --example with-tailwindcss next-paypal-example&lt;/code&gt;  to create a NextJS application with Tailwindcss already configured. &lt;/p&gt;

&lt;h1&gt;
  
  
  Setup Project Structure
&lt;/h1&gt;

&lt;p&gt;We’ll create a new folder in our current project called &lt;code&gt;utils&lt;/code&gt;. Inside our utils folder, we’ll create a &lt;code&gt;constants&lt;/code&gt; folder. Within the constants folder, add an &lt;code&gt;index.js&lt;/code&gt; file. &lt;/p&gt;

&lt;p&gt;Your folder structure should now look like&lt;/p&gt;

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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Install the Paypal package&lt;/strong&gt;&lt;br&gt;
Install the react paypal package using &lt;code&gt;yarn add @paypal/react-paypal-js@4.1.0&lt;/code&gt;. &lt;br&gt;
&lt;strong&gt;Make sure to use the package at version 4.1.0 as i've found that people are having issues with the later version. I haven't had the time to try out later versions yet.&lt;/strong&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Collect Payments
&lt;/h1&gt;

&lt;p&gt;Time to start collecting Payments!&lt;/p&gt;

&lt;p&gt;In your &lt;code&gt;utils/constants/index.js&lt;/code&gt; file, add your &lt;strong&gt;clientId.&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const PAYPAL_CLIENT_ID = {
    clientId: 'ATVzbN_TdDnGGVfyPxu6J-5ddFftdqu8l6tFpIy5TEZ7hjbx7y9Q4TY0ICI0Pot2dBBABc-myxZgYOfj'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;In your &lt;code&gt;_app.js&lt;/code&gt; file, bring in the &lt;code&gt;PayPalScriptProvider&lt;/code&gt; using &lt;code&gt;import { PayPalScriptProvider } from "@paypal/react-paypal-js";&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Then, wrap your &lt;code&gt;Components&lt;/code&gt; with that tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PayPalScriptProvider&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="s2"&gt;@paypal/react-paypal-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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;PAYPAL_CLIENT_ID&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;../utils/constants&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MyApp&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="nx"&gt;pageProps&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PayPalScriptProvider&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;client-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;PAYPAL_CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientId&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;pageProps&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&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="sr"&gt;/PayPalScriptProvider&lt;/span&gt;&lt;span class="err"&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;MyApp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, head into  &lt;code&gt;pages/index.js&lt;/code&gt; to create the page that collects the payments and bring in the PayPal Button.&lt;/p&gt;

&lt;p&gt;Let’s create some state to hold data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;succeeded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSucceeded&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;paypalErrorMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPaypalErrorMessage&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;orderID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setOrderID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;billingDetails&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setBillingDetails&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&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;p&gt;The &lt;code&gt;orderId&lt;/code&gt; is the most important piece of state we care about. When the user clicks the &lt;strong&gt;Pay with PayPal&lt;/strong&gt; button, Paypal will generate an orderId for the order and return that back to us. In the &lt;code&gt;createOrder&lt;/code&gt; function below, we can see this in action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;      &lt;span class="c1"&gt;// creates a paypal order&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createOrder&lt;/span&gt; &lt;span class="o"&gt;=&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="nx"&gt;actions&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;return&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;purchase_units&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;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="c1"&gt;// charge users $499 per order&lt;/span&gt;
                  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;499&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="c1"&gt;// remove the applicaiton_context object if you need your users to add a shipping address&lt;/span&gt;
            &lt;span class="na"&gt;application_context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;shipping_preference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NO_SHIPPING&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;orderID&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="nf"&gt;setOrderID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orderID&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;orderID&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;Along with the &lt;code&gt;createOrder&lt;/code&gt; function, we need another function that runs when the payment is approved - &lt;code&gt;onApprove&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;      &lt;span class="c1"&gt;// handles when a payment is confirmed for paypal&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onApprove&lt;/span&gt; &lt;span class="o"&gt;=&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="nx"&gt;actions&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;return&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;details&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;payer&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nf"&gt;setBillingDetails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nf"&gt;setSucceeded&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;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setPaypalErrorMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Something went wrong.&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;Lastly, we can plugin out PayPal button from our react-paypal-js package to handle the payments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;PayPalButtons&lt;/span&gt;
                &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
                  &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="na"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pill&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pay&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="na"&gt;tagline&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="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;horizontal&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;createOrder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createOrder&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="nx"&gt;onApprove&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onApprove&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paypal will redirect the user to a new window to complete the payment. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropbox.com%2Fs_DC6E8A8E4922E20EC90B04A7B753483339F9486803BF4597878F1DB1072D2D9F_1613318001261_Screenshot%2B2021-02-14%2Bat%2B16.53.04.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropbox.com%2Fs_DC6E8A8E4922E20EC90B04A7B753483339F9486803BF4597878F1DB1072D2D9F_1613318001261_Screenshot%2B2021-02-14%2Bat%2B16.53.04.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can test this out using the sandbox email provided on the Paypal developer dashboard.&lt;/p&gt;

&lt;p&gt;The full repo for the code is &lt;a href="https://github.com/onedebos/nextjs-paypal-example" rel="noopener noreferrer"&gt;here&lt;/a&gt;.  &lt;/p&gt;

</description>
      <category>paypal</category>
      <category>react</category>
      <category>payments</category>
    </item>
    <item>
      <title>How to use cookies for persisting users in Nextjs</title>
      <dc:creator>Adebola</dc:creator>
      <pubDate>Thu, 29 Oct 2020 10:48:26 +0000</pubDate>
      <link>https://dev.to/debosthefirst/how-to-use-cookies-for-persisting-users-in-nextjs-4617</link>
      <guid>https://dev.to/debosthefirst/how-to-use-cookies-for-persisting-users-in-nextjs-4617</guid>
      <description>&lt;h2&gt;
  
  
  With LocalStorage
&lt;/h2&gt;

&lt;p&gt;There are a number of ways to persist users in a React or Single Page Appplication. A lot of times, devs generally use localStorage to store user data and load the data from there when required. While this approach works, it's not the most effective way as it leaves users vulnerable to attacks. Using cookies is a little safer although it's still not the safest option. Personally, I prefer a mixture of using cookies and JWT's&lt;a href="https://jwt.io/"&gt;JSON Web tokens&lt;/a&gt; with expiry to persist user session and to force a user to re-login when their session expires. Using JWT's is out of the scope of this article.&lt;/p&gt;

&lt;p&gt;As LocalStorage is undefined on the server-side(since localStorag does not exist on the server), it's impossible to access localStorage before rendering a route. As such, our best bet is to check if a user's cookie is valid on the server side before rendering a route.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started using cookies in React/NextJS
&lt;/h2&gt;

&lt;p&gt;To use cookies in NextJS, we need to install 2 packages. For this tutorial, we'll be using &lt;strong&gt;cookie&lt;/strong&gt; and &lt;strong&gt;react-cookie&lt;/strong&gt;. &lt;strong&gt;React-cookie&lt;/strong&gt; allows us set the cookie from the client side while the &lt;strong&gt;cookie&lt;/strong&gt; package lets us access the set cookie from the server-side. Install both packages by running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install react-cookie cookie
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://npmjs.com/package/cookie-cutter"&gt;Cookie-cutter&lt;/a&gt; is a tiny package that does the same thing as react-cookie.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting a cookie
&lt;/h2&gt;

&lt;p&gt;With both packages installed, It's time to set a cookie. Usually, we set a cookie for a user once they've succesfully signed in or signed up to our application. To set a cookie on Sign in, follow the example below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/login.js&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;useCookies&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="s2"&gt;react-cookie&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Login&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCookie&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useCookies&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&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;handleSignIn&lt;/span&gt; &lt;span class="o"&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="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;response&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;yourLoginFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;//handle API call to sign in here.&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&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="nx"&gt;setCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&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;stringify&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="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;maxAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Expires after 1hr&lt;/span&gt;
        &lt;span class="na"&gt;sameSite&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="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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="nx"&gt;htmlFor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&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="nx"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;enter username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Login&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the snippet above, we call the &lt;code&gt;setCookie&lt;/code&gt; hook from &lt;code&gt;react-cookies&lt;/code&gt; and set it to a default name. In our case, that's &lt;strong&gt;user&lt;/strong&gt;. We then&lt;br&gt;
make a request to sign in a user by calling a function to log the user in. We take the response from that API call, stringify the data(cookies are formatted as text) and store that data in a cookie.&lt;/p&gt;

&lt;p&gt;We also pass some additional options to the cookie including &lt;strong&gt;path&lt;/strong&gt; - makes sure your cookie is accessible in all routes, &lt;strong&gt;maxAge&lt;/strong&gt;, how long from the time the cookie is set till it expires and &lt;strong&gt;sameSite&lt;/strong&gt;. Samesite indicates that this cookie can only be used on the site it originated from - It is important to set this to true to avoid errors within firefox logs.&lt;/p&gt;
&lt;h2&gt;
  
  
  Giving your app access to the Cookie
&lt;/h2&gt;

&lt;p&gt;To ensure that every route in our application has access to the cookie, we need to wrap our APP component in a cookie provider.&lt;/p&gt;

&lt;p&gt;Inside &lt;code&gt;_app.js&lt;/code&gt;, add the following bit of code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/_app.js&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;CookiesProvider&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="s2"&gt;react-cookie&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;pageProps&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CookiesProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;pageProps&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&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="sr"&gt;/CookiesProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;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;
  
  
  Setting up the function to parse the cookie
&lt;/h2&gt;

&lt;p&gt;Next, we need to setup a function that will check if the cookie exists on the server, parse the cookie and return it. Created a new folder called &lt;strong&gt;helpers&lt;/strong&gt; and within that add an &lt;strong&gt;index.js&lt;/strong&gt; file.&lt;/p&gt;

&lt;p&gt;Inside this file, add the following piece of code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// helpers/index.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cookie&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cookie&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;parseCookies&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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookie&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookie&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;The function above accepts a request object and checks the request headers to find the cookie stored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessing the cookie within your component
&lt;/h2&gt;

&lt;p&gt;Finally, we will use &lt;code&gt;getInitialProps&lt;/code&gt; in our component to check if the user already has a valid cookie on the server side before rendering the requested route. An alternative to this approach is using &lt;code&gt;getServerSideProps&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;parseCookies&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="s2"&gt;../helpers/&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;HomePage&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;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Homepage&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&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;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Data&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;user&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;/p&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="sr"&gt;/div&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&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="nx"&gt;HomePage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getInitialProps&lt;/span&gt; &lt;span class="o"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parseCookies&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="k"&gt;if&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="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="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&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="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nb"&gt;Object&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;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;301&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&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;return&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="o"&gt;&amp;amp;&amp;amp;&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within &lt;code&gt;getInitialProps&lt;/code&gt;, we're passing in the request object(req) that's available to us on the server-side in NextJS to the &lt;code&gt;parseCookies&lt;/code&gt; function. This function returns the cookie to us which we can then send back to the client as props.&lt;/p&gt;

&lt;p&gt;We also do a check on the server to see if the response object is available. The &lt;strong&gt;res&lt;/strong&gt; object is only available on the server. If a user hits the &lt;strong&gt;HomePage&lt;/strong&gt; route using &lt;strong&gt;next/link&lt;/strong&gt; or &lt;strong&gt;next/router&lt;/strong&gt;, the &lt;strong&gt;res&lt;/strong&gt; object will not be available. &lt;/p&gt;

&lt;p&gt;Using the &lt;strong&gt;res&lt;/strong&gt; object, we check if there are cookies and if they're still valid. We do this check using the &lt;code&gt;res&lt;/code&gt; object. If the &lt;code&gt;data&lt;/code&gt; object is empty, it means the cookie isn't valid. If the cookie isn't valid, we then redirect the user back to the index page rather than showing a flash of the &lt;strong&gt;HomePage&lt;/strong&gt; before redirecting the user.&lt;/p&gt;

&lt;p&gt;Note that subsequent requests to pages containing &lt;code&gt;getInitialProps&lt;/code&gt; &lt;strong&gt;using next/link&lt;/strong&gt; or &lt;strong&gt;next/router&lt;/strong&gt; will be done from the client side. i.e The cookie will be extracted from the client rather than the server side for other routes that are accessed via &lt;strong&gt;using next/link&lt;/strong&gt; or &lt;strong&gt;next/router&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And with that, you can now store cookies for users in your application, expire those cookies and secure your app to a good extent.&lt;/p&gt;

</description>
      <category>cookies</category>
      <category>nextjs</category>
      <category>react</category>
      <category>auth</category>
    </item>
    <item>
      <title>How to show a loading bar when changing routes in NextJS</title>
      <dc:creator>Adebola</dc:creator>
      <pubDate>Thu, 29 Oct 2020 10:38:45 +0000</pubDate>
      <link>https://dev.to/debosthefirst/how-to-show-a-loading-bar-when-changing-routes-in-nextjs-304e</link>
      <guid>https://dev.to/debosthefirst/how-to-show-a-loading-bar-when-changing-routes-in-nextjs-304e</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F800%2F1%2A9E6_50z9VcxAoSdotHaNyw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F800%2F1%2A9E6_50z9VcxAoSdotHaNyw.gif" alt="Nprogress"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up
&lt;/h2&gt;

&lt;p&gt;I spent days trying to find the right thing to google on how to setup the blue loading bar that shows at the top of your screen when navigating between routes in NextJS. It's important to have this loading indicator to signify that a route change is occuring to avoid users assuming that the application has frozen.&lt;br&gt;
This article assumes that you already have NextJS setup. To get started, we need to install the &lt;code&gt;nprogress&lt;/code&gt; library.&lt;br&gt;
Run&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  In _app.js
&lt;/h2&gt;

&lt;p&gt;Inside &lt;strong&gt;pages/_app.js&lt;/strong&gt;, add the following piece of code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;NProgress&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nprogress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Head&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/head&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/router&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onRouteChangeStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&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;NProgress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onRouteChangeComplete&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="nx"&gt;NProgress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onRouteChangeError&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="nx"&gt;NProgress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;done&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MyApp&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="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
               &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;
                &lt;span class="nx"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stylesheet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;href&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://cdnjs.cloudflare.com/ajax/libs    /nprogress/0.2.0/nprogress.min.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
             &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Head&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;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="nx"&gt;pageProps&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;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;Above, we access the Router object available inside &lt;strong&gt;next/router&lt;/strong&gt; to ensure. &lt;code&gt;onRouteChangeStart&lt;/code&gt; i.e when a Route change is in progress, NProgress will run i.e &lt;code&gt;NProgress.start&lt;/code&gt;. Once the route change is done i.e &lt;code&gt;onRouteChangeComplete&lt;/code&gt;, we call the done method on &lt;code&gt;NProgress&lt;/code&gt;. We call the same method if there's an error.&lt;br&gt;
I had issues using the NProgress stylesheet from the &lt;code&gt;nprogress&lt;/code&gt; package within my application and resorted to using the stylesheet they provide via their CDN. Without the stylesheet, you won't notice anything on the screen.&lt;br&gt;
Now, when you navigate to any page within your application, you should see a blue loading bar at the top of your screen&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>loading</category>
      <category>nprogress</category>
      <category>route</category>
    </item>
    <item>
      <title>The easiest way to achieve dark mode in tailwindcss</title>
      <dc:creator>Adebola</dc:creator>
      <pubDate>Sat, 05 Sep 2020 21:38:29 +0000</pubDate>
      <link>https://dev.to/debosthefirst/the-easiest-way-to-achieve-dark-mode-in-tailwindcss-nb0</link>
      <guid>https://dev.to/debosthefirst/the-easiest-way-to-achieve-dark-mode-in-tailwindcss-nb0</guid>
      <description>&lt;p&gt;Here's a quickest way I've found to achieve dark and light mode theming in tailwindcss. &lt;/p&gt;

&lt;h3&gt;
  
  
  In tailwind.config.js
&lt;/h3&gt;

&lt;p&gt;In this file, add the following &lt;strong&gt;screens&lt;/strong&gt; to &lt;strong&gt;extend&lt;/strong&gt;. i.e&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
  future: {
    removeDeprecatedGapUtilities: true,
  },
  purge: ['./components/**/*.{js,ts,jsx,tsx}', './pages/**/*.{js,ts,jsx,tsx}'],
  theme: {
    extend: {
      colors: {},
      screens: {
        'light': { raw: '(prefers-color-scheme: light)' },
        'dark': { raw: '(prefers-color-scheme: dark)' }
      }
    },
  },
  variants: {
    backgroundColor: ['responsive', 'hover', 'focus', 'checked'],
  },
  plugins: [],
}

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

&lt;/div&gt;



&lt;p&gt;Now, we can do something like &lt;code&gt;dark:bg-red-700&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To test, open your Google dev tools console, click the 3 dots icon, click &lt;strong&gt;more tools&lt;/strong&gt; then select &lt;strong&gt;rendering&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Under &lt;strong&gt;rendering&lt;/strong&gt;, scroll down to &lt;strong&gt;Emulate CSS media feature prefers-color-scheme&lt;/strong&gt;. In the dropdown, select &lt;strong&gt;prefers-color-scheme: dark&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You should see our &lt;code&gt;dark:bg-red-700&lt;/code&gt; now take effect.&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>darkmode</category>
      <category>easy</category>
      <category>css</category>
    </item>
    <item>
      <title>How to make your web apps load in less than a second</title>
      <dc:creator>Adebola</dc:creator>
      <pubDate>Sat, 05 Sep 2020 11:30:47 +0000</pubDate>
      <link>https://dev.to/debosthefirst/how-to-make-your-web-apps-load-in-less-than-a-second-399o</link>
      <guid>https://dev.to/debosthefirst/how-to-make-your-web-apps-load-in-less-than-a-second-399o</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1200%2F1%2A02ChEOW5wSAc2SjizDoNJA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1200%2F1%2A02ChEOW5wSAc2SjizDoNJA.png" alt="Preloading/Prefetching by Adebola Adeniran"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I recently noticed that when I search for certain articles on google and click the search result, the page shows up instantly. I was curious as to how it worked and started to do some digging.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1200%2F1%2AyEbs_S6bnnNngHkG7DvPAQ.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1200%2F1%2AyEbs_S6bnnNngHkG7DvPAQ.gif" alt="Preloading effect on WhichWifi.work"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The GIF above shows the &lt;strong&gt;preloading&lt;/strong&gt; effect instantly loading &lt;strong&gt;&lt;a href="https://whichwifi.work" rel="noopener noreferrer"&gt;whichwifi&lt;/a&gt;&lt;/strong&gt; - a review app i launched recently. If you click &lt;a href="https://whichwifi.work" rel="noopener noreferrer"&gt;this&lt;/a&gt; link, it should open up the whichwifi page without "loading" it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tech
&lt;/h2&gt;

&lt;p&gt;The technique used in showing pages immediately a user clicks on the link is not entirely new. It's a technique called &lt;a href="https://en.wikipedia.org/wiki/Link_prefetching" rel="noopener noreferrer"&gt;prefetching/preloading&lt;/a&gt;. I'll explain the difference in a bit. Stick with me.&lt;/p&gt;

&lt;p&gt;The gist is, when a link(s) shows up on a webpage, the browser immediately starts to fetch resources for the linked pages within that domain so that when a user clicks the link, they're shown the page instantly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/docs/api-reference/next/link" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; makes use of this tech extensively in their framework which makes for better responsiveness and faster page load times.&lt;/p&gt;

&lt;p&gt;This &lt;a href="https://en.wikipedia.org/wiki/Link_prefetching" rel="noopener noreferrer"&gt;wikipedia&lt;/a&gt; entry contains some of the pros and cons of prefetching.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1200%2F1%2AXJEqftFaKKSIbjrvs8wCAg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1200%2F1%2AXJEqftFaKKSIbjrvs8wCAg.gif" alt="preloading WhichWifi.work from Google"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@addyosmani" rel="noopener noreferrer"&gt;Addy Osmani&lt;/a&gt;, a software engineer at google, also has a very &lt;a href="https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf" rel="noopener noreferrer"&gt;insightful article&lt;/a&gt; on the tech behind preloading and prefetching and how google uses it in search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preloading vs Prefetching
&lt;/h2&gt;

&lt;p&gt;There's a slight difference between preloading and prefetching. While preloading allows you load resources ahead of time, prefetching is used for subsequent requests within your site.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to get started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Next.js
&lt;/h3&gt;

&lt;p&gt;If you're building on the &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; framework(my favorite React framework btw), &lt;a href="https://nextjs.org/docs/api-reference/next/link" rel="noopener noreferrer"&gt;prefetching&lt;/a&gt; links comes bundled right out of the box. Once a user lands on your site, Next will automatically prefetch all of the pages that are linked on that page.&lt;/p&gt;

&lt;p&gt;To preload critical resources before a user lands on your page, instant.page provides a script to help us handle that. Here's a &lt;a href="https://github.com/onedebos/which-wifi/blob/master/pages/_document.tsx" rel="noopener noreferrer"&gt;link&lt;/a&gt; to my github repo showing how I achieved preloading by modifiying the _document file in next.js&lt;/p&gt;

&lt;h3&gt;
  
  
  quicklink
&lt;/h3&gt;

&lt;p&gt;Google also has a package that allows you preload websites within your viewport. It's called &lt;a href="https://github.com/GoogleChromeLabs/quicklink" rel="noopener noreferrer"&gt;quicklink&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Instant.page
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://instant.page" rel="noopener noreferrer"&gt;Instant.page&lt;/a&gt; provides a script that you can include in your website to automatically prefetch your site when a user hovers over the link from anywhere.&lt;br&gt;
NB - It doesn't always work, but when it does, it's great!&lt;/p&gt;
&lt;h3&gt;
  
  
  Do it yourself
&lt;/h3&gt;

&lt;p&gt;Most sites use preloading to load the fonts on their website before a user gets to the site to avoid users seeing a strange font for a split second before seeing the actual font. You can achieve this by using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;link rel="preload" as="font" type="font" href=[link_to_font] /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Possible issues
&lt;/h3&gt;

&lt;p&gt;You may run into issues with preloading if your application isn't &lt;a href="https://www.freecodecamp.org/news/server-side-rendering-your-react-app-in-three-simple-steps-7a82b95db82e/" rel="noopener noreferrer"&gt;server-side rendered&lt;/a&gt; but generally I've found that preloading works most times even if your site is statically rendered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In conclusion, as developers, we are always looking for ways to give users a better experience. Preloading/prefetching resources speeds up time to first contentful paint, time to interactive and gives your users a better experience on your site resulting in higher conversions.&lt;/p&gt;

</description>
      <category>quicklink</category>
      <category>instantpage</category>
      <category>preload</category>
      <category>prefetch</category>
    </item>
  </channel>
</rss>
