<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Lev</title>
    <description>The latest articles on Forem by Lev (@levz0r).</description>
    <link>https://forem.com/levz0r</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%2F146192%2F9ffa251a-b183-43c7-90ff-c0fae1a84d6d.jpg</url>
      <title>Forem: Lev</title>
      <link>https://forem.com/levz0r</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/levz0r"/>
    <language>en</language>
    <item>
      <title>Implementing Google OAuth Authentication in Next.js Using Passport.js and Serverless Functions</title>
      <dc:creator>Lev</dc:creator>
      <pubDate>Sat, 26 Aug 2023 16:00:07 +0000</pubDate>
      <link>https://forem.com/levz0r/implementing-google-oauth-authentication-in-nextjs-using-passportjs-and-serverless-functions-549f</link>
      <guid>https://forem.com/levz0r/implementing-google-oauth-authentication-in-nextjs-using-passportjs-and-serverless-functions-549f</guid>
      <description>&lt;p&gt;User authentication is a critical component of web applications, ensuring security and personalized experiences for users. Google OAuth authentication is a popular choice, allowing users to sign in using their Google accounts. In this tutorial, we’ll explore how to implement Google OAuth authentication in a Next.js application using &lt;a href="https://www.passportjs.org/"&gt;Passport.js&lt;/a&gt;, &lt;a href="https://www.passportjs.org/packages/passport-google-oauth2/"&gt;passport-google-oauth&lt;/a&gt;, &lt;a href="https://www.npmjs.com/package/next-connect"&gt;next-connect&lt;/a&gt;, and serverless functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is OAuth?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/OAuth"&gt;OAuth&lt;/a&gt;, in essence, is a delegated authorization framework. It enables third-party applications to access specific user data hosted by another service provider, such as Google, without requiring the user to reveal their credentials. Instead of sharing passwords, OAuth grants access using access tokens and refresh tokens.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits of OAuth for Authentication
&lt;/h3&gt;

&lt;p&gt;OAuth offers several advantages for implementing user authentication:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Users’ credentials remain confidential, reducing the risk of data breaches due to compromised passwords.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified User Experience&lt;/strong&gt;: OAuth simplifies the user experience by allowing them to grant selective access without sharing their complete account information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorization Granularity&lt;/strong&gt;: Service providers can define scopes that control the level of access granted to third-party applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Password Exposure&lt;/strong&gt;: Third-party applications don’t receive or store user passwords, increasing security.
Single Sign-On (SSO): OAuth supports single sign-on, allowing users to access multiple applications with a single set of credentials.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Google OAuth Integration
&lt;/h3&gt;

&lt;p&gt;One popular use case of OAuth is integrating with Google’s authentication services. Google OAuth enables users to log into applications using their Google accounts, benefiting from Google’s robust security measures and convenient login process.&lt;/p&gt;

&lt;p&gt;Integrating Google OAuth with a Next.js application involves configuring your app as a trusted OAuth client within the Google Developer Console. This enables your app to request access to user data, such as their Google profile information, email, or other specified permissions.&lt;/p&gt;

&lt;p&gt;In the next sections, we’ll delve into the technical steps required to set up Google OAuth authentication in a Next.js application using serverless functions. This approach combines the security of Google’s authentication services with the efficiency and flexibility of serverless functions to create a seamless authentication flow for your users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Serverless Functions in Next.js
&lt;/h2&gt;

&lt;p&gt;In recent years, serverless architecture has gained significant traction in web development due to its scalability, cost-effectiveness, and ease of deployment. Serverless functions, also known as serverless APIs or lambda functions, allow developers to execute code in response to events without managing server infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction to Serverless Functions
&lt;/h3&gt;

&lt;p&gt;Serverless functions are self-contained pieces of code that perform specific tasks and are executed on-demand. They are designed to be stateless, meaning they don’t store data between invocations. Instead, they handle a single request and provide a response. This architecture allows developers to focus on writing code without the overhead of managing server infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Use Serverless Functions?
&lt;/h3&gt;

&lt;p&gt;Serverless functions offer several advantages that make them appealing for various use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: Serverless functions automatically scale based on demand, ensuring optimal performance during traffic spikes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Efficiency&lt;/strong&gt;: You pay only for the resources consumed during function execution, reducing infrastructure costs.
Simplified Deployment: Deploying serverless functions is straightforward, and providers handle deployment details.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster Development&lt;/strong&gt;: Serverless functions encourage modular development and rapid iteration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managed Infrastructure&lt;/strong&gt;: Cloud providers manage server provisioning, scaling, and maintenance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How Serverless Functions Work in Next.js
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt;, a popular &lt;a href="https://react.dev/"&gt;React framework&lt;/a&gt;, offers built-in support for &lt;a href="https://vercel.com/docs/functions/serverless-functions"&gt;serverless functions&lt;/a&gt;. These functions can be created directly within your Next.js project and seamlessly integrated with your application. With serverless functions in Next.js, you can build backend APIs, handle form submissions, and perform various tasks without the need for a dedicated server.&lt;/p&gt;

&lt;p&gt;In a Next.js project, serverless functions are placed in the &lt;code&gt;pages/api&lt;/code&gt; directory. Each file in this directory represents a serverless function that can be invoked via HTTP requests. The serverless functions are automatically deployed along with your Next.js application when you deploy it to platforms like &lt;a href="https://vercel.com/dashboard"&gt;Vercel&lt;/a&gt;, &lt;a href="https://www.netlify.com/"&gt;Netlify&lt;/a&gt;, or other serverless hosting providers.&lt;/p&gt;

&lt;p&gt;In the next section, we’ll dive into the process of setting up Google OAuth authentication in a Next.js application using serverless functions. We’ll combine the power of OAuth with the flexibility of serverless architecture to create a secure and efficient authentication flow for your users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you begin, make sure you have the following set up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js and npm installed&lt;/li&gt;
&lt;li&gt;A Google Developer Console project with OAuth 2.0 credentials&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Set Up Google OAuth Credentials
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Go to the &lt;a href="https://console.developers.google.com/"&gt;Google Developer Console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Create a new project or use an existing one.&lt;/li&gt;
&lt;li&gt;Navigate to “APIs &amp;amp; Services” &amp;gt; “Credentials.”&lt;/li&gt;
&lt;li&gt;Create credentials and select “OAuth 2.0 Client IDs.”
Configure your authorized redirect URIs. In our example: &lt;code&gt;http://localhost:3000/api/account/google/callback&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Install Dependencies
&lt;/h3&gt;

&lt;p&gt;In your Next.js project, install the necessary dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;passport passport-google-oauth next-connect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Implementing the OAuth Login Flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a Login Button&lt;/strong&gt;: In your application’s UI, create a button that users can click to initiate the OAuth login flow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redirect to OAuth Endpoint&lt;/strong&gt;: When the user clicks the login button, redirect them to the OAuth authorization URL you constructed using your OAuth Client ID and the appropriate scope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OAuth Callback Handling&lt;/strong&gt;: After the user grants access and Google redirects back to your application, your serverless function should handle the OAuth callback. Retrieve the authorization code from the query parameters and exchange it for an access token.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store User Data&lt;/strong&gt;: Once you receive the access token, you can use it to request user data from Google. Store relevant user information in your application’s session or database.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By following these steps, you can implement Google OAuth authentication in your Next.js application without using third-party libraries like &lt;code&gt;next-auth&lt;/code&gt;. This approach provides you with full control over the authentication process and allows you to tailor the implementation to your specific needs. While it requires more manual configuration, it offers a deeper understanding of how OAuth works and allows for more customization.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Create OAuth Strategy and Callback Serverless Function
&lt;/h3&gt;

&lt;p&gt;Create an OAuth strategy using &lt;code&gt;passport-google-oauth&lt;/code&gt;. Create a file named &lt;code&gt;pages/api/account/google/[operation].ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextApiRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextApiResponse&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;next&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;createRouter&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;next-connect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;passport&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;passport&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Google&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;IOAuth2StrategyOption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;VerifyFunction&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;passport-google-oauth&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;findUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sendWelcomeEmail&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;@/app/account/actions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dayjs&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;dayjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;duration&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;dayjs/plugin/duration&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;createUserFromProvider&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;../lib&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="kd"&gt;get&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;lodash&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;getSecureCookie&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;@/app/lib/services/cookies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;dayjs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;externalResolver&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="nx"&gt;passport&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="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;OAuth2Strategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;clientID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_GOOGLE_CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GOOGLE_CLIENT_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;callbackURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_GOOGLE_CALLBACK_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;as&lt;/span&gt; &lt;span class="nx"&gt;IOAuth2StrategyOption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;refreshToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;VerifyFunction&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;done&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;span class="nx"&gt;profile&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createRouter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NextApiRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextApiResponse&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;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&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="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;operation&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;passport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;google&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;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;profile&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;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;})(&lt;/span&gt;
      &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;next&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;operation&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;callback&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;passport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;google&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="na"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;user&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;findUser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;profile&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;user&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;createUserFromProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;identityProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GOOGLE&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="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendWelcomeEmail&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identityProvider&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GOOGLE&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/?error=exists_with_different_identity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cookie&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;getSecureCookie&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;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;photos[0].value&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="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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;displayName&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="p"&gt;),&lt;/span&gt;
            &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;!&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="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;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Set-Cookie&lt;/span&gt;&lt;span class="dl"&gt;"&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;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="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unknown 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="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;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&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;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;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;The provided code is an implementation of Google OAuth authentication using Passport.js, Next.js API routes, and some custom logic for user management. Let’s break down the code step by step:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Import Statements&lt;/strong&gt;: The code starts by importing various modules and functions required for the authentication process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Passport Configuration&lt;/strong&gt;: The &lt;code&gt;passport.use&lt;/code&gt; function configures the Google OAuth2Strategy with the provided client ID, client secret, and callback URL. This strategy handles the authentication process with Google.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Router Setup&lt;/strong&gt;: The &lt;code&gt;createRouter&lt;/code&gt; function from &lt;code&gt;next-connect&lt;/code&gt; is used to create an API route handler (&lt;code&gt;router&lt;/code&gt;). The router will handle different operations based on the query parameter named &lt;code&gt;operation&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google OAuth Flow&lt;/strong&gt;: If the &lt;code&gt;operation&lt;/code&gt; is set to &lt;code&gt;"auth"&lt;/code&gt;, it triggers the Google OAuth authentication process. The user will be redirected to Google's login page to authenticate their account. If the &lt;code&gt;operation&lt;/code&gt; is set to &lt;code&gt;"callback"&lt;/code&gt;, it handles the callback after the user logs in with their Google account. The &lt;code&gt;passport.authenticate&lt;/code&gt; function is used to authenticate the user based on the Google OAuth strategy. If the authentication is successful (&lt;code&gt;profile&lt;/code&gt; contains user information), it checks if the user already exists in the local database based on their email. If the user does not exist, a new user is created using the &lt;code&gt;createUserFromProvider&lt;/code&gt; function. A welcome email is also sent to the new user asynchronously. If the user exists but has a different identity provider (not Google), the user is redirected with an error message. If the user exists and has Google as their identity provider, a secure cookie containing user information is generated and set in the response header. The user is then redirected to the profile page. Finally, if there is an error during authentication, a 500 status code is returned.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling&lt;/strong&gt;: The &lt;code&gt;onError&lt;/code&gt; handler is defined to handle any errors that might occur during the authentication process. If an error occurs, it logs the error and sends an appropriate response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export and Handling&lt;/strong&gt;: The &lt;code&gt;router.handler&lt;/code&gt; function is used to export the API route handler. This handler will be invoked when a request is made to the specified API route.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In summary, the code follows the Google OAuth authentication flow using Passport.js, fetches user information, checks for existing users, and handles the creation of new users or redirects based on the user’s identity. It also takes care of error handling and response generation throughout the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Securing and Managing User Sessions
&lt;/h2&gt;

&lt;p&gt;User sessions play a crucial role in maintaining a seamless and secure authentication experience in your Next.js application. By properly securing and managing user sessions, you can ensure that users stay authenticated during their interactions with your platform.&lt;/p&gt;

&lt;h3&gt;
  
  
  Storing and Managing User Sessions
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Session Tokens or Cookies&lt;/strong&gt;: Choose between using session tokens or HTTP cookies to manage user sessions. Session tokens are often stored in memory or databases, while cookies are stored in the user’s browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token-Based Approach&lt;/strong&gt;: If you opt for a token-based approach, generate a unique session token for each authenticated user. This token can be stored in the browser’s &lt;code&gt;localStorage&lt;/code&gt; or &lt;code&gt;sessionStorage&lt;/code&gt; and sent with each request to identify the user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cookie-Based Approach&lt;/strong&gt;: With a cookie-based approach, set an HTTP cookie containing the user’s session ID. Cookies are automatically included in subsequent requests, allowing your serverless functions to identify the user.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Using Cookies or Tokens for Session Management
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cookies&lt;/strong&gt;: Cookies provide built-in security features, such as the &lt;a href="https://web.dev/samesite-cookies-explained/"&gt;SameSite&lt;/a&gt; attribute, which helps prevent cross-site request forgery (CSRF) attacks. However, cookies have size limitations, and users can disable them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tokens&lt;/strong&gt;: Using tokens allows you to implement additional security measures, such as token expiration and refresh tokens. However, token-based approaches require additional development to handle token expiration and renewal.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Enhancing User Experience
&lt;/h2&gt;

&lt;p&gt;A seamless user experience is essential in any authentication flow. Enhancements such as handling loading states, managing errors, and personalizing user profiles can greatly impact how users perceive your application’s authentication process. In this section, we’ll explore ways to enhance the user experience during and after the Google OAuth authentication process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling Loading States and Errors
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Loading States&lt;/strong&gt;: During the authentication flow, users may experience brief waiting periods. Implement loading indicators to inform users that their request is being processed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling&lt;/strong&gt;: Anticipate and handle errors gracefully. Display user-friendly error messages for scenarios like invalid credentials, network issues, or server errors.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Redirecting After Successful Authentication
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Redirecting to the Home Page&lt;/strong&gt;: After a user successfully authenticates via Google OAuth, redirect them to the appropriate page in your application. Typically, this is the home page or a dashboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personalized Greetings&lt;/strong&gt;: Consider displaying a personalized greeting message on the redirected page to enhance the user experience. Welcome the user by their name, retrieved from the user data fetched during the OAuth process.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Personalizing User Profiles
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fetching User Data&lt;/strong&gt;: After authentication, retrieve additional user data using the access token. Fetching data such as user profile images, names, and email addresses can help you create a more personalized experience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Displaying User Data&lt;/strong&gt;: Display fetched user data on the user’s profile page or within the application interface. Personalized content reinforces the value of authentication and demonstrates your application’s user-centric approach.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  User Preferences and Customization
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Allowing User Preferences&lt;/strong&gt;: Give users the option to customize their experience by providing settings or preferences. For example, users might choose their preferred language or theme.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consent Management&lt;/strong&gt;: If your application requires additional permissions beyond basic user information, clearly explain the purpose of each permission and allow users to grant or revoke consent.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Enhancing the user experience during Google OAuth authentication involves thoughtful design and attention to detail. By addressing loading states, errors, and personalization, you create a user-friendly atmosphere that encourages engagement and fosters trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verification Process for Production
&lt;/h2&gt;

&lt;p&gt;When you’re ready to take your application live and allow users to authenticate using Google OAuth, you must go through Google’s verification process. This process involves submitting your application for review by Google to ensure it complies with their policies and security standards. Verification is essential to prevent unauthorized use of user data and ensure a secure experience for your users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9aasnpdenqx6gb6yqoq3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9aasnpdenqx6gb6yqoq3.png" alt="Unverified application screen." width="499" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once your application successfully passes the verification process, you’ll receive the green light to use Google OAuth in production. This step is crucial to provide a secure and reliable authentication experience to your users.&lt;/p&gt;

&lt;p&gt;As you navigate the verification process and transition your application to a production environment, the benefits of Google OAuth and serverless functions will continue to provide a solid foundation for secure user authentication. Remember that the journey doesn’t end here; continuous monitoring, updates, and enhancements will ensure a seamless and trustworthy experience for your application’s users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Implementing Google OAuth authentication in your Next.js application using serverless functions is a powerful way to create a secure and user-friendly authentication process. In this guide, we’ve covered the entire journey, from understanding OAuth authentication to deploying your application with enhanced security and user experience. Let’s recap the benefits and next steps:&lt;/p&gt;

&lt;p&gt;Recap of the Process&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Understanding OAuth&lt;/strong&gt;: You’ve gained a deep understanding of OAuth and its advantages for secure user authentication without exposing credentials.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serverless Functions in Next.js&lt;/strong&gt;: You’ve explored how serverless functions work in Next.js and why they are an excellent choice for implementing authentication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Setting Up Google OAuth&lt;/strong&gt;: You’ve learned how to create a Google Developer Project, configure OAuth credentials, and integrate OAuth with your Next.js application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creating Serverless Functions&lt;/strong&gt;: You’ve created serverless functions to handle OAuth callbacks, exchange authorization codes for tokens, and fetch user data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Securing User Sessions&lt;/strong&gt;: You’ve understood the importance of secure session management and implemented measures to ensure user privacy and authorized access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhancing User Experience&lt;/strong&gt;: You’ve discovered how to handle loading states, errors, and personalize user profiles, creating a smooth and engaging authentication flow.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By following the steps outlined in this guide, you’ve successfully implemented Google OAuth authentication in your Next.js application using serverless functions. This accomplishment not only enhances security but also contributes to a user-centric experience that fosters trust and engagement. As you continue to refine and expand your application, you’ll create a robust platform that users can rely on for secure and seamless authentication.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>oauth</category>
      <category>webdev</category>
      <category>google</category>
    </item>
    <item>
      <title>Leveraging RxJS for Seamless Communication Between Components in Different Layouts in Next.js</title>
      <dc:creator>Lev</dc:creator>
      <pubDate>Tue, 08 Aug 2023 15:04:18 +0000</pubDate>
      <link>https://forem.com/levz0r/leveraging-rxjs-for-seamless-communication-between-components-in-different-layouts-in-nextjs-4c9d</link>
      <guid>https://forem.com/levz0r/leveraging-rxjs-for-seamless-communication-between-components-in-different-layouts-in-nextjs-4c9d</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx45q1d5lbga7wwip3ycp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx45q1d5lbga7wwip3ycp.png" alt="" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt;, a popular React framework for building web applications, one of the challenges developers often face is efficiently communicating between components residing in different layouts. While Next.js provides a &lt;a href="https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts"&gt;well-organized&lt;/a&gt; structure for managing components within layouts, it doesn’t inherently offer a direct communication mechanism between them. In this blog post, we’ll explore how to address this issue using RxJS, a powerful library for reactive programming, to establish seamless communication between components from different layouts in Next.js.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding RxJS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://rxjs.dev/"&gt;RxJS&lt;/a&gt; (Reactive Extensions for JavaScript) is a library for composing asynchronous and event-based programs using observable sequences. It enables the creation and manipulation of observables, which are sequences of data or events over time. Observables can be used to handle events, manage asynchronous operations, and streamline communication between different parts of your application.&lt;/p&gt;

&lt;p&gt;In Angular, RxJS plays a crucial role in handling asynchronous operations, managing data streams, and facilitating communication between different parts of the application. It provides powerful tools such as observables, operators, and subjects that enable developers to work with reactive programming paradigms seamlessly.&lt;/p&gt;

&lt;p&gt;Here are the key points to consider regarding RxJS:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Observables:&lt;/strong&gt; RxJS revolves around the concept of &lt;a href="https://rxjs.dev/guide/observable"&gt;Observables&lt;/a&gt;, which are streams of data or events over time. Observables can represent a single value, multiple values, or even asynchronous data streams. They provide a powerful foundation for handling asynchronous operations and data manipulation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operators:&lt;/strong&gt; RxJS offers a rich set of &lt;a href="https://rxjs.dev/guide/operators"&gt;operators&lt;/a&gt; that enable developers to &lt;a href="https://rxjs.dev/guide/operators#transformation-operators"&gt;transform&lt;/a&gt;, &lt;a href="https://rxjs.dev/guide/operators#filtering-operators"&gt;filter&lt;/a&gt;, &lt;a href="https://rxjs.dev/guide/operators#join-operators"&gt;combine&lt;/a&gt;, and manipulate data streams efficiently. These operators allow for the implementation of complex asynchronous workflows with concise and readable code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reactive Programming:&lt;/strong&gt; RxJS embraces reactive programming principles, where applications respond to changes and events as they occur, rather than waiting for explicit calls or updates. This approach leads to more responsive and interactive applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event Handling:&lt;/strong&gt; RxJS provides a streamlined approach to handle various events, whether they are user interactions, API responses, or other asynchronous operations. Observables can be used to manage events and react accordingly, simplifying event-driven programming.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Management:&lt;/strong&gt; With RxJS, developers can easily manage data flows within their applications. It offers a standardized way to handle data from various sources and provides a unified mechanism for data transformation and synchronization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling:&lt;/strong&gt; RxJS provides error handling mechanisms that allow developers to handle errors gracefully within observable streams. This ensures that errors are properly managed without causing application crashes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backpressure and Memory Management:&lt;/strong&gt; RxJS includes backpressure and memory management strategies to handle scenarios where the rate of data production exceeds the rate of data consumption. This helps prevent memory leaks and ensures optimal performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multicasting:&lt;/strong&gt; RxJS supports &lt;a href="https://www.learnrxjs.io/learn-rxjs/operators/multicasting"&gt;multicasting&lt;/a&gt;, which allows multiple subscribers to share a single source of data. This is particularly useful when dealing with shared resources or when you want to optimize data processing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing:&lt;/strong&gt; RxJS code is highly testable due to its functional and declarative nature. Testing observables and operators is &lt;a href="https://rxjs.dev/guide/testing/marble-testing"&gt;straightforward&lt;/a&gt;, making it easier to create robust and reliable unit tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Framework Compatibility:&lt;/strong&gt; RxJS is not limited to a specific framework and can be used in various environments, including Angular, React, Vue.js, Node.js, and more. This makes it a versatile tool for handling reactive programming in different projects.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;RxJS is just one of the many solutions available for handling asynchronous and event-based programming. Depending on the specific use case and project requirements, developers may consider alternative libraries and tools. Here are some examples of other solutions available:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Redux and Redux-Observable:&lt;/strong&gt; &lt;a href="https://redux.js.org/"&gt;Redux&lt;/a&gt; is a predictable state management library commonly used with React applications. It helps manage the state of an application in a single, centralized store, making it easier to track changes and maintain a unidirectional data flow. &lt;a href="https://redux-observable.js.org/"&gt;Redux-Observable&lt;/a&gt; is an extension to Redux that leverages RxJS to handle asynchronous side effects and manage complex data flows. It allows developers to express side effects as observable streams, enabling seamless integration with Redux.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React’s Context API and useContext Hook:&lt;/strong&gt; &lt;a href="https://react.dev/learn/passing-data-deeply-with-context"&gt;React’s Context API&lt;/a&gt; provides a way to pass data through the component tree without having to pass props down manually at every level. It enables components to subscribe to changes in the context and react accordingly when the data changes. The &lt;a href="https://react.dev/reference/react/useContext"&gt;useContext&lt;/a&gt; hook allows functional components to consume context values directly without using higher-order components or render props.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EventEmitter:&lt;/strong&gt; &lt;a href="https://nodejs.dev/en/learn/the-nodejs-event-emitter/"&gt;EventEmitter&lt;/a&gt; is a core module in Node.js that provides an implementation of the Observer pattern. It allows developers to create custom event emitters and listeners to facilitate communication between different parts of a Node.js application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pub-Sub Libraries:&lt;/strong&gt; There are various pub-sub libraries available, such as &lt;a href="https://sahadar.github.io/pubsub/"&gt;PubSubJS&lt;/a&gt; and nano-pubsub, which implement the Publish-Subscribe pattern. These libraries allow components or modules to publish messages (events) to specific channels, and other components can subscribe to those channels to receive the messages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MobX:&lt;/strong&gt; &lt;a href="https://mobx.js.org/"&gt;MobX&lt;/a&gt; is another state management library that follows the reactive programming paradigm. It automatically tracks observable data and updates components that depend on it, making it a suitable choice for handling reactivity and data synchronization.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Scenario
&lt;/h2&gt;

&lt;p&gt;Consider a typical scenario in Next.js where you have two components, &lt;code&gt;ComponentA&lt;/code&gt; and &lt;code&gt;ComponentB&lt;/code&gt;, residing in separate layouts, say &lt;code&gt;LayoutA&lt;/code&gt; and &lt;code&gt;LayoutB&lt;/code&gt;. The goal is to enable &lt;code&gt;ComponentA&lt;/code&gt; to communicate with &lt;code&gt;ComponentB&lt;/code&gt;, even though they are in different layouts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Project
&lt;/h2&gt;

&lt;p&gt;Before diving into the implementation, ensure you have a Next.js project set up. If you haven’t done that, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install Next.js globally: &lt;code&gt;npm install -g create-next-app&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create a new Next.js project: &lt;code&gt;npx create-next-app my-nextjs-app&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to the project directory: &lt;code&gt;cd my-nextjs-app&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Implementing Communication with RxJS
&lt;/h2&gt;

&lt;p&gt;Step 1: Install RxJS in your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;rxjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 2: Create a custom event bus using RxJS in a new file, &lt;code&gt;eventBus.js&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;Subject&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;rxjs&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;eventBus&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;Subject&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;eventBus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 3: In &lt;code&gt;ComponentA&lt;/code&gt;, publish an event using the &lt;code&gt;eventBus&lt;/code&gt; when a specific action occurs:&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;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;eventBus&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;../path/to/eventBus&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;ComponentA&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="nx"&gt;handleClick&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="nx"&gt;eventBus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&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="s1"&gt;ACTION_FROM_COMPONENT_A&lt;/span&gt;&lt;span class="dl"&gt;'&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello from Component A!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;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;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;button&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="nx"&gt;handleClick&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;Trigger&lt;/span&gt; &lt;span class="nx"&gt;Action&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;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="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;ComponentA&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 4: In &lt;code&gt;ComponentB&lt;/code&gt;, subscribe to the &lt;code&gt;eventBus&lt;/code&gt; and handle the event accordingly:&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;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;eventBus&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;../path/to/eventBus&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;ComponentB&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;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="nf"&gt;useEffect&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;subscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;eventBus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ACTION_FROM_COMPONENT_A&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="nf"&gt;setMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;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;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unsubscribe&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="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;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;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Received&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;message&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="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;ComponentB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 5: Include &lt;code&gt;ComponentA&lt;/code&gt; in &lt;code&gt;LayoutA&lt;/code&gt; and &lt;code&gt;ComponentB&lt;/code&gt; in &lt;code&gt;LayoutB&lt;/code&gt;. Then, utilize the layouts in your system as needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By using RxJS as an event bus, we’ve successfully established communication between &lt;code&gt;ComponentA&lt;/code&gt; and &lt;code&gt;ComponentB&lt;/code&gt;, even though they reside in different layouts within our Next.js application. RxJS's reactive nature allows us to handle events seamlessly and makes our application more organized and maintainable.&lt;/p&gt;

&lt;p&gt;Remember to handle memory management properly by unsubscribing from subscriptions when components are unmounted to prevent memory leaks. With this approach, you can extend communication across multiple components and layouts efficiently, providing a smooth and interactive user experience in your Next.js applications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6v0ss1s46d4aoci3au82.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6v0ss1s46d4aoci3au82.png" alt="Remember to handle memory management properly!&amp;lt;br&amp;gt;
" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While RxJS is a powerful and widely-used solution for reactive programming and asynchronous data handling, developers have a range of other options available to address specific needs in their projects. Choosing the right solution depends on factors such as the project’s complexity, the framework being used, and the specific requirements for data synchronization and event handling. By exploring various libraries and tools, developers can find the best fit for their applications and deliver efficient and responsive user experiences.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>rxjs</category>
      <category>communication</category>
      <category>react</category>
    </item>
    <item>
      <title>How to Make Charts SEO-Friendly?</title>
      <dc:creator>Lev</dc:creator>
      <pubDate>Sun, 05 Jan 2020 15:29:54 +0000</pubDate>
      <link>https://forem.com/levz0r/how-to-make-charts-seo-friendly-1j72</link>
      <guid>https://forem.com/levz0r/how-to-make-charts-seo-friendly-1j72</guid>
      <description>&lt;p&gt;Hi,&lt;/p&gt;

&lt;p&gt;I was breaking my head trying to understand what is the best way to structurize the data behind the following chart so it would be more "understandable" to search engines.&lt;br&gt;
I checked most of the entities in &lt;a href="https://schema.org/"&gt;https://schema.org/&lt;/a&gt; but still not sure what are the best to choose.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bw0o22H6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/fzj1mp88qohrhicxln9q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bw0o22H6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/fzj1mp88qohrhicxln9q.png" alt="Alt Text" width="509" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any help will be appreciated!&lt;/p&gt;

&lt;p&gt;Thanks!&lt;/p&gt;

</description>
      <category>seo</category>
      <category>charts</category>
      <category>data</category>
      <category>help</category>
    </item>
    <item>
      <title>Let’s build a domain parking page with real-time analytics</title>
      <dc:creator>Lev</dc:creator>
      <pubDate>Tue, 14 May 2019 12:20:31 +0000</pubDate>
      <link>https://forem.com/levz0r/let-s-build-a-domain-parking-page-with-real-time-analytics-2l0</link>
      <guid>https://forem.com/levz0r/let-s-build-a-domain-parking-page-with-real-time-analytics-2l0</guid>
      <description>&lt;p&gt;It is true that most of the known domain registrars offer a domain parking service. GoDaddy even lets you earn cash with their &lt;a href="https://www.godaddy.com/domains/cashparking" rel="noopener noreferrer"&gt;CashParking&lt;/a&gt; solution, but alas, this service will cost $3.99 per month for the basic plan and you better be sure your domain(s) will generate enough traffic to cover this expense.&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%2F800%2F1%2A7SOXr-TZSTwHXFnr7oFQbQ.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%2F800%2F1%2A7SOXr-TZSTwHXFnr7oFQbQ.png"&gt;&lt;/a&gt;&lt;/p&gt;
A screenshot from CashParking (Taken on April 20th, 2019)



&lt;p&gt;Of course, alternatives do exist. We can park our domain for “free” by advertising the registrar. Here’s how the free domain parking looks like on GoDaddy:&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%2F800%2F1%2AJNwDNUYt7sRzvhsnVS0NPA.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%2F800%2F1%2AJNwDNUYt7sRzvhsnVS0NPA.png"&gt;&lt;/a&gt;&lt;/p&gt;
A screenshot of one of my domains, parked at GoDaddy



&lt;p&gt;Namecheap isn’t an exception. Here’s how their free domain parking page looks like:&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%2F800%2F1%2A6Ymmt6v40Qv9DpXAXGM0bA.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%2F800%2F1%2A6Ymmt6v40Qv9DpXAXGM0bA.png"&gt;&lt;/a&gt;&lt;/p&gt;
http://parkingpage.namecheap.com/



&lt;p&gt;Now don’t get me wrong — The registrars don’t have to park our domains. Their main business is to register domains and manage DNS. Therefore, it seems there is no simple and free solution for someone who wants to evaluate the usage of a domain name, while the website is under construction.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;Amazon Simple Storage Service&lt;/a&gt; to the rescue
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Full disclosure: I am a big fan of Amazon Web Services.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Because every file (object) stored at Simple Storage Service (S3) has a unique URL, we can &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteHosting.html" rel="noopener noreferrer"&gt;host&lt;/a&gt; a website on it. What is great about this service, is that it will cost us nothing (or approximately so): As part of the &lt;a href="https://aws.amazon.com/free/" rel="noopener noreferrer"&gt;AWS free usage tier&lt;/a&gt;, Amazon gives us 20,000 GET requests a free of charge and the next 1,000 requests for as low as $0.0004 (as of writing this story). Let’s do the math:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If our domain name is a blast and we have 100,000 monthly visitors, we will pay just as little as (100,000–20,000)/1000*0.0004=$0.032&lt;/li&gt;
&lt;li&gt;For the average domain name, we will pay nothing…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s how Amazon describes S3:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[…] Amazon S3 is designed for 99.999999999% (11 9’s) of durability, and stores data for millions of applications for companies all around the world.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Back to domain parking… In order to park our domain at S3, all we have to do is to build a static HTML webpage with some descriptive text (e.g “under construction” or “coming soon”), our domain name written in big and bold letters and initiate &lt;a href="https://analytics.google.com/" rel="noopener noreferrer"&gt;Google Analytics&lt;/a&gt; script for websites. When the webpage will be uploaded to an S3 Bucket (analogous to a directory on your OS) and having Static Website Hosting enabled, our bucket will have a unique URL, which will look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.domain.com.s3-website-us-east-1.amazonaws.com" rel="noopener noreferrer"&gt;http://&lt;strong&gt;www.domain.com&lt;/strong&gt;.s3-website-us-east-1.amazonaws.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before our parking page will go live, there is another optional (but suggested) step we need to perform: Create a &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html" rel="noopener noreferrer"&gt;CORS configuration&lt;/a&gt; for our bucket. Please read the &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html#example-scenarios-cors" rel="noopener noreferrer"&gt;use cases&lt;/a&gt; for which CORS is mandatory.&lt;/p&gt;

&lt;p&gt;After finishing with configuration, we would create a &lt;a href="https://en.wikipedia.org/wiki/CNAME_record" rel="noopener noreferrer"&gt;CNAME&lt;/a&gt; record which origin is &lt;a href="http://www.domain.com" rel="noopener noreferrer"&gt;www.domain.com&lt;/a&gt; and which will be mapped to the S3 URL. &lt;strong&gt;It is important to notice that, S3 requires the name of the full domain (including subdomain) to match the bucket name, or otherwise, the solution won’t work.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Parking page creation automation
&lt;/h3&gt;

&lt;p&gt;After we figured out how to have a parking page on S3, we will now automate the process, to be able to create a new parking page automatically by doing as few operations as possible.&lt;/p&gt;

&lt;p&gt;For this, I have created a boilerplate &lt;a href="https://github.com/levz0r/s3-domain-parking" rel="noopener noreferrer"&gt;project&lt;/a&gt; which will let you configure your domain name, as well as the descriptive text (see picture below) and connect your Google Analytics account to the landing page, so you won’t miss any visitor.&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%2F800%2F1%2AhbvvjcYJeATxZhxZDe70gQ.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%2F800%2F1%2AhbvvjcYJeATxZhxZDe70gQ.png"&gt;&lt;/a&gt;&lt;/p&gt;
This is how your parking page is going to look like…



&lt;p&gt;In order to automate the process, we need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download and extract the parking webpage from the boilerplate &lt;a href="https://github.com/levz0r/s3-domain-parking/archive/master.zip" rel="noopener noreferrer"&gt;zip&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Open a free AWS account &lt;a href="https://aws.amazon.com/free/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. During the process, Amazon will ask you for a credit card number. But as long as we will have less than 20,000 visitors per month, it won’t cost us a dime &lt;em&gt;[Disclaimer: The author is not responsible for any charges you may receive from Amazon and/or their subsidiaries]&lt;/em&gt;;&lt;/li&gt;
&lt;li&gt;Create an IAM user with &lt;strong&gt;programmatic access&lt;/strong&gt; and the following permissions: &lt;code&gt;s3:PutObject&lt;/code&gt;, &lt;code&gt;s3:PutBucketWebsite&lt;/code&gt;, &lt;code&gt;s3:GetBucketWebsite&lt;/code&gt;, &lt;code&gt;s3:GetBucketCORS&lt;/code&gt;, &lt;code&gt;s3:PutBucketAcl&lt;/code&gt;, &lt;code&gt;s3:CreateBucket&lt;/code&gt;, &lt;code&gt;s3:ListBucket&lt;/code&gt;, &lt;code&gt;s3:PutBucketCORS&lt;/code&gt;, &lt;code&gt;s3:GetBucketAcl&lt;/code&gt;, &lt;code&gt;s3:HeadBucket&lt;/code&gt;, &lt;code&gt;s3:PutObjectAcl&lt;/code&gt;. If you’re unsure how to do that, head to &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html" rel="noopener noreferrer"&gt;Creating an IAM User in Your AWS Account&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/using-with-s3-actions.html" rel="noopener noreferrer"&gt;Specifying Permissions in a Policy&lt;/a&gt;. Once the user is created, write down the Access Key ID and the Secret key — we will need them for the configuration step;&lt;/li&gt;
&lt;li&gt;Download and install &lt;a href="https://nodejs.org/en/download/" rel="noopener noreferrer"&gt;Node.js 10 and npm&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;Optional: Create a Google Analytics account and write down your Tracking ID (&lt;a href="https://support.google.com/analytics/answer/7476135?hl=en#choosetracking" rel="noopener noreferrer"&gt;instructions&lt;/a&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;p&gt;Now open the terminal and go to the extracted directory containing the boilerplate from step 1. There you will find a &lt;code&gt;config.json&lt;/code&gt; file. With your favorite text editor, make the following modifications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;trackingId&lt;/code&gt;: Google Analytics Tracking ID (optional).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;domainName&lt;/code&gt;: Domain name. It will appear in big bold text as shown in the screenshot above. Please use the top-level domain name (without ‘www.’).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;innerHtml&lt;/code&gt;: The HTML which will appear under the domain name. In the screenshot above, it was set to: “Coming soon…”.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;accessKeyId&lt;/code&gt;: The AWS Access Key ID from step 3.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;secretAccessKey&lt;/code&gt;: The Secret Key from step 3.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;awsRegion&lt;/code&gt;: AWS default region. We can leave it ‘us-east-1’.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;showRibbon&lt;/code&gt;: If you want to support the project and make the ‘Fork me on GitHub’ ribbon visible on the left corner of the landing page, set the field value to true, otherwise set it to false.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’re almost done. All we have to do now is to execute to commands:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm run deploy&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first command can be run once. For further deployments — modify &lt;code&gt;config.json&lt;/code&gt; and run only the second command.&lt;/p&gt;

&lt;p&gt;Follow the progress on the screen. If everything went smooth, the last line on your terminal should be: “&lt;em&gt;Please create a new record: CNAME (&lt;a href="http://www.your-domain" rel="noopener noreferrer"&gt;www.your-domain&lt;/a&gt;) -&amp;gt;&lt;a href="http://www.(your-domain).s3-website-us-east-1.amazonaws.com" rel="noopener noreferrer"&gt;www.(your-domain).s3-website-us-east-1.amazonaws.com&lt;/a&gt;&lt;/em&gt;”. But before creating, go to &lt;em&gt;&lt;a href="http://www.(your-domain).s3-website-us-east-1.amazonaws.com" rel="noopener noreferrer"&gt;www.(your-domain).s3-website-us-east-1.amazonaws.com&lt;/a&gt;&lt;/em&gt; and verify your landing page is accessible. If you set Google Analytics Tracking ID, verify that there is at least one active user on the site.&lt;/p&gt;

&lt;p&gt;Last step: Update the CNAME record using your DNS provider. Give them some time to propagate the change, then verify your domain now has a landing page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parking your top-level domain
&lt;/h3&gt;

&lt;p&gt;Unfortunately, S3 website hosting can’t work with top-level domains. However, using our DNS provider, we can redirect the top-level domain to the www subdomain which is parked on S3. Here are &lt;a href="https://www.namecheap.com/support/knowledgebase/article.aspx/385/2237/how-to-redirect-a-url-for-a-domain" rel="noopener noreferrer"&gt;instructions&lt;/a&gt; for creating a domain redirection using Namecheap.&lt;/p&gt;

&lt;h3&gt;
  
  
  Geek alert! What’s under the hood?
&lt;/h3&gt;

&lt;p&gt;The project consists of two parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The web framework.&lt;/li&gt;
&lt;li&gt;The AWS deployment script.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the web framework, Angular 7 was chosen. But why use Angular for a single HTML file? The answer is plain and simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility:&lt;/strong&gt; While one needs a single static page, someone might have additional needs (contact form, advertisements, animations, etc.). Angular lets us easily customize our web application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personal preference:&lt;/strong&gt; I work with it on a daily basis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; As we will see shortly, there is no noticeable difference in performance and loading times between Angular and the existing parking pages served by GoDaddy and Namecheap.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In order to report to Google Analytics using a dynamic (configurable) Tracking ID, &lt;a href="https://github.com/angulartics/angulartics2" rel="noopener noreferrer"&gt;angulartics2&lt;/a&gt; was the choice which at the first glance might seem like overkill, but again — I wanted to give flexibility. Mixpanel in addition, or instead of, Google Analytics? Change just a few lines of code and we’re all set.&lt;/p&gt;

&lt;p&gt;For the AWS deployment script, there are only two external dependencies: &lt;a href="https://www.npmjs.com/package/aws-sdk" rel="noopener noreferrer"&gt;aws-sdk &lt;/a&gt; and mime. Using the AWS SDK, we create/update the S3 bucket, the CORS configuration, and upload the generated Angular web app to the S3 bucket. It is important to resolve each file’s mime type, or otherwise, our landing page won’t be accessible to browsers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benchmark
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://developers.google.com/speed/pagespeed/insights/" rel="noopener noreferrer"&gt;PageSpeed Insights&lt;/a&gt; was used to measure performance.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Namecheap/desktop:&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%2Fcdn-images-1.medium.com%2Fmax%2F800%2F1%2AfRWtL8gPOGpg4-584Zomjg.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%2F800%2F1%2AfRWtL8gPOGpg4-584Zomjg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Namecheap/mobile:&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%2Fcdn-images-1.medium.com%2Fmax%2F800%2F1%2A_1sB3DUIwseD5rAj8bRjIg.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%2F800%2F1%2A_1sB3DUIwseD5rAj8bRjIg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GoDaddy/desktop:&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%2Fcdn-images-1.medium.com%2Fmax%2F800%2F1%2AypaqEX9tAmoPmQL8mgfpdw.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%2F800%2F1%2AypaqEX9tAmoPmQL8mgfpdw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GoDaddy/mobile:&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%2Fcdn-images-1.medium.com%2Fmax%2F800%2F1%2AVcqnUo5X4eGeW-aeiC4ffQ.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%2F800%2F1%2AVcqnUo5X4eGeW-aeiC4ffQ.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;S3 Hosting/desktop:&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%2Fcdn-images-1.medium.com%2Fmax%2F800%2F1%2Apn9kdlXz0CN2uvUN2m6ELQ.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%2F800%2F1%2Apn9kdlXz0CN2uvUN2m6ELQ.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;S3 Hosting/mobile:&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%2Fcdn-images-1.medium.com%2Fmax%2F800%2F1%2ArWxQst-oPx0H0Wd3FtD0Pw.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%2F800%2F1%2ArWxQst-oPx0H0Wd3FtD0Pw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To summarize the comparison of PageSpeed Insights, we can clearly see who the winner is on desktop browsers… On mobile, GoDaddy is just 2 points above S3. Namecheap was, well… Cheap in all tests. This makes S3 hosting both feasible and even preferable to the alternatives we’ve discussed earlier.&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%2F800%2F1%2A6Kxu9M9QbDUGtk-t4oJATg.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%2F800%2F1%2A6Kxu9M9QbDUGtk-t4oJATg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now go and enjoy your free domain parking! 😍&lt;/p&gt;

&lt;p&gt;For your convenience, here is the project repository:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/levz0r" rel="noopener noreferrer"&gt;
        levz0r
      &lt;/a&gt; / &lt;a href="https://github.com/levz0r/s3-domain-parking" rel="noopener noreferrer"&gt;
        s3-domain-parking
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Configurable lightweight domain parking page with Google Analytics support. 
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;AWS S3 Domain Parking&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/f7621c60d74a2ced2df48f8ac206f787885c33c4e2fa3068f7e86e168ac35881/68747470733a2f2f692e6962622e636f2f6b34765435484d2f73637265656e636170747572652d6c6f63616c686f73742d343230312d323031392d30342d31392d32312d31342d35372d6d6163626f6f6b676f6c642d66726f6e742e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/f7621c60d74a2ced2df48f8ac206f787885c33c4e2fa3068f7e86e168ac35881/68747470733a2f2f692e6962622e636f2f6b34765435484d2f73637265656e636170747572652d6c6f63616c686f73742d343230312d323031392d30342d31392d32312d31342d35372d6d6163626f6f6b676f6c642d66726f6e742e706e67" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This project creates an S3 bucket that is configured to host a static website with CORS enabled. All you have to do is to configure the project and create a CNAME record in your domain.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Prerequisites&lt;/h2&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/free/" rel="nofollow noopener noreferrer"&gt;AWS free tier account&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html" rel="nofollow noopener noreferrer"&gt;Programmatic access to AWS account&lt;/a&gt; with the following &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access_controlling.html" rel="nofollow noopener noreferrer"&gt;permissions&lt;/a&gt;: &lt;code&gt;s3:PutObject&lt;/code&gt;, &lt;code&gt;s3:PutBucketWebsite&lt;/code&gt;, &lt;code&gt;s3:GetBucketWebsite&lt;/code&gt;, &lt;code&gt;s3:GetBucketCORS&lt;/code&gt;, &lt;code&gt;s3:PutBucketAcl&lt;/code&gt;, &lt;code&gt;s3:CreateBucket&lt;/code&gt;, &lt;code&gt;s3:ListBucket&lt;/code&gt;, &lt;code&gt;s3:PutBucketCORS&lt;/code&gt;, &lt;code&gt;s3:GetBucketAcl&lt;/code&gt;, &lt;code&gt;s3:HeadBucket&lt;/code&gt;, &lt;code&gt;s3:PutObjectAcl&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Access to Domain management (Namecheap, GoDaddy, etc.);&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://analytics.google.com" rel="nofollow noopener noreferrer"&gt;Google Analytics&lt;/a&gt; account (optional).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/download/" rel="nofollow noopener noreferrer"&gt;Node.js and npm&lt;/a&gt; installed.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Limitations&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Because of the way S3 static website hosting works, it is not possible to create a CNAME record of the top level of your domain. Therefore, you can park &lt;code&gt;(subdomain).domain.com&lt;/code&gt; (e.g &lt;code&gt;www.domain.com&lt;/code&gt;). Having said that, some DNS providers (Namecheap) allow redirecting the top level domain to a subdomain.
&lt;em&gt;See question &lt;a href="https://serverfault.com/questions/410727/point-s3-bucket-to-top-level-domain" rel="nofollow noopener noreferrer"&gt;here&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Configuration&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Before your parking…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/levz0r/s3-domain-parking" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Thank you for reading! As always, I am happy to receive your feedback and suggestions! If you liked what you’ve read, please share this story with your friends!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;-Lev&lt;/em&gt;&lt;/p&gt;

&lt;h6&gt;
  
  
  This article was originally posted on &lt;a href="https://medium.com/@levz0r/domain-parking-with-real-time-analytics-fef95b093756" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;
&lt;/h6&gt;

</description>
      <category>aws</category>
      <category>automation</category>
      <category>programming</category>
      <category>internet</category>
    </item>
    <item>
      <title>How to poll a Gmail inbox using cypress.io</title>
      <dc:creator>Lev</dc:creator>
      <pubDate>Tue, 19 Mar 2019 09:58:08 +0000</pubDate>
      <link>https://forem.com/levz0r/how-to-poll-a-gmail-inbox-using-cypressio-c9c</link>
      <guid>https://forem.com/levz0r/how-to-poll-a-gmail-inbox-using-cypressio-c9c</guid>
      <description>&lt;p&gt;Hey! Read about my experience writing custom tasks for the cypress framework.&lt;br&gt;
Read the article on Medium: &lt;a href="https://medium.com/@levz0r/how-to-poll-a-gmail-inbox-in-cypress-io-a4286cfdb888"&gt;https://medium.com/@levz0r/how-to-poll-a-gmail-inbox-in-cypress-io-a4286cfdb888&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>e2e</category>
      <category>testing</category>
      <category>node</category>
    </item>
    <item>
      <title>Angular CLI + gulp: Versioning your application made easy!</title>
      <dc:creator>Lev</dc:creator>
      <pubDate>Sat, 16 Mar 2019 17:29:31 +0000</pubDate>
      <link>https://forem.com/levz0r/angular-cli--gulp-versioning-your-application-made-easy-h4g</link>
      <guid>https://forem.com/levz0r/angular-cli--gulp-versioning-your-application-made-easy-h4g</guid>
      <description>&lt;p&gt;Read here: &lt;a href="https://medium.com/@levz0r/angular-cli-gulp-versioning-your-application-made-easy-d72a5f55b083"&gt;https://medium.com/@levz0r/angular-cli-gulp-versioning-your-application-made-easy-d72a5f55b083&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>gulpjs</category>
      <category>angularcli</category>
      <category>sentry</category>
    </item>
  </channel>
</rss>
