<?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: Dalu46</title>
    <description>The latest articles on Forem by Dalu46 (@dalu46).</description>
    <link>https://forem.com/dalu46</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%2F876626%2F9cf734e5-7afc-42a6-9d9c-ddfca9073042.png</url>
      <title>Forem: Dalu46</title>
      <link>https://forem.com/dalu46</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dalu46"/>
    <language>en</language>
    <item>
      <title>Hidden Complexities of Scaling GraphQL Federation (And How to Fix Them)</title>
      <dc:creator>Dalu46</dc:creator>
      <pubDate>Wed, 25 Jun 2025 14:00:53 +0000</pubDate>
      <link>https://forem.com/hackmamba/hidden-complexities-of-scaling-graphql-federation-and-how-to-fix-them-2peg</link>
      <guid>https://forem.com/hackmamba/hidden-complexities-of-scaling-graphql-federation-and-how-to-fix-them-2peg</guid>
      <description>&lt;p&gt;Federation gives teams the autonomy to move faster, but it also creates a web of hidden dependencies that are easy to overlook, often found in complex distributed systems.&lt;/p&gt;

&lt;p&gt;Schema changes can conflict without warning, ownership becomes harder to track, and the federation gateway, responsible for composing and deploying the supergraph, often becomes a single point of friction. Any issue in one subgraph can delay deploys for the entire graph. Platform teams are left responding to problems without the visibility or control to prevent them.&lt;/p&gt;

&lt;p&gt;Not every issue causes an outage. Sometimes a deploy gets held back because schema checks fail unexpectedly. At other times, a feature like a product details page returns &lt;code&gt;null&lt;/code&gt; because a field was removed from another team’s subgraph.&lt;/p&gt;

&lt;p&gt;You may notice that authorization logic behaves differently across multiple services, or stale queries slip past CI because the gateway composition succeeded, but the runtime still fails. Even when teams follow best practices, the graph becomes harder to evolve.&lt;/p&gt;

&lt;p&gt;This guide will teach you what starts to strain as GraphQL federation scales. I’ll walk you through the common failure points, the coordination challenges that emerge over time, and how we’ve built Grafbase to help platform teams manage federation without slowing down teams or introducing additional risk.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2n7qxun9s9dp4acovkru.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2n7qxun9s9dp4acovkru.png" alt="Complexities of scaling graphql federation" width="800" height="801"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How cross-team changes create friction in the graph
&lt;/h2&gt;

&lt;p&gt;As the graph grows and more teams contribute, friction becomes harder to contain. These issues stem from the accumulation of edge cases, mismatched assumptions, and operational gaps between teams.&lt;/p&gt;

&lt;p&gt;Here’s what that tends to look like in practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Schema changes that don’t fail fast&lt;/strong&gt;&lt;strong&gt;.&lt;/strong&gt; A field gets renamed or restructured in an individual subgraph. Another team’s query still depends on it. The gateway composes cleanly, but the runtime fails. Clients receive nulls, and no one is certain whether the issue originated from the schema, the query, or the deployment process.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Inconsistent conventions across multiple subgraphs&lt;/strong&gt;&lt;strong&gt;.&lt;/strong&gt; One team returns paginated lists with &lt;code&gt;pageInfo&lt;/code&gt;, another returns raw arrays. Errors follow different structures. Without shared review rules or CI checks, the unified API becomes inconsistent for consumers and harder to support.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deployments are blocked by schema drift&lt;/strong&gt;&lt;strong&gt;.&lt;/strong&gt; A platform team tries to publish the supergraph, but composition fails due to an uncoordinated change in a subgraph. The deploy is held until that team updates their schema, even if their change had nothing to do with the intended release.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance costs from distributed queries&lt;/strong&gt;&lt;strong&gt;.&lt;/strong&gt; A single client query might pull data from pricing, inventory, and recommendations subgraphs. Each adds a few hundred milliseconds. The end-user sees the total delay, even if each service is fast in isolation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Drift in authentication, logging, and schema checks&lt;/strong&gt;&lt;strong&gt;.&lt;/strong&gt; One subgraph uses field-level authentication, while another skips authentication altogether. Some teams log queries; others don’t. Traces are inconsistent, and without shared tooling or policy enforcement, platform teams end up stitching together visibility after things go wrong.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These cases emerge quietly at first, then repeatedly, disrupting your process. They add risk to every deployment and shift platform work toward reactive maintenance instead of scalable support.&lt;/p&gt;

&lt;h2&gt;
  
  
  What these frictions are costing you
&lt;/h2&gt;

&lt;p&gt;A poorly managed federated graph can increase cognitive load for developers and overwhelm platform teams with issues like schema inconsistencies and network latency. This can lead to application errors, downtime, and even delayed shipping, resulting in lost revenue or deterioration of the organization's reputation.&lt;/p&gt;

&lt;p&gt;For instance, one of the common operational pain points in federated architecture is the "all-or-nothing" failure mode, which is frequently debated in community forums, such as &lt;a href="https://github.com/apollographql/federation/issues/355" rel="noopener noreferrer"&gt;this GitHub thread&lt;/a&gt;. In such scenarios, when a single subgraph becomes unhealthy or unresponsive, the central GraphQL gateway can fail the entire supergraph, resulting in a system-wide 500 error for clients. &lt;/p&gt;

&lt;p&gt;The longer these frictions accumulate, the more you shift your focus from making meaningful improvements to protecting what already exists:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Refactoring shared types becomes too risky:&lt;/strong&gt; You duplicate fields across subgraphs (with the same purpose, but different names) because it feels safer than coordinating changes across groups.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ownership becomes unclear:&lt;/strong&gt; You end up managing schema sequencing, gateway composition, and rollout order, even when you’re not responsible for the underlying services. Time that could be invested in infrastructure or automation is redirected toward conflict resolution.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Schema migrations get delayed:&lt;/strong&gt; Cleanup tasks remain open for months. Duplicate logic is left in place because you don’t feel confident removing it. You wait until it’s necessary to touch anything shared.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Product decisions are shaped by coordination overhead:&lt;/strong&gt; You defer exposing new data because integrating it into the unified graph means relying on another team’s schema, and this pressure can sometimes lead to skipping crucial validation steps to avoid a delay in shipping. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the time you recognize the pattern, it’s already affecting how you plan, test, and deploy your work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should scaling GraphQL federation look like?
&lt;/h2&gt;

&lt;p&gt;In a healthy setup, subgraph teams deploy on their own timelines. Schema changes validate cleanly across environments before anything blocks, and ownership is embedded in the schema.&lt;/p&gt;

&lt;p&gt;Platform engineers won't spend time chasing rollbacks or patching CI. Instead, they’re working on systems that make the graph easier to evolve.&lt;/p&gt;

&lt;p&gt;You should expect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Schema conflicts are caught early through automated checks across environments. The federated GraphQL schema is designed to be backward compatible.&lt;/li&gt;
&lt;li&gt;Auth, logging, and validation are defined within each subgraph but applied consistently across the graph.&lt;/li&gt;
&lt;li&gt;Gateway tooling provides clear traces, error context, and actionable insights, enhancing query planning.&lt;/li&gt;
&lt;li&gt;There are fewer internal docs and handoffs, so teams can onboard and contribute without friction, leading to a smoother developer experience.&lt;/li&gt;
&lt;li&gt;There are coordinated improvements across the platform without disrupting feature development.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9rfqcinc3fz7ryojojpd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9rfqcinc3fz7ryojojpd.png" alt="What scaling federation should look like" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That setup is possible, and in the next section, I’ll walk you through how to use  &lt;a href="https://grafbase.com/" rel="noopener noreferrer"&gt;Grafbase&lt;/a&gt; to achieve this kind of environment, making federation easier to manage as adoption grows without adding overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use Grafbase to simplify federation for your enterprise teams
&lt;/h2&gt;

&lt;p&gt;Grafbase is designed to tackle the complexities of a growing federated architecture through its comprehensive approach, focusing on:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Built-in schema validation, observability, and audit-level insight&lt;/strong&gt;&lt;br&gt;
As mentioned earlier, one of the primary challenges in scaling GraphQL federation is managing schemas across independently evolving subgraphs. For example, imagine managing the schema for a &lt;code&gt;User&lt;/code&gt; service and an &lt;code&gt;Orders&lt;/code&gt; service as separate entities. If the &lt;code&gt;User&lt;/code&gt; service changes a fundamental field, such as &lt;code&gt;id&lt;/code&gt;, it could break the &lt;code&gt;Orders&lt;/code&gt; service if the &lt;code&gt;Orders&lt;/code&gt; service extends the User type based on that &lt;code&gt;id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Grafbase tackles this through its platform's architecture. By configuring subgraphs via the &lt;code&gt;grafbase.toml&lt;/code&gt;, specifying their GraphQL endpoints:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# grafbase.toml

[graphql]
schema = "./schema.graphql"

[subgraphs.accounts]
introspection_url = "http://localhost:4000/graphql"

[subgraphs.orders]
introspection_url = "http://localhost:4001/graphql"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Grafbase automatically introspects these endpoints during local development, pulling the latest schema from each subgraph. This allows teams to iterate on services independently while still catching breaking changes early, before they make it into production.&lt;/p&gt;

&lt;p&gt;Grafbase analyzes these schemas and registers them within its &lt;a href="https://grafbase.com/docs/platform/schema-registry" rel="noopener noreferrer"&gt;schema registry&lt;/a&gt;. This registry acts as the source of truth for your GraphQL schemas. During this composition, Grafbase performs automated checks, including build, operation, and lint checks, to identify potential schema inconsistencies before deployment. This validation helps maintain the stability and integrity of the federated API. &lt;/p&gt;

&lt;p&gt;The Grafbase Gateway also provides &lt;a href="https://grafbase.com/docs/gateway/observability#logs" rel="noopener noreferrer"&gt;logs&lt;/a&gt;, &lt;a href="https://grafbase.com/docs/gateway/observability#metrics" rel="noopener noreferrer"&gt;metrics&lt;/a&gt;, and &lt;a href="https://grafbase.com/docs/gateway/observability#traces" rel="noopener noreferrer"&gt;traces&lt;/a&gt; for monitoring and debugging the federated graph. It even allows viewing schema changes over time via a changelog, and supports custom checks with the &lt;code&gt;grafbase check&lt;/code&gt; command to enforce organization-specific rules. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;br&gt;
Built with Rust, &lt;a href="https://grafbase.com/blog/benchmarking-grafbase-vs-apollo-vs-cosmo-vs-mesh" rel="noopener noreferrer"&gt;Grafbase delivers around 40% faster query&lt;/a&gt; &lt;a href="https://grafbase.com/blog/benchmarking-grafbase-vs-apollo-vs-cosmo-vs-mesh" rel="noopener noreferrer"&gt;&lt;/a&gt;speeds and significantly reduced CPU usage. It maintains low latency and consistent performance even during traffic spikes. This ensures fast applications and lower infrastructure costs at any scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security and self-hosting&lt;/strong&gt;&lt;br&gt;
Grafbase provides security through field-level, &lt;a href="https://grafbase.com/blog/custom-authentication-and-authorization-in-graphql-federation" rel="noopener noreferrer"&gt;WebAssembly&lt;/a&gt; (Wasm) based authorization. This allows you to define precisely who is allowed to view specific fields within your GraphQL types. With Wasm, you can attach arbitrary and complex authorizations, granting full access to request and response data and potential Input/Output (I/O) operations. This provides you with the freedom to tailor security policies to your unique data model and business rules, extending beyond simple role-based or type-level authorization.&lt;/p&gt;

&lt;p&gt;For companies with specific security and compliance requirements, Grafbase also offers flexibility in deployment options, including crucial self-hosted and air-gapped environments. This simplifies API infrastructure, giving you control over your entire system and data, and ensuring compliance with internal and industry regulations without relying on a fully managed cloud solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customization&lt;/strong&gt;&lt;br&gt;
Grafbase &lt;a href="https://grafbase.com/extensions" rel="noopener noreferrer"&gt;extensions&lt;/a&gt; and &lt;a href="https://grafbase.com/guides/implementing-gateway-hooks" rel="noopener noreferrer"&gt;hooks&lt;/a&gt; are a powerful mechanism for customizing the Grafbase gateway's behavior without the overhead of managing additional infrastructure. This stands in contrast to approaches that utilize external plugins, which must be configured and updated independently. Grafbase extensions make it easier to adopt GraphQL Federation by enabling the declarative integration of services such as authentication, storage, and databases within your schema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI-powered API querying&lt;/strong&gt;&lt;br&gt;
Grafbase features forward-looking capabilities, such as native Model Context Protocol (&lt;a href="https://grafbase.com/docs/gateway/mcp" rel="noopener noreferrer"&gt;MCP&lt;/a&gt;) support. MCP paves the way for the potential of incorporating AI agents that can query APIs using natural language. From an engineering perspective, this presents new avenues for consumption and interaction with APIs, particularly in high-scale deployments where it can be challenging to familiarize oneself with the entire API surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;Not every team needs the same federation setup. But the further you scale, the more evident the gap between tools that let you patch things together and platforms built to support distributed teams by default.&lt;/p&gt;

&lt;p&gt;Grafbase is designed for that next stage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Moving from a monolith?&lt;/strong&gt; Grafbase simplifies the transition by encouraging clear subgraph boundaries and managing the gateway for you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managing infra in-house?&lt;/strong&gt; Use Grafbase declaratively, self-hosted or in the cloud, with native support for CI/CD, caching, and observability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migrating off Apollo Gateway?&lt;/strong&gt; Avoid stitching and manual resolver work. Grafbase automates schema composition without giving up team-level control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Navigating compliance and access control?&lt;/strong&gt; Define field-level authentication, RBAC, and isolated preview environments directly within your schema.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re looking for a GraphQL federation setup that prioritizes autonomy &lt;em&gt;and&lt;/em&gt; structure, Grafbase might be the shift you’re looking for. &lt;/p&gt;

&lt;p&gt;Start with the &lt;a href="https://grafbase.com/docs" rel="noopener noreferrer"&gt;docs&lt;/a&gt;, explore our &lt;a href="https://www.google.com/search?q=https://grafbase.com/guides/federation-composition" rel="noopener noreferrer"&gt;schema composition guide&lt;/a&gt;, or check out the &lt;a href="https://grafbase.com/guides/migrating-from-apollo" rel="noopener noreferrer"&gt;Apollo migration walkthrough&lt;/a&gt; to see how Grafbase can help you scale without the friction.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Get Started with Serverless Architectures: Top Tools You Need to Know</title>
      <dc:creator>Dalu46</dc:creator>
      <pubDate>Tue, 01 Apr 2025 23:47:36 +0000</pubDate>
      <link>https://forem.com/hackmamba/get-started-with-serverless-architectures-top-tools-you-need-to-know-3p12</link>
      <guid>https://forem.com/hackmamba/get-started-with-serverless-architectures-top-tools-you-need-to-know-3p12</guid>
      <description>&lt;p&gt;Managing the infrastructure to host and execute backend code requires you to size, provision, and scale several servers, apply security patches, and then manage operating system updates and infrastructure for performance and availability.&lt;/p&gt;

&lt;p&gt;Wouldn't it be great if you could build out your application without spending time managing servers? That's the whole idea behind serverless architecture. &lt;/p&gt;

&lt;p&gt;This article will guide you through the essential tools for adopting serverless architecture, offering a practical roadmap to get started. It aims to demystify the serverless ecosystem and highlight key technologies.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does Serverless Really Mean?
&lt;/h2&gt;

&lt;p&gt;One of the most common misconceptions in the industry is that going serverless means eliminating servers entirely. In contrast, it just means removing them from the developer’s perspective. At the end, there is a server running your application code. Serverless just means that you don’t operate them. The cloud providers (e.g., AWS Lambda and Azure Functions) handle the infrastructure, allowing you to focus on your code.&lt;/p&gt;

&lt;p&gt;Think of ordering food from your favorite restaurants. You have great food without needing to shop, cook, or clean. Serverless computing is pretty much the same: you run your code without managing the servers.&lt;/p&gt;

&lt;p&gt;As your application grows and gains many users worldwide, processing data becomes complex and demands scaling. This will require handling downtime, reducing latency, and managing servers, which results in additional costs and resources. By adopting serverless architecture, larger applications can run faster with lower costs and resources.&lt;/p&gt;

&lt;p&gt;Serverless architecture is well-suited for web and mobile applications with different workloads. Examples are real-time data processing apps like chat apps, backend for frontend services (BFF) apps like e-commerce apps, automation and scheduled tasks apps like task manager apps, and more. &lt;/p&gt;

&lt;h2&gt;
  
  
  Key Benefits of Serverless Architecture
&lt;/h2&gt;

&lt;p&gt;There are many other reasons why going serverless is a good design choice over managing your servers yourself. Here are the most essential ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cost-Effectiveness&lt;/strong&gt;: Serverless architecture uses a pay-as-you-go model; you pay only for what you use. This method can significantly reduce operational expenses, which is great for developers with strict budgets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower&lt;/strong&gt; &lt;strong&gt;D&lt;/strong&gt;&lt;strong&gt;evOps&lt;/strong&gt; &lt;strong&gt;R&lt;/strong&gt;&lt;strong&gt;equirements:&lt;/strong&gt; Developers or businesses using serverless platforms do not need to spend on hiring and training DevOps resources, as server operations are managed for them. Instead, it allows the engineering team to focus on application development.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High&lt;/strong&gt; &lt;strong&gt;A&lt;/strong&gt;&lt;strong&gt;vailability:&lt;/strong&gt; Serverless platforms generally offer high availability by distributing functions across multiple data centers or regions. This built-in redundancy enhances application reliability and reduces the risk of downtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; Serverless solutions scale up and down automatically, responding to changing workloads without manual provisioning. This allows you to quickly increase your resources anytime without being concerned about the server's capacity as it scales.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Top Serverless Tools for Compute, Storage, and Databases
&lt;/h2&gt;

&lt;p&gt;This section will discuss the top serverless tools that will give you an edge while getting started with serverless architecture. It will be divided into serverless compute tools and serverless databases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Serverless compute tools
&lt;/h3&gt;

&lt;p&gt;Here are the top serverless cloud providers. While each platform has distinct features and advantages, they allow developers to focus on writing code rather than managing servers.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AWS Lambda&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt; is a compute service that runs your backend code in response to events such as object uploads and HTTP requests.&lt;br&gt;
It automatically handles all the capacity, patching, scaling, and administration of the infrastructure to run your AWS Lambda functions.&lt;br&gt;
Lambda also provides visibility and performance and automatically manages the computing resources, making it easy to build applications that respond quickly to new information.&lt;br&gt;
Like other serverless providers, Lambda as a service doesn't come with built-in storage.&lt;br&gt;
Lambda functions are stateless; each invocation is considered a clean slate.&lt;br&gt;
Nevertheless, Lambda functions can work with additional services that provide storage.&lt;br&gt;
Common examples include &lt;a href="https://www.googleadservices.com/pagead/aclk?sa=L&amp;amp;ai=DChcSEwid8YDgzZuLAxVDmVAGHTlOONIYABAAGgJkZw&amp;amp;co=1&amp;amp;ase=2&amp;amp;gclid=Cj0KCQiAwOe8BhCCARIsAGKeD56MGJ6kJiJKmv4kMjozWZsExS-XDi_Lw3STVp8YdC_Zp069H8VJFX4aAkjKEALw_wcB&amp;amp;ohost=www.google.com&amp;amp;cid=CAESVuD29Nuoo3wL2cr-sdMmHzknfiuUsgdCn4SAkQ0SuZE2AWyPtaSfCtnmNAILsg9riDJNrj_XUjF7n4JCc2c3-QZ9bhiV0ifLnKxiPXOEJeHfxA8kAfzk&amp;amp;sig=AOD64_0TG0Hdbm2jZwFzc3wx9RVuq1TOVQ&amp;amp;q&amp;amp;nis=4&amp;amp;adurl&amp;amp;ved=2ahUKEwispPvfzZuLAxUUZ0EAHdIkDD8Q0Qx6BAgSEAE" rel="noopener noreferrer"&gt;S3&lt;/a&gt; (Simple Storage Service), &lt;a href="https://aws.amazon.com/efs/" rel="noopener noreferrer"&gt;EFS&lt;/a&gt; (Elastic File System), and databases like RDS, Neon, and DynamoDB.&lt;/p&gt;
&lt;h3&gt;
  
  
  Use Case
&lt;/h3&gt;

&lt;p&gt;AWS Lambda is perfect for applications that process images due to its integration with &lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;AWS S3&lt;/a&gt;, an object storage service. A good example is an e-commerce application that renders images in different sizes.&lt;br&gt;
Here are the top features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring and Logging:&lt;/strong&gt; Lambda integrates with Amazon &lt;a href="https://aws.amazon.com/cloudwatch/" rel="noopener noreferrer"&gt;CloudWatch&lt;/a&gt; for monitoring and logging, providing insights into function performance and errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-language Support:&lt;/strong&gt; AWS Lambda supports multiple programming languages.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Azure Functions&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://azure.microsoft.com/en-us/services/functions/" rel="noopener noreferrer"&gt;Azure Functions&lt;/a&gt; is a function-as-a-service in Microsoft Azure that runs small pieces of code or "functions" in the cloud.&lt;br&gt;
Functions are executed by a trigger (an action that invokes the code).&lt;br&gt;
Azure Functions is an extension of the &lt;a href="https://www.googleadservices.com/pagead/aclk?sa=L&amp;amp;ai=DChcSEwjahJTT-puLAxWwpFAGHRuCIuIYABAAGgJkZw&amp;amp;ae=2&amp;amp;aspm=1&amp;amp;co=1&amp;amp;ase=5&amp;amp;gclid=Cj0KCQiAwOe8BhCCARIsAGKeD54lTiWHffaV1bO2mROOrkzFdep28MlOE9fTRZHMMQwcD4JjMMlr4osaArVOEALw_wcB&amp;amp;ohost=www.google.com&amp;amp;cid=CAESVuD28B414Q0lnCLLm9q8mU7aeVb_nIh8Mmsamc3m_wEg3Ecd1MbAveqmUMLomlE1xJHYYtZNpJkDRGc8lXPRhq6W1XqiEEDvqPkvB68N52cEIVdk71K3&amp;amp;sig=AOD64_10l0mGGQwkevRQ4yxrwv1C6mQAdA&amp;amp;q&amp;amp;adurl&amp;amp;ved=2ahUKEwjq8o7T-puLAxW7XUEAHZmtI2MQ0Qx6BAgIEAE" rel="noopener noreferrer"&gt;Azure App Service&lt;/a&gt;.&lt;br&gt;
Azure Functions focus on event-driven scenarios and provide on-demand compute resources required for your applications.&lt;br&gt;
You can use Azure Functions to build web APIs, respond to database changes, process IoT streams, manage messaging queues, and more.&lt;/p&gt;
&lt;h3&gt;
  
  
  Use Case
&lt;/h3&gt;

&lt;p&gt;Azure Functions integrates with most tools in the Microsoft ecosystem, including &lt;a href="https://learn.microsoft.com/en-us/azure/storage/queues/storage-queues-introduction" rel="noopener noreferrer"&gt;Azure Queue&lt;/a&gt; (adding messages to a queue) and &lt;a href="https://dotnet.microsoft.com/en-us/apps/aspnet/signalr" rel="noopener noreferrer"&gt;SignalR&lt;/a&gt; (sending real-time updates). This makes Azure Functions the perfect choice for real-time applications like chat apps.&lt;br&gt;
Here are the top features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It supports multiple languages, including C#, Java, Python, PowerShell, and TypeScript, and it can extend its language support to languages such as Go.&lt;/li&gt;
&lt;li&gt;Microsoft Azure offers single-pane operations, a powerful feature that provides a unified view of hybrid environments via the Operation Management Suite (OMS), a Management-as-a-Service (MaaS).&lt;/li&gt;
&lt;li&gt;It allows monitoring and management of various data sources, including storage, virtual network services, machines, logs, and insights.&lt;/li&gt;
&lt;li&gt;Flexible development with integrations with GitHub and Azure DevOps.&lt;/li&gt;
&lt;li&gt;The entire SDK for functions is open-source.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Google Cloud Functions&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://cloud.google.com/functions/" rel="noopener noreferrer"&gt;Google Cloud Functions&lt;/a&gt; is a scalable serverless execution environment for building and connecting cloud services. It provides triggers automatically, with out-of-the-box support for HTTP and event-driven triggers from GCP services.&lt;br&gt;
There are two types of Google Cloud Functions: &lt;strong&gt;API cloud functions&lt;/strong&gt; and &lt;strong&gt;event-driven&lt;/strong&gt; cloud functions.&lt;br&gt;
The API cloud functions are invoked from standard HTTP requests, while the events-driven cloud functions handle events from the cloud infrastructure.&lt;br&gt;
The event driver provides you with a trigger, which is stored in the data, and then sends you a response according to your function.&lt;/p&gt;
&lt;h3&gt;
  
  
  Use Case
&lt;/h3&gt;

&lt;p&gt;Out of all the tools in this list, Google Cloud Functions is the best for image analysis.&lt;br&gt;
While AWS Lambda is good for processing images, Google Cloud Functions is the perfect choice for applications that require image analysis because of its integration with &lt;a href="https://cloud.google.com/vision" rel="noopener noreferrer"&gt;Google Cloud Vision API&lt;/a&gt;. It is excellent for building social media applications and applications with face recognition.&lt;br&gt;
Here are its key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allows configuration of functions based on the application's need, enabling trigger-based execution, memory and CPU allocation, and scaling controls.&lt;/li&gt;
&lt;li&gt;Allows configuring an instance to complete multiple concurrent requests.&lt;/li&gt;
&lt;li&gt;Automatic provisioning of resources.&lt;/li&gt;
&lt;li&gt;Simplified and intuitive developer experience.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cloudflare Workers&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://workers.cloudflare.com/" rel="noopener noreferrer"&gt;Cloudflare Workers&lt;/a&gt; is a serverless computing platform that allows users to create or augment existing serverless applications. Unlike most serverless platforms, it requires little configuration and no region selection, enabling users to deploy applications anywhere on Earth.&lt;/p&gt;
&lt;h3&gt;
  
  
  Use Case
&lt;/h3&gt;

&lt;p&gt;It’s best for geo-location content delivery applications because of its low latency and edge network spread around the globe. Examples are live streaming apps, online games, product apps that show products and currency based on the users' location, and so on.&lt;br&gt;
Here are the key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is fast and runs on lightweight &lt;a href="https://developers.cloudflare.com/workers/learning/how-workers-works" rel="noopener noreferrer"&gt;V8 isolates&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Easy deployments with just a little configuration.&lt;/li&gt;
&lt;li&gt;High-performance global network.&lt;/li&gt;
&lt;li&gt;Cloudflare Workers use the same network that runs Cloudflare content delivery network, load balancing, web application firewall, and more, leading to faster performance.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Serverless databases
&lt;/h3&gt;

&lt;p&gt;Here are the top serverless databases with their key features and use cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Neon&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://neon.tech/" rel="noopener noreferrer"&gt;Neon&lt;/a&gt; is a serverless Postgres database that scales up and down based on your application workload by separating storage and computing. When you develop your app, you don’t need to worry about the cost of using it. Neon scales to zero when the database is not in use. When you deploy your app to production, you don’t need to worry about the capacity of your database; Neon automatically scales to the demands of your workload. It’s the perfect database for working with serverless architecture.&lt;br&gt;
Neon applications can be deployed traditionally and connected over &lt;a href="https://www.fortinet.com/resources/cyberglossary/tcp-ip#:~:text=What%20does%20TCP%20mean%3F,data%20in%20digital%20network%20communications." rel="noopener noreferrer"&gt;TCP&lt;/a&gt; using any Postgres driver. However, more teams are leveraging edge deployment in environments where they can’t establish a direct TCP connection, such as Cloudflare workers, AWS Lambda, and Vercel Edge functions. Neon provides a low-latency serverless driver for such deployments, working over both WebSockets and HTTP for efficient real-time and request-based interactions.&lt;/p&gt;
&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;p&gt;Neon is best for building modern, data-driven, and SaaS applications. An example is a subscription-based analytics app with real-time customer insights for different businesses.&lt;br&gt;
Here are the key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provides database &lt;a href="https://neon.tech/docs/introduction/read-replicas" rel="noopener noreferrer"&gt;read replica&lt;/a&gt;, a feature that allows replicating databases, enabling you to provide read-only access without duplicating data.&lt;/li&gt;
&lt;li&gt;Provides API and CLI tools for database management.&lt;/li&gt;
&lt;li&gt;It’s cost-efficient, as your costs are directly tied to the resources your workload consumes—you don't pay for idle capacity.&lt;/li&gt;
&lt;li&gt;Integrates with any language or framework and supports over 70 Postgres extensions.&lt;/li&gt;
&lt;li&gt;Neon uses PgBouncer for &lt;a href="https://neon.tech/docs/connect/connection-pooling" rel="noopener noreferrer"&gt;connection pooling&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Firebase&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://firebase.google.com/" rel="noopener noreferrer"&gt;Firebase&lt;/a&gt; is a serverless app development platform based on Google Cloud. It integrates with several other Google products, such as analytics, and provides client-side SDKs for building iOS, Android, and web applications.&lt;br&gt;
Firebase offers Cloud Functions edge deployments, enabling the serverless execution of backend code in response to events triggered by HTTPS requests. This allows you to build responsive and scalable applications without managing servers.&lt;/p&gt;
&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;p&gt;Firebase is great for building real-time applications that work with media content like images, videos, and audio. Examples are social media applications and e-commerce apps that have real-time customer support.&lt;br&gt;
Here are its key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Offers fast and secure static web hosting with global CDN delivery.&lt;/li&gt;
&lt;li&gt;Provides out-of-the-box authentication with support for multiple providers like Google, Facebook, and email with password.&lt;/li&gt;
&lt;li&gt;It comes with Firestore and Realtime Database. These are scalable NoSQL databases for storing data and building interactive applications.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Building A Simple Serverless API
&lt;/h2&gt;

&lt;p&gt;To fully understand serverless architecture, let’s build a simple application to see how it works. This section will walk you through a step-by-step guide to building a simple serverless API using Cloudflare Workers and Neon, a serverless Postgres database.&lt;/p&gt;

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

&lt;p&gt;First, create an account at &lt;a href="https://neon.tech/" rel="noopener noreferrer"&gt;neon.tech&lt;/a&gt;. Once you have an account, click &lt;strong&gt;Create a new project&lt;/strong&gt; and name it &lt;strong&gt;&lt;em&gt;serverless_api&lt;/em&gt;&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;Next, click on &lt;strong&gt;Dashboard&lt;/strong&gt; → &lt;strong&gt;Connect&lt;/strong&gt;, then copy the connection string. Keep it safe because you will need it to connect with Cloudflare Workers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxbem8j4hnt0yx7legbsh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxbem8j4hnt0yx7legbsh.png" alt="Neon console" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a Cloudflare Worker
&lt;/h3&gt;

&lt;p&gt;Sign up on &lt;a href="https://dash.cloudflare.com/sign-up/workers-and-pages" rel="noopener noreferrer"&gt;Cloudflare&lt;/a&gt;. In the sidebar, click &lt;strong&gt;Compute (Workers) → Workers &amp;amp; Pages&lt;/strong&gt; → &lt;strong&gt;Create.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0shfdtdoxvh2x82c8u1r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0shfdtdoxvh2x82c8u1r.png" alt="Cloudflare console" width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Name your Worker, &lt;code&gt;serverless-api&lt;/code&gt;, and click &lt;strong&gt;Deploy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4dwj0hc8vip3twlbvme.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4dwj0hc8vip3twlbvme.png" alt="Name your worker" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the Worker is deployed, you will be directed to the Worker editor page, where you can write your Worker code directly in the browser. In a real application, you would write the logic to query your Neon database. However, the API will return a simple “&lt;strong&gt;Hello Serverless World&lt;/strong&gt;” text for this application. &lt;/p&gt;

&lt;h3&gt;
  
  
  Connect Cloudflare Worker with Neon
&lt;/h3&gt;

&lt;p&gt;On the sidebar, click &lt;strong&gt;Compute (Workers) → Workers &amp;amp; Pages&lt;/strong&gt; → &lt;strong&gt;Integrations,&lt;/strong&gt; then select Neon and click &lt;strong&gt;Add Integration&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvc2hdnpstpqcfcjv3ocw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvc2hdnpstpqcfcjv3ocw.png" alt="add neon integration" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow the prompt. You will be asked to authorize Neon and select the &lt;strong&gt;project&lt;/strong&gt;, &lt;strong&gt;branch&lt;/strong&gt;, and &lt;strong&gt;database&lt;/strong&gt;. Cloudflare will automatically show your Neon connection string. Compare it with the one you have retrieved from Neon to confirm.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;These Secrets will be added to the existing Environment Variables of this Worker.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wgycpwe01kipr15j0cm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wgycpwe01kipr15j0cm.png" alt="confirm and finish neon integration" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once that is done, Cloudflare will provide an endpoint that you can fetch in frontend code, similar to this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://serverless-api.&amp;lt;your email&amp;gt;.workers.dev/ 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can also test the serverless function in the preview browser on the Cloudflare console. &lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;Worker.js&lt;/code&gt; file, paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&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 Severless World!&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will return a simple &lt;strong&gt;Hello World&lt;/strong&gt; and log the Neon connection string in the console to show that the database was successfully connected. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fopamagxy6glivfbe8r1t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fopamagxy6glivfbe8r1t.png" alt="test the serverless function on Cloudflare playground" width="800" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You may ask why a serverless API is needed when you could query the database directly. Well, the answer is simple: as soon as the application begins to grow, there will be lots of users calling that API (function). However, since the serverless provider automatically handles scalability, your server will be able to handle the change in workload without crashing. If you want a more extensive tutorial on building a serverless application with Cloudflare Workers and Neon, check &lt;a href="///scl/fi/2nigvsmav6qhhgprvcsml/This-Is-Why-You-Should-Use-Cloudflare-Workers.paper?rlkey=k3fwydvj3kw1834nr1ox6rkfw&amp;amp;dl=0"&gt;this tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Serverless computing offers a powerful approach to building modern applications, enabling automatic scaling, low cost, and faster response time. By using the right tools, such as AWS Lambda, Google Cloud Functions, Cloudflare Workers, and Neon for databases, you can simplify development while reducing infrastructure complexity.&lt;/p&gt;

&lt;p&gt;Curious about Neon’s capabilities? &lt;a href="https://neon.tech/" rel="noopener noreferrer"&gt;Sign up&lt;/a&gt; today and experience hassle-free, serverless PostgreSQL!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>This Is Why You Should Use Cloudflare Workers</title>
      <dc:creator>Dalu46</dc:creator>
      <pubDate>Sat, 15 Mar 2025 15:12:43 +0000</pubDate>
      <link>https://forem.com/hackmamba/this-is-why-you-should-use-cloudflare-workers-2i4b</link>
      <guid>https://forem.com/hackmamba/this-is-why-you-should-use-cloudflare-workers-2i4b</guid>
      <description>&lt;p&gt;&lt;a href="https://workers.cloudflare.com/" rel="noopener noreferrer"&gt;Cloudflare Workers&lt;/a&gt; are serverless edge-computing providers that allow users to create or augment existing serverless applications. It uses the &lt;a href="https://www.cloudflare.com/learning/serverless/glossary/what-is-chrome-v8/" rel="noopener noreferrer"&gt;V8 engine&lt;/a&gt; under the hood, the same engine used by Node.js and Chromium. These Workers are deployed all around the globe to process data closer to your users, yielding faster responses and improving processing speed.&lt;/p&gt;

&lt;p&gt;Building large-scale applications requires rigorous infrastructure and server management. Cloudflare Workers eliminate this burden by deploying and managing your code. They handle issues related to downtimes due to heavy traffic and have low latency, which improves your application's performance.&lt;/p&gt;

&lt;p&gt;By the end of this article, you will understand the benefits of Cloudflare Workers and how they differ from other serverless platforms like AWS Lambda. You will also learn about Neon's serverless capabilities, the benefits of using Neon database, and how you can use Neon with Cloudflare Workers to build your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use Cloudflare Workers
&lt;/h2&gt;

&lt;p&gt;Large applications with millions of users worldwide require complex data processing. Usually, the necessary servers for storing and processing data are in more than one physical location worldwide. To ensure the application runs smoothly, large applications require complex procedures such as configurations, database maintenance, and automation, which lead to additional costs and resources.&lt;/p&gt;

&lt;p&gt;If you are looking to build scalable applications, some of the benefits of using Cloudflare Workers over other serverless providers are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Minimal&lt;/strong&gt; &lt;strong&gt;Latency&lt;/strong&gt;: Because Cloudflare Workers deploy your code across its global network, there is less distance between your data and your users’ devices. As a result, your application loads and processes faster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: As your application scales, handling client requests and managing your servers becomes tedious. Cloudflare Workers solve this issue by storing and processing your code, minimizing downtime, and ensuring your application remains accessible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost&lt;/strong&gt;: Cloudflare Workers are cost-effective as you only have to pay for the resources (e.g., CPU time, requests) you use. This pricing structure helps you save costs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt;: Cloudflare Workers give you control over writing logic that can handle your HTTP requests and responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the next section, you will see how Cloudflare Workers compares to AWS Lambda, another serverless provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloudflare Workers vs. AWS Lambda@Edge
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/lambda/edge/#:~:text=Lambda%40Edge%20is%20a%20feature,multiple%20locations%20around%20the%20world." rel="noopener noreferrer"&gt;AWS Lambda@Edge&lt;/a&gt; is a feature in AWS Lambda that supports running serverless functions at Amazon CloudFront edge locations, bringing computation closer to the user. It is optimized for CDN-based workloads and supports request and response modification, content customization, security features, and smart routing—all without the need for infrastructure management.&lt;br&gt;
However, Cloudflare Workers stand out in certain areas. If you are contemplating which one to use, here’s how they compare:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Cloudflare Workers&lt;/th&gt;
&lt;th&gt;AWS Lambda@edge&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Distributed across wider areas, making it much faster.&lt;/td&gt;
&lt;td&gt;Runs in AWS's global edge locations, tied to CloudFront.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Edge Computing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Designed for edge computing, optimized for API gateways, content localization, and personalization.&lt;/td&gt;
&lt;td&gt;Optimized for CDN-based workloads within Amazon CloudFront, enabling request and response modifications, content personalization, and security enhancements at the edge.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deployment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Easier setup with abstracted complexities, better documentation, and deployment tools.&lt;/td&gt;
&lt;td&gt;Requires deploying via AWS Lambda with CloudFront triggers, involving IAM roles and permissions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cold Starts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Faster cold starts due to small functions and edge capabilities.&lt;/td&gt;
&lt;td&gt;Generally slower cold starts, but mitigated by CloudFront caching and optimizations.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Easily integrates with services like Neon serverless database.&lt;/td&gt;
&lt;td&gt;Best suited for AWS services like Amazon S3, DynamoDB, and Cognito; optimized for CloudFront workloads.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code Size Limit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5MB total package size&lt;/td&gt;
&lt;td&gt;1MB per function (after compression), with a max of 50MB uncompressed including dependencies.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Next, let’s explore the benefits of integrating Cloudflare Workers with Neon, how it transforms everything, and how it can make life easier. You'll understand how to integrate your Cloudflare Worker with Neon in a snap and also see how to build a serverless web app in an instant.&lt;/p&gt;
&lt;h2&gt;
  
  
  Benefits of Integrating Cloudflare Workers with Neon Serverless Database
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://neon.tech/" rel="noopener noreferrer"&gt;Neon&lt;/a&gt; serverless database is a fully managed open-source Postgres database that decouples the storage and compute components, enabling it to dynamically scale up during high activities and down during idle periods. Neon comes with a serverless driver that makes it super easy to connect Neon to your Cloudflare Workers.  &lt;/p&gt;

&lt;p&gt;Other reasons why you should consider integrating your Cloudflare Workers with Neon serverless database are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Low&lt;/strong&gt; &lt;strong&gt;L&lt;/strong&gt;&lt;strong&gt;atency&lt;/strong&gt;: Cloudflare Workers run on the edge, which means they are close to the user and have low latency. Because Neon’s read replicas and autoscaling capabilities enable efficient fetching of all data, the database queries remain fast for users anywhere, improving the overall app performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost&lt;/strong&gt; &lt;strong&gt;E&lt;/strong&gt;&lt;strong&gt;fficiency&lt;/strong&gt;: Both Cloudflare Workers and Neon adjust to workload spikes based on your application's demand, which requires zero infrastructure management. They use a pay-as-you-go model and suspend idle instances to help lower costs. You only pay for what you use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified&lt;/strong&gt; &lt;strong&gt;D&lt;/strong&gt;&lt;strong&gt;eployment&lt;/strong&gt;: You don’t need to worry about provisioning or managing servers, as both Cloudflare Workers and Neon are fully serverless. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security:&lt;/strong&gt; Cloudflare Workers ship with built-in security features such as DDoS protection, request filtering, and firewalls. On the database side, Neon enables secure access with end-to-end encryption (TLS) and row-level security, simplifying enforcing access control.
## Building a Serverless Web App Using Cloudflare Workers and Neon&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://neon.tech/" rel="noopener noreferrer"&gt;Neon&lt;/a&gt; serverless databases can be integrated with Cloudflare Workers to process data. Neon provides you with a &lt;a href="https://neon.tech/docs/serverless/serverless-driver" rel="noopener noreferrer"&gt;&lt;strong&gt;serverless driver&lt;/strong&gt;&lt;/a&gt; that you can use to connect to your database and then create access data from your database in Cloudflare Workers. Neon’s serverless driver is optimized for edge environments, enabling SQL queries over HTTP or WebSockets. This makes it ideal for Cloudflare Workers, which run in a lightweight, distributed manner without persistent connections. &lt;/p&gt;

&lt;p&gt;This section demonstrates how to use the Neon serverless database with Cloudflare Workers to build a serverless web application. This application is a simple Next.js application that fetches brief details about African countries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmduckumvy55mr70lil3g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmduckumvy55mr70lil3g.png" alt="Application Demo" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;br&gt;
To follow through this tutorial, you need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Cloudflare account - &lt;a href="https://dash.cloudflare.com/sign-up" rel="noopener noreferrer"&gt;create a Cloudflare account&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A Neon account - &lt;a href="https://console.neon.tech/signup" rel="noopener noreferrer"&gt;create a Neon account&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Familiarity with Next.js and TypeScript&lt;/li&gt;
&lt;li&gt;Node installed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How The Application Works&lt;/strong&gt;&lt;br&gt;
The Cloudflare Worker serverless function is created and contains code that accepts and processes requests from the Next.js application. This function serves as your backend code, fetching data from your Neon serverless Postgres database and returning a &lt;strong&gt;JSON&lt;/strong&gt; version of the data as the response. You can then render the response data in your application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Project Setup and Installations&lt;/strong&gt;&lt;br&gt;
Follow the steps below to set up and install the necessary packages for the application:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Clone the project from &lt;a href="https://github.com/Dalu46/my-workers" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. On the terminal, paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Dalu46/my-workers.git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;my-workers
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Open the project in your code editor (preferably VSCode).&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Create your first worker by running the following command in the integrated terminal:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create cloudflare@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br&gt;
This will attempt to install &lt;code&gt;cloudflare-client-cli&lt;/code&gt; (also known as &lt;strong&gt;C3&lt;/strong&gt; in Cloudflare docs) and &lt;code&gt;wrangler&lt;/code&gt; with the following prompts in the image below. Type &lt;strong&gt;“./workers”&lt;/strong&gt; for the first question. This will be the name of your Cloudflare Worker and the folder created when the installation is completed. Select the &lt;strong&gt;Hello World example&lt;/strong&gt; option, click enter, then select the &lt;strong&gt;TypeScript&lt;/strong&gt; option and click enter. &lt;br&gt;
Finally, select &lt;strong&gt;No&lt;/strong&gt; for the last question about deploying your application. You will deploy your application after adding your serverless function code. &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbc6kypc5zm3kslmkut80.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbc6kypc5zm3kslmkut80.png" alt="Cloudflare Workers Insallation and Setup" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Create your Neon Serverless Database&lt;/strong&gt;&lt;br&gt;
Follow the steps below to create a Neon database for the application:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in to your Neon account.&lt;/li&gt;
&lt;li&gt;Create a new project and a new database, &lt;strong&gt;neon-db&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;On the left dashboard menu, click on the &lt;strong&gt;SQL Editor&lt;/strong&gt; to add queries to create a new table.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Using Neon’s AI feature, you can generate a simple table using this prompt; you can generate the SQL command from your Neon Console:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“create a table with 3 columns: id | country | Details where id is the unique numeric identifier, country is a string type, and details is a string type. Populate the table with 10 unique fields. Each field has a unique ID, a name of an African country, and a 2-paragraph sentence of that African country”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft3c30eawv0w2sdmm3l23.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft3c30eawv0w2sdmm3l23.png" alt="Neon SQL Editor" width="800" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will generate a PostgreSQL query command, and after it is done, click the &lt;strong&gt;Run&lt;/strong&gt; button to create the table. The table would have 3 columns: the &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;country&lt;/code&gt;, and &lt;code&gt;details&lt;/code&gt; columns, and it should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6e0uvu5pgpgytgm34c23.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6e0uvu5pgpgytgm34c23.png" alt="Neon Database" width="800" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Connect your&lt;/strong&gt; &lt;strong&gt;Cloudflare Worker&lt;/strong&gt; &lt;strong&gt;to your Neon Serverless Database&lt;/strong&gt;&lt;br&gt;
Your database is ready. You can now connect your serverless function to it by following the steps below:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Click the &lt;strong&gt;Connect&lt;/strong&gt; button to retrieve your database URL from your Neon dashboard. This URL should look like this: &lt;code&gt;postgresql://username:FzB6q0lQTTOF@ep-lively-union-42160037-pooler.us-east-2.aws.neon.tech/neon-test-db?sslmode=require&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the &lt;code&gt;wrangler.json&lt;/code&gt; file in the &lt;code&gt;./workers&lt;/code&gt; folder. Add your &lt;code&gt;DATABASE_URL&lt;/code&gt; to the variable object to enable your Worker access and connect to your database:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6ix30yl3o7junrmhd10.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6ix30yl3o7junrmhd10.png" alt="Cloudflare Worker Variables Configuration" width="800" height="395"&gt;&lt;/a&gt;&lt;br&gt;
Open your VSCode integrated terminal, run the following command to navigate to the Workers directory, and generate your &lt;code&gt;DATABASE_URL&lt;/code&gt; variable type in the &lt;code&gt;worker-configuration.d.ts&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;workers &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run cf-typegen
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Install the &lt;strong&gt;@neondatabase/serverless&lt;/strong&gt; package (this package connects to your Neon serverless database).&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @neondatabase/serverless
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Open the &lt;code&gt;index.ts&lt;/code&gt; file in the path &lt;code&gt;workers/src/index.ts&lt;/code&gt;. This file is your Cloudflare Worker (a.k.a. serverless function). It contains the logic to connect to and fetch your database records from the Neon serverless database and return a JSON response of your database data.&lt;/p&gt;

&lt;p&gt;Replace the default content in the &lt;code&gt;index.ts&lt;/code&gt; file with the following:&lt;br&gt;
&lt;/p&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="cm"&gt;/**
     * Welcome to Cloudflare Workers! This is your first worker.
     *
     * - Run `npm run dev` in your terminal to start a development server
     * - Open a browser tab at &amp;lt;http://localhost:8787/&amp;gt; to see your worker in action
     * - Run `npm run deploy` to publish your worker
     *
     * Bind resources to your worker in `wrangler.json`. After adding bindings, a type definition for the
     * `Env` object can be regenerated with `npm run cf-typegen`.
     *
     * Learn more at &amp;lt;https://developers.cloudflare.com/workers/&amp;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;Client&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;@neondatabase/serverless&lt;/span&gt;&lt;span class="dl"&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="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// connect to Neon serverless database&lt;/span&gt;
                    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&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;Client&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;DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&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;rows&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT * FROM african_countries;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// handle CORs error by whitelisting domains&lt;/span&gt;
                    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Origin&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;allowedOrigins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;http://localhost:5173&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;http://localhost:5174&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;http://localhost:5175&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;http://localhost:3000&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;

            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;corsHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;allowedOrigins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Credentials&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Headers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Origin, X-Requested-With, Content-Type, Accept&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Methods&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET,HEAD,PUT,PATCH,POST,DELETE&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;corsHeaders&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;satisfies&lt;/span&gt; &lt;span class="nx"&gt;ExportedHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Env&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Deploy your Cloudflare Worker&lt;/strong&gt;&lt;br&gt;
You’re ready to deploy your serverless function to your Cloudflare account. Follow the steps below to deploy your Cloudflare Worker: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;npx wrangler deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br&gt;
This will open a new browser window where you must authenticate and authorize using your Cloudflare account. The page will require you to log in if you're not already. Otherwise, click the &lt;strong&gt;Allow&lt;/strong&gt; button to authorize. You should see something like this:&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fldf1fdwuogbsxh99o04s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fldf1fdwuogbsxh99o04s.png" alt="Cloudflare Account Authorization" width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Close the browser tab and retrieve your serverless function URL from your terminal. &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa5bg3kanmjley8of55sc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa5bg3kanmjley8of55sc.png" alt="Terminal showing deployed Cloudflare Workers URL" width="800" height="234"&gt;&lt;/a&gt; You can also see your new serverless function (Cloudflare Worker) deployed to your Cloudflare account. Click on &lt;strong&gt;workers&lt;/strong&gt; to view details of your deployed Cloudflare Worker. &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fipi1mo9vj0oxgqc82exw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fipi1mo9vj0oxgqc82exw.png" alt="Cloudflare Workers Preview" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once you have retrieved your serverless function URL, you can send an HTTP request using the &lt;code&gt;fetch()&lt;/code&gt; method. The data returned in the response is then used to build the application. In the &lt;code&gt;src/app/details/[id]/page.tsx&lt;/code&gt; file, replace the URL with your serverless function URL.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use client&lt;/span&gt;&lt;span class="dl"&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;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="s2"&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useRouter&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/navigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;styles&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;../../page.module.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AfricanCountry&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;country&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;details&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Details&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;countries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCountries&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AfricanCountry&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;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://workers.your_url.workers.dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// serverless function URL&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useParams&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;countryData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;countries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&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="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="nf"&gt;useRouter&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;handleRedirect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="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="c1"&gt;// fetch countries data from our neon db using our serverless function URL&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;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;setIsLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;URL&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;_countries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;data&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="nf"&gt;setCountries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_countries&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unable to fetch data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;reportError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;setIsLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="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="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&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;isLoading&lt;/span&gt; 
            &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Fetching&lt;/span&gt; &lt;span class="nx"&gt;details&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;Neon&lt;/span&gt; &lt;span class="nx"&gt;DB&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;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;header&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;countryData&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;country&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;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&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;countryData&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="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;/&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="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;countries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&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;No&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="nx"&gt;found&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="p"&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;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleRedirect&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;BACK&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;Details&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;&lt;p&gt;To preview the application, run &lt;code&gt;npm run dev&lt;/code&gt;. You should be able to try it out at &lt;strong&gt;&lt;em&gt;localhost:3000&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;This is the final application preview:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnrj0ak0k171sou3zjs3h.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnrj0ak0k171sou3zjs3h.gif" alt="A gif to show how the final application works" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Dalu46/my-workers" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Cloudflare Workers bring the power of edge computing to serverless development with speed, scalability, and efficiency. When used with serverless databases like Neon, you can build incredibly responsive yet cost-effective apps without managing any infrastructure. Whether building real-time apps, handling API calls, or serving dynamic content, this combination offers performance and agility to scale faster.&lt;/p&gt;

&lt;p&gt;Looking to learn more? Look at this &lt;a href="https://neon.tech/docs/guides/cloudflare-pages" rel="noopener noreferrer"&gt;Neon tutorial&lt;/a&gt; about building web applications using a Neon serverless database with Cloudflare Workers. You can also harness and explore other &lt;a href="https://developers.cloudflare.com/learning-paths/get-started/concepts/what-is-cloudflare/" rel="noopener noreferrer"&gt;concepts covered by Cloudflare&lt;/a&gt; Workers, such as edge computing, security, and deployments, while keeping in mind the challenges of edge computing, including resource constraints, network reliability issues, security risks, and management complexities.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>6 Common Postgres Beginner Mistakes and Best Practices</title>
      <dc:creator>Dalu46</dc:creator>
      <pubDate>Fri, 28 Feb 2025 15:57:58 +0000</pubDate>
      <link>https://forem.com/hackmamba/6-common-postgres-beginner-mistakes-and-best-practices-2ag0</link>
      <guid>https://forem.com/hackmamba/6-common-postgres-beginner-mistakes-and-best-practices-2ag0</guid>
      <description>&lt;p&gt;“Anyone who has never made a mistake has never tried something new.” - Albert Einstein&lt;/p&gt;

&lt;p&gt;Postgres' popularity is steadily increasing. It is the most popular open-source relational database available, and with almost 40 years of development, it is an excellent choice for applications of all sizes. However, starting with Postgres can feel like climbing a mountain, and just like learning anything new, you will undoubtedly make mistakes. Although mistakes are a normal part of the learning experience, they can be time-consuming and difficult to debug. So why not avoid them in the first place?&lt;/p&gt;

&lt;p&gt;In this article, you'll learn about the most common mistakes beginners make when starting with Postgres. You'll see why these mistakes happen, how to avoid them, and techniques to help you write queries correctly. You'll also get actionable advice and practical tips to build confidence and develop better habits for database management. Learning from others' experiences can save you valuable time and frustration, so let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Beginner Mistakes
&lt;/h2&gt;

&lt;p&gt;Here are the six common PostgreSQL mistakes beginners should avoid to maintain efficient and secure database environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Not Understanding What VACUUM Is or When to Use it
&lt;/h3&gt;

&lt;p&gt;Understanding and using VACUUM accurately is important for maintaining a healthy and performant PostgreSQL database. VACUUM is a powerful command that reclaims storage occupied by dead tuples (dead rows). When a vacuum process runs, it marks the space occupied by dead tuples as reusable by other tuples.&lt;/p&gt;

&lt;p&gt;When you delete records from a Postgres database table, Postgres does not immediately remove the rows from the database; these are just marked as deleted. The previous version of the record still remains in the data file. This is the same with updates; each update of a row creates a new version of that row. This is called table bloat. These dead spaces are empty rows that occupy unused disk space in the data file and remain present until a &lt;a href="https://www.postgresql.org/docs/current/sql-vacuum.html" rel="noopener noreferrer"&gt;vacuum&lt;/a&gt; is done.&lt;/p&gt;

&lt;p&gt;Many beginners are unaware that they need to run the &lt;code&gt;VACUUM&lt;/code&gt; command to clean up dead tuples, which results in bloated databases and slower performance.&lt;/p&gt;

&lt;p&gt;It is necessary to run the &lt;code&gt;VACCUM&lt;/code&gt; command periodically, especially on frequently updated tables. Regularly reclaiming dead space can improve query performance, reduce disk space usage, decrease disk I/O by reducing table bloat, and ensure your database runs smoothly.&lt;/p&gt;

&lt;p&gt;To identify tables requiring vacuuming, run the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;relname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_dead_tup&lt;/span&gt;  
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_stat_all_tables&lt;/span&gt;  
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;n_dead_tup&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgmwksndc0jpgru5fe426.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgmwksndc0jpgru5fe426.png" alt="An image illustrating how vacuum works" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that despite being necessary for a healthy database, &lt;code&gt;VACUUM&lt;/code&gt; can have damaging effects when misused. For instance, using the &lt;code&gt;VACUUM FULL&lt;/code&gt; command on the production database can lock it for some time. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Forgetting to Close Connections
&lt;/h3&gt;

&lt;p&gt;Every time you open a connection, either to fetch or update data, it takes time and utilizes resources like memory and CPU. If these connections are not closed properly after use, they remain open and idle, consuming system resources and eventually running out of the database’s connection limit. This is called a connection leak and can result in errors and downtimes.&lt;/p&gt;

&lt;p&gt;One common cause of poor database performance is idle connections. Most developers new to Postgres think that open connections are just idle connections that are not doing anything, but this is incorrect because they’re consuming server resources.&lt;/p&gt;

&lt;p&gt;When a connection is not closed, it can trigger an increase in resource consumption, lock tables or rows, and even stop the execution of other queries. This can lead to degradation in database performance over time or even crash your application.&lt;/p&gt;

&lt;p&gt;To prevent connection leaks, you must ensure that every connection opened is correctly closed after use. You can do this manually in your code or by using connection pooling tools like &lt;a href="https://www.pgbouncer.org/" rel="noopener noreferrer"&gt;PgBouncer&lt;/a&gt;, which manage connections efficiently and prevent resource exhaustion.&lt;/p&gt;

&lt;p&gt;Here's a sample Python code snippet to properly handle PostgreSQL connections and avoid connection leaks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;

&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dbname=test user=postgres password=secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Writing Inefficient Queries
&lt;/h3&gt;

&lt;p&gt;If you’re working with a large number of rows from a big table, you need to be careful while writing queries because it can be tricky. An unoptimized query may scan the entire table or touch significantly more rows than necessary. &lt;/p&gt;

&lt;p&gt;Inefficient queries are often the result of poorly written SQL, missing indexes, or a lack of understanding of how PostgreSQL executes queries. When a query hits too many rows, it drives up disk I/O and uses up more CPU and memory (thus decreasing available resources for other queries). Eventually, this will limit the overall performance of your database and your application.&lt;/p&gt;

&lt;p&gt;Wrong queries are common among beginners. For example, &lt;code&gt;SELECT *&lt;/code&gt; without &lt;code&gt;WHERE&lt;/code&gt; or &lt;code&gt;INNER&lt;/code&gt; or joining two large tables without proper indexes causes PostgreSQL to scan and process more data than necessary, leading to performance issues.&lt;/p&gt;

&lt;p&gt;Here are some useful tips for writing better-optimized queries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use Indexes&lt;/strong&gt;: Indexes are one of the most effective ways to speed up queries. They allow PostgreSQL to quickly locate rows without scanning the entire table. For example, if you frequently query a user table by email, create an index on the email column:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_users_email&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Avoid &lt;code&gt;SELECT *&lt;/code&gt;&lt;/strong&gt;: Fetching all columns when you only need a few can increase the amount of data processed and transferred. This is called the wildcard frenzy. The wildcard might seem enticing, as it displays all data at once, but this approach is not recommended. It’s like going through a lot of things you don’t need to find what you’re looking for, and that’s not efficient. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best way to query data is to be specific. Rather than returning all of the data from the database and filtering post-query, ask for the data columns you actually need. This makes queries faster and your results clearer.&lt;br&gt;
Rather than writing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'test@example.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead, specify only the columns you need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'test@example.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use &lt;code&gt;LIMIT&lt;/code&gt;&lt;/strong&gt;: When working with small tables, you may not need to worry about the number of returned rows. However, when working with more oversized tables, &lt;code&gt;LIMIT&lt;/code&gt; is beneficial for improving query performance. You can achieve that by using the LIMIT command.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Forgetting to Add Primary Keys, Impacting Data Integrity
&lt;/h3&gt;

&lt;p&gt;Whenever you create tables in your Postgres database, define primary keys. Without a primary key, the table lacks a unique identifier for each row, making it impossible to enforce data integrity and leading to problems such as duplicate rows. Over time, this can lead to inconsistent data, ruptured relationships, and unreliable query results.&lt;/p&gt;

&lt;p&gt;Most novices skip creating a primary key for a table, either because they are unaware of its necessity or they mistakenly think Postgres will provide some unique enforcement on its own. However, this assumption can lead to serious problems, especially as your database grows and becomes more complex.&lt;/p&gt;

&lt;p&gt;To avoid these problems, continually assign a primary key for each table. This will help you maintain the integrity of your data, ensure good performance in your queries, and have consistent relationships in your database.&lt;/p&gt;

&lt;p&gt;Here are some best practices to prevent data integrity issues emerging from missing primary keys: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Always Define a Primary Key&lt;/strong&gt;: Include a primary key that uniquely identifies each row when defining a table. For example, in an orders table, you can use order_id as the primary key:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use Composite Keys&lt;/strong&gt;: Sometimes, using a single column isn’t sufficient to uniquely identify a row. In such cases, use a composite key (a combination of columns) as the primary key. For example, in an &lt;code&gt;order_items&lt;/code&gt; table, you might use both &lt;code&gt;order_id&lt;/code&gt; and &lt;code&gt;product_id&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;order_items&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product_id&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;ul&gt;
&lt;li&gt;
&lt;strong&gt;Add Primary Keys to Existing Tables&lt;/strong&gt;: If you have already created a table without a primary key, you can add one later using the ALTER TABLE command. For example:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Overcomplicating Schema Design
&lt;/h3&gt;

&lt;p&gt;A database schema is any structure that you define around the data. This includes views, tables, relationships, fields, indexes, functions, and other elements. Without any of these, getting lost in a database is easy. &lt;/p&gt;

&lt;p&gt;Most beginners are tempted to design a database schema with hundreds of tables, complex relationships, and constraints to guarantee flexibility and scalability. This practice often leads to an overcomplicated schema design that is difficult to maintain and inefficient to query. Overcomplicating schema design usually results in reduced performance and an increased developer learning curve.&lt;/p&gt;

&lt;p&gt;When designing your schema, it is essential to maintain a balance between &lt;a href="https://en.wikipedia.org/wiki/Database_normalization#:~:text=Database%20normalization%20is%20the%20process,part%20of%20his%20relational%20model." rel="noopener noreferrer"&gt;normalization&lt;/a&gt; and simplicity. Although data normalization reduces redundancy and improves data integrity, it can also increase complexity. For example, splitting a single logical entity into multiple tables or adding too many foreign key relationships can make queries harder to write and slower to execute.&lt;/p&gt;

&lt;p&gt;To avoid complicating the schema too much, consider simplicity and practicality. Start with a base design that fulfills your immediate needs and expand it as your application grows. Keeping your schema clean and intuitive will make it easier to maintain and query your database.&lt;/p&gt;

&lt;p&gt;Here are some helpful tips for creating less complex schemas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start Simple&lt;/strong&gt;: Begin with a minimal schema that addresses your immediate needs. Avoid adding tables or columns that you don’t currently require. For example, if you’re building a user table, start with basic fields like &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, and &lt;code&gt;email&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Normalize Thoughtfully&lt;/strong&gt;: Normalization is important, but don’t overdo it. For example, splitting a user table into &lt;code&gt;user_profiles&lt;/code&gt;, &lt;code&gt;user_emails&lt;/code&gt;, and &lt;code&gt;user_addresses&lt;/code&gt; might seem like a good idea, but it can lead to unnecessary complexity. Instead, keep related data together unless there’s a compelling reason to separate it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid Overusing Foreign Keys&lt;/strong&gt;: While foreign keys are essential for maintaining relationships between tables, using them excessively can complicate your schema. For example, creating a separate table for every one-to-many relationship can result in too many joins. Instead, consider embedding related data directly in a table when appropriate.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4o48uz946a4vlbuaccko.png" alt="illustrating complex vs simple schema" width="800" height="431"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Overlooking the Importance of Backups
&lt;/h3&gt;

&lt;p&gt;Data loss is a nightmare scenario, whether caused by a malicious attack, human error, or a software bug. One of the common mistakes that most developers new to Postgres or working with databases make is overlooking the importance of backups, and the consequences could be devastating. &lt;/p&gt;

&lt;p&gt;The most critical aspects of database management are implementing and regularly maintaining a reliable backup strategy. When disaster hits, and it ultimately will, you'll be incredibly grateful that you took the time to plan ahead and back up your valuable data.&lt;/p&gt;

&lt;p&gt;There are different ways to backup your data, including using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pg_dump&lt;/code&gt;, a command-line utility for backing up PostgreSQL databases. It can generate logical backups (SQL scripts) or physical backups (binary replicas of the data files). Logical backups are usually more flexible, while physical backups are faster to restore.
Here’s an example of how to use it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Logical&lt;/span&gt; &lt;span class="n"&gt;backup&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plain&lt;/span&gt; &lt;span class="k"&gt;SQL&lt;/span&gt; &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pg_dump&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;U&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="n"&gt;database_name&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;backup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;sql&lt;/span&gt;

&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Physical&lt;/span&gt; &lt;span class="n"&gt;backup&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pg_dump&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;U&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Fc&lt;/span&gt; &lt;span class="n"&gt;database_name&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;backup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dump&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Third-party tools like &lt;a href="https://www.pgbarman.org/" rel="noopener noreferrer"&gt;Barman&lt;/a&gt; or &lt;a href="https://devcenter.heroku.com/articles/heroku-postgres-backups" rel="noopener noreferrer"&gt;pgBackups&lt;/a&gt; provide advanced backup and recovery capabilities for PostgreSQL. These tools make managing backups easier and offer granularity in the backup process.&lt;/li&gt;
&lt;li&gt;A managed PostgreSQL service (e.g., Amazon RDS, Google Cloud SQL, Azure Database for PostgreSQL, and Neon). One that is a beginner-friendly managed Postgres database is Neon. The following section will discuss how Neon simplifies database management for beginners, especially for backups and schema design.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcki0i3uyjb4sgn1yy29w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcki0i3uyjb4sgn1yy29w.png" alt="image illustrating backup" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tackling Schema Changes, Autoscaling, and Backups the Smart Way
&lt;/h2&gt;

&lt;p&gt;Managing a large database has a few drawbacks: schema upgrades will introduce downtime, backups will be problematic, and scaling will require a lot of hands-on effort. This can lead to insecurity, data loss, downtime, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://neon.tech/" rel="noopener noreferrer"&gt;Neon&lt;/a&gt; helps mitigate such issues with a serverless PostgreSQL solution that prioritizes flexibility and resilience. One of the biggest challenges of working with databases is schema changes, especially with growing applications. Traditional database migrations may be risky and cause downtime or inconsistencies. Neon supports schema migration tools such as &lt;a href="https://alembic.sqlalchemy.org/" rel="noopener noreferrer"&gt;SQLAlchemy or Alembic&lt;/a&gt;, &lt;a href="https://www.red-gate.com/products/flyway/community/" rel="noopener noreferrer"&gt;Flyway&lt;/a&gt;, &lt;a href="https://www.liquibase.com/" rel="noopener noreferrer"&gt;Liquibase&lt;/a&gt;, and so on. This allows developers to perform structured migrations with minimal or no user interruption.&lt;/p&gt;

&lt;p&gt;Another challenge of working with a database is ensuring the database scales efficiently as the workload fluctuates. Neon handles autoscaling to enable your database to withstand workload changes automatically without the need for manual intervention.&lt;/p&gt;

&lt;p&gt;Additionally, manually maintaining data and recovering it quickly is a common database challenge. Neon has automatic backups and point-in-time recovery, so if anything goes wrong, like a failing migration or a mistake with a delete, you can quickly restore the data.&lt;/p&gt;

&lt;p&gt;There are three main ways to perform backups in Neon:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Point-in-Time Restore (PITR)&lt;/strong&gt;: Point-in-time recovery allows you to restore your database to a specific moment in time. It requires backing up live database files and archiving the Write Ahead Log (&lt;a href="https://www.postgresql.org/docs/current/wal-intro.html" rel="noopener noreferrer"&gt;WAL&lt;/a&gt;), which is the log of all the modifications happening on the database. Neon automatically backs up all of the data with &lt;a href="https://neon.tech/docs/introduction/point-in-time-restore" rel="noopener noreferrer"&gt;Point-in-Time Restore&lt;/a&gt; (PITR), which helps users restore data to any specific point in time. Neon achieves this by retaining a history of all branches without explicitly taking backups. This is crucial for data integrity, guaranteeing your apps’ continuity in case of accidental data deletion and failed login attempts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual Backup Using &lt;code&gt;pg_dump&lt;/code&gt;&lt;/strong&gt;: Like in regular PostgreSQL databases, Neon supports manual backup using &lt;code&gt;pg_dump&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated Backups through GitHub Actions&lt;/strong&gt;: Neon allows for planning automated remote backups (e.g., on Amazon S3) using GitHub Actions to back up sensitive data. You can read more about using GitHub Actions for automated backups &lt;a href="https://neon.tech/docs/manage/backups#backups-with-pgdump" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each method serves different needs: PITR is best for quick recovery, &lt;code&gt;pg_dump&lt;/code&gt; for full database snapshots, and GitHub Actions for automated external backups.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;There are many mistakes that might make you feel discouraged when getting started with Postgres. Don't be; there are several resources and tools available to help you learn more about Postgres. One such tool is Neon, a serverless solution that helps reduce the performance impact of vacuuming, provides autoscaling, offers a branching feature for backups, and many more. &lt;/p&gt;

&lt;p&gt;Remember that making mistakes is a major part of the learning process. Don’t be afraid to make mistakes; they help you improve.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;For more information on the tools and concepts used in this guide, refer to the following resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/ef/core/" rel="noopener noreferrer"&gt;Entity Framework Core Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://neon.tech/docs/introduction" rel="noopener noreferrer"&gt;Neon Postgres&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://neon.tech/docs/guides/branch-restore" rel="noopener noreferrer"&gt;Neon Branch Restore with Time Travel Assist&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>How to safely create a read replica of production for analytics and data teams</title>
      <dc:creator>Dalu46</dc:creator>
      <pubDate>Wed, 15 Jan 2025 23:18:10 +0000</pubDate>
      <link>https://forem.com/hackmamba/how-to-safely-create-a-read-replica-of-production-for-analytics-and-data-teams-199h</link>
      <guid>https://forem.com/hackmamba/how-to-safely-create-a-read-replica-of-production-for-analytics-and-data-teams-199h</guid>
      <description>&lt;p&gt;Analyzing real-time production data can significantly improve decision-making and business intelligence capabilities. However, direct queries to production databases may result in data loss and performance issues. This is where a read replica is most beneficial. A read replica is a solution that engineers can leverage to guarantee that business intelligence teams can aggressively query data without compromising the integrity of primary production systems.&lt;/p&gt;

&lt;p&gt;The general idea of using read replicas involves segregating read-only work from production database operations, which serve multiple purposes. One of these is the ability to distribute read requests across multiple read replica compute instances. This allows for easy application scaling and significantly increases throughput for both read-write and read-only workloads.&lt;/p&gt;

&lt;p&gt;This tutorial will guide you through the steps to set up a read-only replica database on Neon, a fully managed serverless Postgres database that simplifies the creation and management of databases and their replicas. After setting up the read replica, we will use it with Metabase, a GUI capable of parsing and presenting data.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://neon.tech/docs/get-started-with-neon/signing-up#:~:text=Simply%20navigate%20to%20https%3A%2F%2F,name%2C%20and%20select%20a%20region." rel="noopener noreferrer"&gt;Neon paid plan account&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://store.metabase.com/checkout" rel="noopener noreferrer"&gt;Metabase account&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Basic knowledge of PostgreSQL&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating a Neon project
&lt;/h2&gt;

&lt;p&gt;One requirement for following this tutorial is having a Neon project. We’d create a simple e-commerce project with three tables: Orders, Products, and Reviews. Let’s get started.&lt;/p&gt;

&lt;p&gt;To create a Neon project, first sign up on Neon. On the Neon console, click on the &lt;strong&gt;New Project&lt;/strong&gt; button. Fill in the &lt;strong&gt;Project Creation&lt;/strong&gt; form and click on &lt;strong&gt;Create&lt;/strong&gt; &lt;strong&gt;p&lt;/strong&gt;&lt;strong&gt;roject&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pslo5o13egevkru9nvf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pslo5o13egevkru9nvf.png" alt="neon dashboard" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note: When you create a project in Neon, a ready-to-use database named &lt;strong&gt;neondb&lt;/strong&gt; is automatically created within the Postgres cluster. You can then create additional databases as needed within the project. &lt;/p&gt;

&lt;p&gt;Once the project is successfully created, navigate to the tab on the left side of the screen and click on &lt;strong&gt;SQL Editor.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fznwr1ekyj6iug13v03ag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fznwr1ekyj6iug13v03ag.png" alt="neon dashboard" width="800" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Paste the following command to create the &lt;strong&gt;Orders,&lt;/strong&gt; &lt;strong&gt;Products,&lt;/strong&gt; and &lt;strong&gt;Reviews tables&lt;/strong&gt;, respectively:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Orders table:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;
    &lt;span class="c1"&gt;-- Create the orders table&lt;/span&gt;
    &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;Orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;OrderID&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CustomerEmail&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;OrderDate&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;OrderTotal&lt;/span&gt; &lt;span class="nb"&gt;DECIMAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;-- Insert the orders data&lt;/span&gt;
    &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;Orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OrderID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CustomerEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OrderDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OrderTotal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ORD00001'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'john.smith@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-05-15'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;125&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Shipped'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ORD00002'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'mary.jones@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-05-12'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Processing'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ORD00003'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'david.lee@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-05-10'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;219&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Completed'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ORD00004'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'jane.doe@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-05-08'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Awaiting Payment'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ORD00005'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'william.chen@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-05-07'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'On Hold'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ORD00006'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'sarah.miller@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-05-05'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Cancelled'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ORD00007'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'michael.brown@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-05-03'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Returned'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ORD00008'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'jennifer.williams@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-05-01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;87&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Delivered'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ORD00009'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'daniel.garcia@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-04-29'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Partially Shipped'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ORD00010'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'elizabeth.taylor@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-04-27'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Shipped'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Products table:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;    &lt;span class="c1"&gt;-- Create the table&lt;/span&gt;
    &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;Products&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;ProductID&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SKU&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Category&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Price&lt;/span&gt; &lt;span class="nb"&gt;DECIMAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Stock&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;-- Insert the data&lt;/span&gt;
    &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;Products&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ProductID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SKU&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Stock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PRO00001'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'123ABC'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Wireless Noise-Cancelling Headphones'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Electronics'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;199&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PRO00002'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'DEF456'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Running Shoes (Men&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;s)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Clothing'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PRO00003'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'GHI789'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Coffee Maker (Smart)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Appliances'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;129&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PRO00004'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'JKL012'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Yoga Mat'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Fitness'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PRO00005'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'MNO345'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Wireless Phone Charger'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Electronics'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PRO00006'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'PQR678'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Backpack (Travel)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Accessories'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PRO00007'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'STU901'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Hardcover Notebook'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Office Supplies'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;-- Unlimited stock represented as NULL&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PRO00008'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'VWX234'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Beard Trimmer (Electric)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Personal Care'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PRO00009'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'YXZ567'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Air Purifier'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Home &amp;amp; Garden'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PRO00010'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'CBA123'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Stainless Steel Water Bottle'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Kitchen &amp;amp; Dining'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Reviews table:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;    &lt;span class="c1"&gt;-- Create the reviews table&lt;/span&gt;
    &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;Reviews&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;ReviewID&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;-- Can be alphanumeric for unique identifier&lt;/span&gt;
      &lt;span class="n"&gt;ProductID&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;CustomerName&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;Rating&lt;/span&gt; &lt;span class="nb"&gt;DECIMAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;-- Decimal for rating (1.0 to 5.0)&lt;/span&gt;
      &lt;span class="n"&gt;ReviewText&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;-- Text allows for longer reviews&lt;/span&gt;

      &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ProductID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ProductID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;-- Links reviews to products&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;-- Insert the orders data&lt;/span&gt;
    &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;Reviews&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ReviewID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ProductID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CustomerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Rating&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ReviewText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;VALUES&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'REV00001'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Alice Johnson'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&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="s1"&gt;'These headphones are amazing! The noise cancellation is incredible and the sound quality is top-notch.'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'REV00002'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'David Lee'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&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="s1"&gt;'Great shoes for running, very comfortable and lightweight. However, the size runs a bit small, so I recommend ordering a half size up.'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'REV00003'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Sarah Miller'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&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="s1"&gt;'The coffee maker works well, but the app can be a bit buggy sometimes. It would be great if they could improve the connectivity.'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'REV00004'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Michael Brown'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&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="s1"&gt;'Love this yoga mat! Perfect thickness and grip for my workouts. It also folds up nicely for easy storage.'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'REV00005'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Jennifer Williams'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'This phone charger is super convenient. No more fumbling with wires! The only downside is that the charging speed could be a bit faster.'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'REV00006'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Daniel Garcia'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&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="s1"&gt;'This backpack is great for travel. Its spacious enough to fit everything I need, and the straps are comfortable for carrying even when its full.'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'REV00007'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Elizabeth Taylor'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&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="s1"&gt;'This hardcover notebook is exactly what I was looking for. The paper quality is excellent, and the cover feels very durable.'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'REV00008'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'William Chen'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'The beard trimmer works well, but its a bit noisy. Otherwise, its a good value for the price.'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'REV00009'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'John Smith'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&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="s1"&gt;'The air purifier is definitely making a difference in the air quality of my home. I can already breathe easier.'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'REV00010'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Mary Jones'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&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="s1"&gt;'This stainless steel water bottle is perfect for keeping my drinks cold all day long. Its also very stylish and leakproof.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have successfully created our e-commerce Neon project with three tables: Orders, Products, and Reviews. We’ve also seeded the database with sample data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a read replica for the database
&lt;/h2&gt;

&lt;p&gt;Neon &lt;a href="https://neon.tech/docs/introduction/read-replicas" rel="noopener noreferrer"&gt;read replicas&lt;/a&gt; are dedicated instances for handling read-only operations on existing data. Unlike traditional methods, Neon doesn't copy data. Instead, Neon's architecture routes read requests to a single source. Adding a read replica to a Neon project does not require additional storage.&lt;/p&gt;

&lt;p&gt;Creating a read replica is just adding a read-only compute endpoint to a Neon branch. You can create multiple read replicas per branch. Connecting to a read replica is the same as connecting to any branch, but you use a read-only compute endpoint instead of a read-write one.&lt;/p&gt;

&lt;p&gt;In the Neon Console, select &lt;strong&gt;Branches.&lt;/strong&gt; Select the branch where your database resides and click on &lt;strong&gt;Add.&lt;/strong&gt; You’ll be prompted with a &lt;strong&gt;Create Compute Endpoint&lt;/strong&gt; dialog. Select &lt;strong&gt;Read-only replica&lt;/strong&gt; as the &lt;strong&gt;Compute type&lt;/strong&gt; and click on &lt;strong&gt;Create.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Note: You can specify the &lt;strong&gt;Compute size&lt;/strong&gt; options by configuring a Fixed-Size compute with a precise amount of vCPU and RAM (the default) or choosing a minimum and maximum compute size to enable autoscaling. Additionally, you can adjust the &lt;strong&gt;Suspend compute&lt;/strong&gt; after a period-of-inactivity parameter, determining how long your read-only compute will remain inactive before being automatically suspended. The default setting is five minutes.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzdkbmcl33h89clvpfemb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzdkbmcl33h89clvpfemb.png" alt="neon console" width="800" height="859"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have successfully created a read-only replica for our database. To view the read replicas for a branch, select &lt;strong&gt;Branches&lt;/strong&gt; in the Neon Console and select the branch where you created the replica. Your read replicas are identified in the &lt;strong&gt;Type&lt;/strong&gt; field under the &lt;strong&gt;Computes&lt;/strong&gt; header. Read replicas have a &lt;code&gt;R/O&lt;/code&gt; value instead of an &lt;code&gt;R/W&lt;/code&gt; value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retrieving the read replica connection string
&lt;/h2&gt;

&lt;p&gt;Connecting to a read replica in a Neon project is similar to connecting to any branch. The only difference is that we use a read-only compute endpoint instead of a read-write one. The read replica connection string will connect our read replica to Metabase, enabling us to perform analytics on our replica data without affecting the main data.&lt;/p&gt;

&lt;p&gt;To get the read replica connection string, select &lt;strong&gt;Branches&lt;/strong&gt; on the Neon Console, and on the branch where you created the read replica, click on the ellipsis menu and click on &lt;strong&gt;Connect&lt;/strong&gt; like so:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu3y8bt71vcrp6w5rvva2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu3y8bt71vcrp6w5rvva2.png" alt="neon console" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll be prompted with a modal. Copy the connection string and save it somewhere. We will need it to connect our read replica to Metabase.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;PostgreSQL&lt;/strong&gt; connection string is similar to the following: &lt;code&gt;postgresql://neondb_owner:************@ep-silent-sound-n9ittsn1.us-west-2.aws.neon.tech/neondb?sslmode=require.&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting your read replica to Metabase
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.metabase.com/" rel="noopener noreferrer"&gt;Metabase&lt;/a&gt; is an open-source business intelligence (BI) tool that allows users to explore, analyze, and visualize data from various sources. We are going to leverage Metabase to visualize our Neon read replica database. &lt;/p&gt;

&lt;p&gt;To add a database connection, click on the settings icon in the top right of the Metabase dashboard, navigate to &lt;strong&gt;Admin settings&lt;/strong&gt;, click on &lt;strong&gt;Databases&lt;/strong&gt;, and then click &lt;strong&gt;Add a database&lt;/strong&gt;. We will be prompted with a Metabase's setup field. Fill out the field with the connection string from the Neon console. The string can be broken down like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;    &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;connection&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="n"&gt;breakdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;]:[&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;neon_hostname&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dbname&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'PostgreSQL'&lt;/span&gt;
    &lt;span class="n"&gt;DISPLAY&lt;/span&gt; &lt;span class="n"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'PostgSQL database'&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;display&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="k"&gt;database&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Metabase&lt;/span&gt; &lt;span class="n"&gt;interface&lt;/span&gt;
    &lt;span class="k"&gt;HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'neon_hostname'&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;eg&lt;/span&gt; &lt;span class="s1"&gt;'ep-silent-sound-n9ittsn1.us-west-2.aws.neon.tech'&lt;/span&gt;
    &lt;span class="n"&gt;PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'5432'&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="n"&gt;postgress&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;
    &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'dbname'&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;eg&lt;/span&gt; &lt;span class="s1"&gt;'neondb'&lt;/span&gt;
    &lt;span class="n"&gt;USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'user'&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;eg&lt;/span&gt; &lt;span class="s1"&gt;'neondb_owner'&lt;/span&gt;
    &lt;span class="n"&gt;PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;eg&lt;/span&gt; &lt;span class="s1"&gt;'JOrmPQz34tAf'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdqvnno99s6sdq49u4lgt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdqvnno99s6sdq49u4lgt.png" alt="metabase dashboard" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set the &lt;strong&gt;SSL&lt;/strong&gt; to &lt;code&gt;require&lt;/code&gt;. This ensures that your connection to the Neon database is encrypted for secure data transmission. Neon requires that all connections use SSL/TLS encryption. &lt;/p&gt;

&lt;p&gt;After filling in the details, click &lt;strong&gt;Save.&lt;/strong&gt; If you encounter errors while adding the Neon read replica database to Metabase, check out the &lt;a href="https://www.metabase.com/docs/latest/databases/connections/postgresql#further-reading" rel="noopener noreferrer"&gt;Metabase documentation&lt;/a&gt; on adding a PostgreSQL database. &lt;/p&gt;

&lt;h2&gt;
  
  
  Performing analysis on Metabase
&lt;/h2&gt;

&lt;p&gt;We can analyze the data now that we have successfully added our read replica database to Metabase. &lt;/p&gt;

&lt;p&gt;On the Metabase dashboard, navigate to &lt;strong&gt;Browse data.&lt;/strong&gt; This will show us all the databases we have created or added. Click on the &lt;strong&gt;PostgreSQL database&lt;/strong&gt;, which we just added. We can now see the Orders, Products, and Review tables we created on the Neon console. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwz2tu1utafkr1sxdo99q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwz2tu1utafkr1sxdo99q.png" alt="metabase dashboard" width="800" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One thing to note is that this isn’t the actual database but a replica of the database with read-only access. Now, we can query, analyze, and do anything else with this data without affecting the original database. This is the key aspect of the read replica feature.&lt;/p&gt;

&lt;p&gt;Let's analyze the total sum of the order by order date. We can achieve this by clicking on the &lt;strong&gt;Orders&lt;/strong&gt; section and selecting the &lt;strong&gt;Order Total&lt;/strong&gt; field. Finally, applying the &lt;strong&gt;Sum over time&lt;/strong&gt; function will give us the desired aggregated daily order totals.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvkgqqgtvjw1a7af19ba9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvkgqqgtvjw1a7af19ba9.png" alt="metabase dashboard" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The read replica database stays in sync with the primary database. It is also asynchronous, meaning it is updated with changes made by a read-write compute as soon as those changes are processed by &lt;a href="https://neon.tech/docs/introduction/architecture-overview" rel="noopener noreferrer"&gt;Safekeepers&lt;/a&gt; and &lt;a href="https://neon.tech/docs/reference/glossary#pageserver" rel="noopener noreferrer"&gt;Pageservers&lt;/a&gt;. &lt;/p&gt;

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

&lt;p&gt;This tutorial explores creating read replicas in Neon, a serverless PostgreSQL solution. Read replicas offer a dedicated environment for data analysis without compromising production database performance. Unlike traditional methods, Neon doesn't copy data but efficiently routes read requests. &lt;/p&gt;

&lt;p&gt;We built a sample e-commerce project with Neon and created a read replica. We then connected it to Metabase, a BI tool, for data exploration.&lt;/p&gt;

&lt;p&gt;Sign up for a &lt;a href="https://neon.tech/docs/get-started-with-neon/signing-up#:~:text=Simply%20navigate%20to%20https%3A%2F%2F,name%2C%20and%20select%20a%20region." rel="noopener noreferrer"&gt;Neon account&lt;/a&gt; today and leverage the &lt;a href="https://neon.tech/docs/introduction/read-replicas" rel="noopener noreferrer"&gt;read replica&lt;/a&gt; feature to scale your applications and perform in-depth analytics seamlessly.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Create Real-time AI Video Avatar in 7 Minutes</title>
      <dc:creator>Dalu46</dc:creator>
      <pubDate>Sat, 07 Dec 2024 11:10:15 +0000</pubDate>
      <link>https://forem.com/simli_ai/how-to-create-real-time-ai-video-avatar-in-7-minutes-2i29</link>
      <guid>https://forem.com/simli_ai/how-to-create-real-time-ai-video-avatar-in-7-minutes-2i29</guid>
      <description>&lt;p&gt;What if you could turn dull, static text and audio-based content into exciting videos with the help of AI? With &lt;a href="https://www.simli.com/" rel="noopener noreferrer"&gt;AI video avatar generators&lt;/a&gt;, you can easily create high-quality videos that grab your audience's attention starting from simple text or audio.&lt;/p&gt;

&lt;p&gt;These AI video generators can serve several purposes, from being deployed as a customer support agent in your application to give more engaging support and enhance customer satisfaction to being used as an educational tool to engage students in interactive learning environments. They can also be used to create virtual assistants that guide users through getting started with a product or tool without needing to go through the documentation.&lt;/p&gt;

&lt;p&gt;This tutorial will guide you in setting up and implementing real-time AI video avatars using &lt;a href="https://docs.simli.com/introduction" rel="noopener noreferrer"&gt;Simli&lt;/a&gt;, an AI video avatar generator. Simli provides developers with a speech-to-video API to create Lipsynced &lt;a href="https://docs.simli.com/api-reference/available-faces#create-your-own-avatar-app-simli-com" rel="noopener noreferrer"&gt;AI avatars&lt;/a&gt; with lifelike, animated characters, realistic head movements, and synchronized speech.&lt;/p&gt;

&lt;p&gt;Following this guide, you will learn how to quickly create a video avatar from voice inputs, ready to be deployed in interactive projects. So, let's get started right away!&lt;/p&gt;

&lt;p&gt;The complete source code for the project is available on &lt;a href="https://github.com/Dalu46/simli-demo" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;You should have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A basic understanding of JavaScript and React.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/" rel="noopener noreferrer"&gt;Node&lt;/a&gt; and Node Package Manager (NPM) installed on your computer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before setting up the API environment and then moving on to creating a real-time AI video avatar using the Simli API, let's briefly look at the steps needed to create an AI video avatar with Simli.&lt;/p&gt;

&lt;h3&gt;
  
  
  Steps to Create an AI Video Avatar with Simli:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Obtain the API key&lt;/li&gt;
&lt;li&gt;Choose a face ID&lt;/li&gt;
&lt;li&gt;Initialize the Simli client&lt;/li&gt;
&lt;li&gt;Call the &lt;code&gt;simliClient.start()&lt;/code&gt; function to set the WebRTC connection&lt;/li&gt;
&lt;li&gt;Stream audio using &lt;code&gt;sendAudioData()&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Set Up Your API Environment in Minutes
&lt;/h2&gt;

&lt;p&gt;Start by &lt;a href="https://www.simli.com/sign-up-in" rel="noopener noreferrer"&gt;signing up on Simli&lt;/a&gt; to retrieve your API key. For a quick sign-in, you can choose Google.&lt;br&gt;
Once you’ve successfully created an account, you will be redirected to the user profile dashboard, where you can generate your API key and track your API usage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnclkb4siae5nw849wo09.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnclkb4siae5nw849wo09.png" alt="Simli Dashboard" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the icon above to copy your API key and store it securely. After retrieving your API key, select an avatar to display on the frontend.&lt;/p&gt;
&lt;h2&gt;
  
  
  Choose Your AI Avatar
&lt;/h2&gt;

&lt;p&gt;Simli provides sample AI avatars that can be accessed through its &lt;a href="https://docs.simli.com/api-reference/available-faces" rel="noopener noreferrer"&gt;available faces&lt;/a&gt;, with new avatars being added constantly.&lt;/p&gt;

&lt;p&gt;Here are a few of the available faces:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9yxt4u81jb8fufz1ryi0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9yxt4u81jb8fufz1ryi0.png" alt="Simli available faces" width="800" height="630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To get the ID for each face, copy the random text after the name. For example, the ID for Jenna will be &lt;code&gt;tmp9i8bbq7c&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you don’t want to use any available avatars, Simli has a &lt;a href="https://docs.simli.com/api-reference/available-faces#create-your-own-avatar-app-simli-com" rel="noopener noreferrer"&gt;create avatar&lt;/a&gt; tool that lets you create custom avatars simply by uploading images. However, this tutorial will use an existing avatar.&lt;/p&gt;

&lt;p&gt;Now that you have the face ID and the Simli API key, let’s create a Next.js app.&lt;/p&gt;
&lt;h2&gt;
  
  
  Create a Next.js App
&lt;/h2&gt;

&lt;p&gt;To bootstrap a Next.js application, open your terminal, &lt;code&gt;cd&lt;/code&gt; into the directory where you would like to create the application, and run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-next-app@latest simli-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will prompt a few questions about configuring the Next.js application. Here’s what you should respond to each question:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3tnx5cx3haqqjjfjsf8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3tnx5cx3haqqjjfjsf8.png" alt="next.js configuration questions" width="800" height="193"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the response for each question as shown above by pressing &lt;code&gt;enter&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing Dependencies
&lt;/h3&gt;

&lt;p&gt;Next, install the &lt;code&gt;simli-client&lt;/code&gt; and &lt;code&gt;AudioContext&lt;/code&gt; packages by running this code:&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;simli-client standardized-audio-context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://github.com/simliai/simli-client" rel="noopener noreferrer"&gt;SimliClient&lt;/a&gt;, also known as &lt;a href="https://docs.simli.com/api-reference/simli-webrtc" rel="noopener noreferrer"&gt;Simli’s WebRTC&lt;/a&gt; frontend client, is a tool to integrate real-time video and audio streaming capabilities into web applications using WebRTC. This will enable you to avoid the manual &lt;a href="https://webrtc.org/" rel="noopener noreferrer"&gt;WebRTC&lt;/a&gt; setup.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.npmjs.com/package/standardized-audio-context" rel="noopener noreferrer"&gt;AudioContext&lt;/a&gt; is used to downsample the audio and convert it into chunks that the &lt;code&gt;SimliClient&lt;/code&gt; can process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialize the SimliClient in Your Project
&lt;/h2&gt;

&lt;p&gt;In your Next.js application, navigate to the &lt;code&gt;page.js&lt;/code&gt; file and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app//page.js&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// Declare video and audio ref &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;useRef&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="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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Home&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;videoRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;audioRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&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="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;video&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;videoRef&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;autoPlay&lt;/span&gt; &lt;span class="nx"&gt;playsInline&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/video&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;audioRef&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;autoPlay&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/audio&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, a &lt;code&gt;videoRef&lt;/code&gt; and &lt;code&gt;audioRef&lt;/code&gt; was created using the &lt;a href="https://react.dev/reference/react/useRef" rel="noopener noreferrer"&gt;useRef hook&lt;/a&gt; to access the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; HTML elements in the component. The &lt;code&gt;SimliClient&lt;/code&gt; SDK uses &lt;code&gt;videoRef&lt;/code&gt; and &lt;code&gt;audioRef&lt;/code&gt; to attach live WebRTC video and audio streams to these HTML elements. The &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; elements will be used to render the video and audio data from the remote streams on the client side.&lt;/p&gt;

&lt;p&gt;The next step is to configure &lt;code&gt;SimliClient&lt;/code&gt; and pass in the video and audio ref. To do so, paste the following code inside &lt;code&gt;Interview.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="c1"&gt;// src/app//page.js&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// configure the simli client&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;SimliClient&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;simli-client&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;simliClient&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;SimliClient&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;simliConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your api key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;faceID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tmp9i8bbq7c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;handleSilence&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="na"&gt;maxSessionLength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;maxIdleTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;videoRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;videoRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;audioRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;audioRef&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;This block of code creates a new instance of the &lt;code&gt;SimliClient&lt;/code&gt; and a &lt;code&gt;simliConfig&lt;/code&gt; object. Let’s break down each part of the &lt;code&gt;simliConfig&lt;/code&gt; object:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;apiKey&lt;/code&gt;: This is a unique key when creating an account with Simli.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;faceID&lt;/code&gt;: Represents the avatar face ID that will be rendered in the video stream. Simli provides &lt;a href="https://docs.simli.com/api-reference/available-faces" rel="noopener noreferrer"&gt;different avatars&lt;/a&gt;; you can choose one using its face ID.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;handleSilence&lt;/code&gt;: This boolean indicates whether the client should handle silent moments in the audio stream (e.g., muting or pausing the video if no audio is detected).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;maxSessionLength&lt;/code&gt;: Sets the maximum session length (in seconds). Here, it's set to 1 hour (3600 seconds), limiting the duration of any single connection session.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;maxIdleTime&lt;/code&gt;: Sets the maximum idle time (in seconds). The session will disconnect after 600 seconds (10 minutes) without activity.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;videoRef&lt;/code&gt; &lt;code&gt;and&lt;/code&gt; &lt;code&gt;audioRef&lt;/code&gt;: These are references to the video and audio elements where the media streams will be displayed in the browser. SimliClient can connect the WebRTC streams directly to these elements by passing these refs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Start Real-time Streaming with AI Video Avatar
&lt;/h2&gt;

&lt;p&gt;Once you have successfully configured &lt;code&gt;SimliClient&lt;/code&gt;, the next step is establishing the webRTC connection. &lt;/p&gt;

&lt;p&gt;But before that, you need to create a function that will reduce the audio to 16 kHz and break it into smaller pulse-code modulation (PCM) chunks. This guide will use a prerecorded mp3 audio that will be sent to the &lt;code&gt;SimliClient&lt;/code&gt;. You can download and use any audio of your choice. &lt;/p&gt;

&lt;p&gt;Paste the following code inside &lt;code&gt;page.js&lt;/code&gt; file to create the &lt;code&gt;downsampleAndChunkAudio&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app//page.js&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// Downsample the audio to PCM chunks&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;downsampleAndChunkAudio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audioUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chunkSizeInMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Create an AudioContext with a target sample rate of 16kHz&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;audioContext&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;AudioContext&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;sampleRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16000&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="c1"&gt;// Fetch and decode audio file&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audioUrl&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;arrayBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&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;audioBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;audioContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decodeAudioData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Extract PCM data from audio buffer&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawPCM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;audioBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getChannelData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// assuming mono audio for simplicity&lt;/span&gt;
  &lt;span class="c1"&gt;// Calculate chunk size in samples (16-bit PCM)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunkSizeInSamples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunkSizeInMs&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;16000&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;pcmChunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="c1"&gt;// Loop through the raw PCM data and create chunks&lt;/span&gt;
  &lt;span class="k"&gt;for &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;i&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;rawPCM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;chunkSizeInSamples&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;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rawPCM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subarray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;chunkSizeInSamples&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Convert each chunk to Int16Array PCM data&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;int16Chunk&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;Int16Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for &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;j&lt;/span&gt; &lt;span class="o"&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;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&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;int16Chunk&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;32768&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32767&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;32768&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;pcmChunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;int16Chunk&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;pcmChunks&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;This &lt;code&gt;downsampleAndChunkAudio&lt;/code&gt; function takes audio as an argument and processes the audio file by downsampling it to 16 kHz and breaking it into smaller PCM chunks. This format is required for audio to be sent to the &lt;code&gt;SimliClient&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, you have to initialize &lt;code&gt;SimliClinet&lt;/code&gt; and establish the WebRTC connection. To do so, paste the following code inside &lt;code&gt;page.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app//page.js&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// Initialize simli client&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; 

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initializeClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;simliClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;simliConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;simliClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
     &lt;span class="c1"&gt;// setIsInitialized(true);&lt;/span&gt;
     &lt;span class="c1"&gt;// Send audio data in chunks&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pcmChunks&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;downsampleAndChunkAudio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audioUrl&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;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&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;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pcmChunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="c1"&gt;// if (isInitialized &amp;amp;&amp;amp; chunk) {&lt;/span&gt;
         &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;simliClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendAudioData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="c1"&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;pcmChunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PCM &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
     &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;initializeClient&lt;/code&gt; function initializes the SimliClient with the simliConfig object that was earlier declared. It then calls the &lt;code&gt;downsampleAndChunkAudio&lt;/code&gt; function to break the audio into chunks of type PCM16 before sending it to the Simli client.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: The audio data should be of PCM16 type and have a sample rate 16KHz.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Audio_bit_depth#:~:text=Thus%2C%20a%2016%2Dbit%20system,represented%20by%20floating%2Dpoint%20numbers." rel="noopener noreferrer"&gt;PCM16&lt;/a&gt; is a standard audio format ideal for voice processing. When you send this audio format to Simli's API, it helps maintain synchronization between the audio and the avatar's lip movements. This enhances the viewer experience, as it mimics natural speaking in real-time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Render and Integrate the AI Avatar on the Frontend
&lt;/h2&gt;

&lt;p&gt;Now that you have finished building the application, let’s render it on the browser. To do so, open your terminal and run this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will start a local host server on &lt;code&gt;http://localhost:3000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Watch the application in action through &lt;a href="https://m.youtube.com/watch?v=HYR_uA33buc" rel="noopener noreferrer"&gt;this video&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/HYR_uA33buc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;You should checkout this &lt;a href="https://github.com/simliai/create-simli-app-openai" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; to explore a hands-on example on how to integrate Simli's API for building interactive AI avatars.&lt;/p&gt;

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

&lt;p&gt;This quick guide showed how to create a real-time AI video avatar using the Simli API. While this article covered only the basics—such as sending prerecorded audio to the Simli API—Simli offers capabilities that extend far beyond this scope.&lt;/p&gt;

&lt;p&gt;To unlock Simli's full potential, you can enhance your AI video avatars by integrating additional tools like &lt;a href="https://openai.com/" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt; for language models and &lt;a href="https://deepgram.com/" rel="noopener noreferrer"&gt;Deepgram&lt;/a&gt; or &lt;a href="https://elevenlabs.io/" rel="noopener noreferrer"&gt;Elevenlabs&lt;/a&gt; for converting text to speech. These tools work seamlessly with Simli to create more engaging and interactive video experiences.&lt;br&gt;
Check &lt;a href="https://dev.to/simli_ai/turn-job-descriptions-into-recruitment-video-with-ai-4c8b"&gt;this tutorial&lt;/a&gt; for a more advanced use case of Simli. &lt;a href="https://www.simli.com/sign-up-in" rel="noopener noreferrer"&gt;Sign up on Simli&lt;/a&gt; today to get started!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Turn Job Descriptions Into Recruitment Video With AI</title>
      <dc:creator>Dalu46</dc:creator>
      <pubDate>Tue, 19 Nov 2024 13:28:16 +0000</pubDate>
      <link>https://forem.com/simli_ai/turn-job-descriptions-into-recruitment-video-with-ai-4c8b</link>
      <guid>https://forem.com/simli_ai/turn-job-descriptions-into-recruitment-video-with-ai-4c8b</guid>
      <description>&lt;p&gt;According to Statistics by the Genius, 60% of job seekers quit because the interview process is too long or complicated, and the average process takes about 23 days. &lt;/p&gt;

&lt;p&gt;The traditional recruitment process is time-consuming and prone to bias. Instead of interviewing each candidate individually, you can use an AI avatar to simultaneously conduct interviews with all candidates. This way, you no longer need to wait as long as 23 days as the recruitment process can be completed in two to three days.&lt;/p&gt;

&lt;p&gt;This article will guide you through building a recruitment AI video avatar that will interview prospective candidates using Simli’s API. It will show how to build an application that can turn job descriptions into interactive interviews, streamlining your hiring process and improving the candidate experience. &lt;/p&gt;

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

&lt;p&gt;To follow along with this tutorial, make sure you have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An understanding of JavaScript and React.&lt;/li&gt;
&lt;li&gt;Node and the Node Package Manager installed.&lt;/li&gt;
&lt;li&gt;A Simli account. To get started, create a free Simli account.&lt;/li&gt;
&lt;li&gt;An OpenAI account. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find the complete source code on GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate Interview Questions With OpenAPI
&lt;/h2&gt;

&lt;p&gt;OpenAI provides APIs that use a large language model trained on large quantities of data to generate text from a prompt. One of these APIs is the chat completions endpoint. By using this endpoint and providing a job description as a prompt, you can generate customized interview questions. &lt;/p&gt;

&lt;p&gt;Once the questions are generated, the next step is to convert them to audio so they can be sent to the SimliClient API, which will generate a Lipsynced AI avatar to interview the applicant. Simli is an AI video generator that provides you with a speech-to-video API to create video AI avatars.&lt;/p&gt;

&lt;p&gt;Also, OpenAI provides an audio API with a speech endpoint based on its TTS (text-to-speech) model. This speech endpoint requires three key inputs: the model, the text to be converted into audio, and the voice for audio generation. This functionality will be used to convert the generated questions to audio.&lt;/p&gt;

&lt;h3&gt;
  
  
  Here’s How The Application Will Work
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The recruiter enters a job description that is sent to OpenAI’s chat completions API. &lt;/li&gt;
&lt;li&gt;The API returns a list of interview questions as text. &lt;/li&gt;
&lt;li&gt;These questions are then sent to the speech endpoint, where they are converted into audio. &lt;/li&gt;
&lt;li&gt;Finally, the audio is sent to Simli’s API, which generates a realistic, lip-synced video avatar to deliver the questions, creating an interactive and engaging interview experience for candidates.
## Set Up Your API Environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simli’s endpoint requires an API key, which you can get by creating a Simli account. Once you’ve successfully created an account, you will be redirected to the user profile dashboard, where you can generate your API key and track your API usage.&lt;br&gt;
Click the copy button and save it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0z8lkzus3x0m3hr46y4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0z8lkzus3x0m3hr46y4.png" alt="Simli Dashboard" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, create an account on OpenAI to receive the API key. Once you create an account, you’ll be redirected to the profile dashboard. On the dashboard, navigate to the API keys section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F891g1gp5oogsx27jbwpp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F891g1gp5oogsx27jbwpp.png" alt="OpenAI Dashboard" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the API keys section, click Create new secret key, give the key a name, and click Create secret key button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iukb53exxwkrvrhcu8h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iukb53exxwkrvrhcu8h.png" alt="OpenAI Dashboard" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your secret key will now be generated. Be sure to copy and store it in a secure location to retrieve it later easily.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: The OpenAI real-time API is in beta and only available for paying users.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With your API keys ready, you can build your AI-driven interview experience. The next step is selecting an AI avatar that aligns with your brand and hiring role.&lt;/p&gt;
&lt;h2&gt;
  
  
  Choosing Your AI Avatar for Recruitment
&lt;/h2&gt;

&lt;p&gt;When selecting an avatar for your AI recruiter, consider the desired brand image and the role you're hiring for. This tutorial will use the 'Franco' avatar, which was randomly chosen. You can explore Simli's library of avatars to find the perfect fit for your needs.&lt;/p&gt;

&lt;p&gt;Simli has a create avatar tool that allows users to create custom avatars by uploading images. Consider using this feature if none of the available faces suit your needs.&lt;/p&gt;

&lt;p&gt;Here’s a picture of Franco in the red box:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5or94h8ki6914ch7q18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5or94h8ki6914ch7q18.png" alt="Simli’s available avatars" width="800" height="646"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve selected your avatar, let’s bring it to life by building a Next.js application.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting Up the Next.js Project
&lt;/h2&gt;

&lt;p&gt;To get started, create a Next.js app by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-next-app@latest interview-simli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will prompt a few questions about configuring the Next.js application. Here's the response to each question:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxdq0decrbowt5afpx6qz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxdq0decrbowt5afpx6qz.png" alt="Next.js configuration" width="800" height="161"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, navigate to the application and install dependencies. Run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;recruitment-video-app
npm &lt;span class="nb"&gt;install &lt;/span&gt;simli-client openai github:openai/openai-realtime-api-beta
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SimliClient is a tool for integrating real-time audio and video streaming capabilities into your web applications using WebRTC. &lt;/p&gt;

&lt;p&gt;Once the project is set up, run the development server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your Next.js project should now be running at &lt;code&gt;http://localhost:3000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In your project's root directory, create a &lt;code&gt;.env&lt;/code&gt; file and store the Simli and OpenAI API key credentials as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_SIMLI_API_KEY="your simli api key"
NEXT_PUBLIC_OPENAI_API_KEY="your openai api key"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create Real-time Video Interactions With Applicants&lt;/p&gt;

&lt;p&gt;In your project, Navigate to the &lt;code&gt;src/app&lt;/code&gt; folder, create a &lt;code&gt;components&lt;/code&gt; folder, and create an &lt;code&gt;Interview.js&lt;/code&gt; file. This component will set up the interactive interview interface where users can initiate and respond to interview questions generated by an AI avatar.&lt;/p&gt;

&lt;p&gt;First, you need to declare state variables and references that help control and monitor different aspects of the component. To do so, paste the following code snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/components/Interview.js&lt;/span&gt;
&lt;span class="c1"&gt;// State management&lt;/span&gt;
&lt;span class="c1"&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;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isAvatarVisible&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsAvatarVisible&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isRecording&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsRecording&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUserMessage&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="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Refs for various components and states&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;videoRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;audioRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;openAIClientRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;audioContextRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;streamRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processorRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&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="c1"&gt;// New refs for managing audio chunk delay&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;audioChunkQueueRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&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;isProcessingChunkRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code block declares different state variables to track &lt;code&gt;isLoading&lt;/code&gt;, &lt;code&gt;isAvatarVisible&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt; and &lt;code&gt;userMessage&lt;/code&gt; state.&lt;br&gt;
It also creates multiple refs. Let’s look at what each one does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;videoRef&lt;/code&gt; and &lt;code&gt;audioRef&lt;/code&gt; provide direct access to the video and audio elements. They are required for configuring the &lt;code&gt;SimliClient&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;audioContextRef&lt;/code&gt; and &lt;code&gt;processorRef&lt;/code&gt; manage audio processing and encoding, which is critical for capturing and sending audio data.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;audioQueueRef&lt;/code&gt; is a buffer for audio chunks, ensuring seamless audio playback by queuing chunks until they’re ready to be sent. The &lt;code&gt;SimliClient&lt;/code&gt; requires the audio to be sent in chunks in PCM16 format, with a 16KHz sample rate.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Simli Client Initialization
&lt;/h2&gt;

&lt;p&gt;Now, let’s initialize the Simli client. Paste the following code snippet inside the &lt;code&gt;Interview.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/components/Interview.js&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// Initializes the Simli client with the provided configuration&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;initializeSimliClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&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;videoRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;audioRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SimliConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;apiKey&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_SIMLI_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;faceID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;simli_faceid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;handleSilence&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="na"&gt;maxSessionLength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// in seconds&lt;/span&gt;
       &lt;span class="na"&gt;maxIdleTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// in seconds&lt;/span&gt;
       &lt;span class="na"&gt;videoRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;videoRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;audioRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;audioRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;};&lt;/span&gt;
     &lt;span class="nx"&gt;simliClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SimliConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Simli Client initialized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;simli_faceid&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code block above initializes and configures a new instance of the &lt;code&gt;SimliClient&lt;/code&gt;. Let’s break down each part of the &lt;code&gt;SimliConfig&lt;/code&gt; function:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;apiKey&lt;/code&gt;: This is Simli API key.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;faceID&lt;/code&gt;: Represents the avatar face ID that will be rendered in the video stream. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;handleSilence&lt;/code&gt;: This boolean indicates whether the client should handle silent moments in the audio stream (e.g., muting or pausing the video if no audio is detected).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;maxSessionLength&lt;/code&gt;: Sets the maximum session length (in seconds).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;maxIdleTime&lt;/code&gt;: Sets the maximum idle time (in seconds). The session will disconnect after 600 seconds (10 minutes) without activity.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;videoRef&lt;/code&gt; &lt;code&gt;and&lt;/code&gt; &lt;code&gt;audioRef&lt;/code&gt;: These are references to the video and audio elements where the media streams will be displayed in the browser. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  OpenAI Client Initialization
&lt;/h2&gt;

&lt;p&gt;The next step is to initialize the OpenAI client. To do so, paste the following code inside the &lt;code&gt;Interview.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/components/Interview.js&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// Initializes the OpenAI client, sets up event listeners, and connects to the API&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;initializeOpenAIClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&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;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Initializing OpenAI client...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;openAIClientRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RealtimeClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;apiKey&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_OPENAI_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;dangerouslyAllowAPIKeyInBrowser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;openAIClientRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateSession&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;initialPrompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;voice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;openai_voice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;turn_detection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;server_vad&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;input_audio_transcription&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;whisper-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="c1"&gt;// Set up event listeners&lt;/span&gt;
    &lt;span class="nx"&gt;openAIClientRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;conversation.updated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;handleConversationUpdate&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;openAIClientRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;conversation.interrupted&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;interruptConversation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;openAIClientRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input_audio_buffer.speech_stopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;handleSpeechStopped&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// openAIClientRef.current.on('response.canceled', handleResponseCanceled);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;openAIClientRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OpenAI Client connected successfully&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setIsAvatarVisible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error initializing OpenAI client:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Failed to initialize OpenAI client: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;initialPrompt&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function &lt;code&gt;initializeOpenAIClient&lt;/code&gt; initializes the OpenAI client, which will handle real-time conversations with the applicant. The client is set up with an API key and an initial message that welcomes the user to the interview. After that, the event listeners are added to manage mistakes and updates within the conversation. Once the client is configured, &lt;code&gt;isAvatarVisible&lt;/code&gt; is set to true, which makes the avatar appear in the user interface.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Setting &lt;code&gt;dangerouslyAllowAPIKeyInBrowser&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; is generally for development or prototyping, as your OpenAI API key could be exposed to the client-side, which is vulnerable. In a production environment, API calls are better handled on the server side by creating a secure Next.js API route, which keeps your key hidden.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Audio Processing and Sending
&lt;/h2&gt;

&lt;p&gt;You need to create a function to reduce audio to 16 kHz and break it into smaller PCM chunks. To do so, paste the following code inside &lt;code&gt;Interview.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/components/Interview.js&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// Downsamples audio data from one sample rate to another&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;downsampleAudio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;audioData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;inputSampleRate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;outputSampleRate&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;inputSampleRate&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;outputSampleRate&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;audioData&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;ratio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inputSampleRate&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;outputSampleRate&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;newLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audioData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;ratio&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;result&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;Int16Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newLength&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for &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;i&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;newLength&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;audioData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&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;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="p"&gt;};&lt;/span&gt;
 &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sending Audio Data To Simli
&lt;/h2&gt;

&lt;p&gt;To send audio data to &lt;code&gt;SimliClient&lt;/code&gt; for playback, you’ll create a function. To do so, paste the following code snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/components/Interview.js&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// Processes the next audio chunk in the queue.&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;processNextAudioChunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&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;audioChunkQueueRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isProcessingChunkRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;isProcessingChunkRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;audioChunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;audioChunkQueueRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&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;audioChunk&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;chunkDurationMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audioChunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;16000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Calculate chunk duration in milliseconds&lt;/span&gt;
      &lt;span class="c1"&gt;// Send audio chunks to Simli immediately&lt;/span&gt;
      &lt;span class="nx"&gt;simliClient&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;sendAudioData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audioChunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sent audio chunk to Simli:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;chunkDurationMs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Duration:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;chunkDurationMs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ms&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;isProcessingChunkRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nf"&gt;processNextAudioChunk&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;processNextAudioChunk&lt;/code&gt; function checks if there are any chunks in &lt;code&gt;audioQueueRef&lt;/code&gt;. If so, it takes the next chunk, sends it to Simli for playback, and removes it from the queue. This ensures that only one chunk is sent at a time, providing a smooth playback experience for the user without overlapping audio. Then, recursively call the function to process the next chunk in the queue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handle OpenAI Responses
&lt;/h2&gt;

&lt;p&gt;Next, a function will be created to manage responses from the OpenAI API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/components/Interview.js&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// Handles conversation updates, including user and assistant messages&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;handleConversationUpdate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Conversation updated:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;delta&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&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;item&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="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assistant&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Assistant message detected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audio&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;downsampledAudio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;downsampleAudio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;24000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;audioChunkQueueRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;downsampledAudio&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;isProcessingChunkRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;processNextAudioChunk&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;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;item&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="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setUserMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&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;transcript&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;span class="c1"&gt;//Handles interruptions in the conversation flow.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;interruptConversation&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&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 interrupted the conversation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;simliClient&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nc"&gt;ClearBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;openAIClientRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;cancelResponse&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="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above defines two functions: &lt;code&gt;handleConversationUpdate&lt;/code&gt; and &lt;code&gt;interruptConversation&lt;/code&gt;. The &lt;code&gt;handleConversationUpdate&lt;/code&gt; function first checks if the message is from the assistant. Then, it checks If the assistant’s message includes audio data; if true, it uses the &lt;code&gt;downsampleAudio&lt;/code&gt; function to convert the audio to a lower sample rate (24,000 Hz to 16,000 Hz).&lt;/p&gt;

&lt;p&gt;This downsampled audio is added to &lt;code&gt;audioChunkQueueRef.current&lt;/code&gt;, a reference for storing audio chunks. If no chunk is being processed, the &lt;code&gt;processNextAudioChunk()&lt;/code&gt; function (which we have previously declared) is called to start processing the audio chunks.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;interruptConversation&lt;/code&gt; function handles conversation interruption. If the user interrupts the interviewer, the function clears the SimliClient buffer and cancels the ongoing response from the OpenAI API. &lt;/p&gt;

&lt;h2&gt;
  
  
  Audio Recording
&lt;/h2&gt;

&lt;p&gt;Next, let’s create functions to handle audio recording when the prospective candidate is talking. Paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/components/Interview.js&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// Starts audio recording from the user's microphone&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;startRecording&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&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;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;audioContextRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;audioContextRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AudioContext&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;sampleRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24000&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Starting audio recording...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nx"&gt;streamRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mediaDevices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUserMedia&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
       &lt;span class="na"&gt;audio&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;audioContextRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createMediaStreamSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="nx"&gt;streamRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;
     &lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nx"&gt;processorRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;audioContextRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createScriptProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="mi"&gt;1&lt;/span&gt;
          &lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nx"&gt;processorRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onaudioprocess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;inputData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getChannelData&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;audioData&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;Int16Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="k"&gt;for &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;i&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;inputData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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;sample&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inputData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
         &lt;span class="nx"&gt;audioData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sample&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;32767&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
         &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="nx"&gt;openAIClientRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;appendInputAudio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audioData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;};&lt;/span&gt;
     &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;processorRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;processorRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audioContextRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nf"&gt;setIsRecording&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Audio recording started&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;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;Error accessing microphone:&lt;/span&gt;&lt;span class="dl"&gt;"&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="nf"&gt;setError&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 accessing microphone. Please check your permissions.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

&lt;span class="c1"&gt;// Stops audio recording from the user's microphone&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stopRecording&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;stopRecording&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&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;processorRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;processorRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
     &lt;span class="nx"&gt;processorRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;streamRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;streamRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTracks&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;track&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;track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
     &lt;span class="nx"&gt;streamRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="nf"&gt;setIsRecording&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Audio recording stopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s what each function does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;startRecording&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;Requests microphone access, creates an audio context and streams audio data.&lt;/li&gt;
&lt;li&gt;Audio data is captured, converted to PCM format for compatibility, and sent to the OpenAI client for processing.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;stopRecording&lt;/code&gt;: Closes the audio context and disconnects the processor.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Interaction Start and Stop
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/components/Interview.js&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// Handles starting the interaction&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;handleStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&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="nf"&gt;setIsLoading&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="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;simliClient&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;initializeOpenAIClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error starting interaction:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error starting interaction: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nf"&gt;setIsAvatarVisible&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="nf"&gt;setIsLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;initializeOpenAIClient&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

      &lt;span class="c1"&gt;// Handles stopping the interaction, cleaning up resources and resetting states.&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleStop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Stopping interaction...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;setIsLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;setError&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;stopRecording&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;setIsAvatarVisible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;simliClient&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;openAIClientRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&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;audioContextRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;audioContextRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;stopRecording&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;onClose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Interaction stopped&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;stopRecording&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, the &lt;code&gt;handleStart&lt;/code&gt; function initializes the interaction by starting the necessary clients and preparing the interface for recording. By calling the &lt;code&gt;simliClient?.start()&lt;/code&gt; method, the &lt;code&gt;SimliClient&lt;/code&gt; initiates a WebRTC handshake to negotiate a connection between the client and Simli's server.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;handleStop&lt;/code&gt; function stops the interaction by calling the &lt;code&gt;simliClient?.close()&lt;/code&gt;  method, cleaning up used resources, like client connections, and updating the loading and avatar visibility states.&lt;/p&gt;

&lt;h2&gt;
  
  
  Component Mount and Cleanup
&lt;/h2&gt;

&lt;p&gt;Finally, for this component, we need to initialize the &lt;code&gt;simliClient&lt;/code&gt; when the component mounts. Paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/components/Interview.js&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// Effect to initialize Simli client once the component mounts and clean up resources on unmount&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="nf"&gt;initializeSimliClient&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;simliClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;simliClient&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;connected&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="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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SimliClient connected&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;audioData&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;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fill&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;simliClient&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;sendAudioData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audioData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sent initial audio data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;startRecording&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;simliClient&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;disconnected&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="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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SimliClient disconnected&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;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="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;simliClient&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;openAIClientRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&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;audioContextRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;audioContextRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="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;initializeSimliClient&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;useEffect&lt;/code&gt; hook initializes the &lt;code&gt;simliClient&lt;/code&gt; when the component mounts, setting up event listeners for when it connects or disconnects. On connection, it sends a silent audio signal to keep the connection alive and starts recording audio. The cleanup function, triggered on component unmount, closes &lt;code&gt;simliClient&lt;/code&gt;, and disconnects the OpenAI client.&lt;/p&gt;

&lt;p&gt;Next, navigate to the &lt;code&gt;src/app/pages.js&lt;/code&gt; file and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/components/Interview.js&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// configure the avatar and display the home page&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; 
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&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;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;useState&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;Interview&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;./components/Interview&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;Demo&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;jobDescription&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setJobDescription&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;avatar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Frank&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;openai_voice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;alloy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;simli_faceid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5514e24d-6086-46a3-ace4-6a7264e5cb7c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;initialPrompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Your name is Frank, an interviewer hiring for a specific role. You are looking for a candidate whose expertise aligns closely with the following job description: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;jobDescription&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Please generate three interview questions that assess key qualifications and relevant experience. Begin by introducing yourself and asking the interviewee to share a bit about their background.`&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="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bg-black min-h-screen flex flex-col items-center font-abc-repro font-normal text-sm text-white p-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex flex-col items-center mt-4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="nx"&gt;htmlFor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;job-description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;font-bold mb-2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nx"&gt;Add&lt;/span&gt; &lt;span class="nx"&gt;Job&lt;/span&gt; &lt;span class="nx"&gt;Description&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;textarea&lt;/span&gt;
              &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;job-description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
              &lt;span class="nx"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Enter job description, e.g., Responsibilities, Requirements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
              &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;jobDescription&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setJobDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&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="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p-2 border border-gray-300 rounded-md w-80 h-24 resize-none mb-4 text-black&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex flex-col items-center gap-6 bg-effect15White p-6 pb-[40px] rounded-xl w-full&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;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;Interview&lt;/span&gt;
            &lt;span class="nx"&gt;openai_voice&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;openai_voice&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nx"&gt;simli_faceid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;simli_faceid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nx"&gt;initialPrompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initialPrompt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/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;Demo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code handles the job description input from the recruiter and passes it to the initial prompt, the face ID, and the OpenAI voice that should be sent to the OpenAI API. Then, it is passed on as props to &lt;code&gt;Interview.js&lt;/code&gt;. component.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Final Result
&lt;/h2&gt;

&lt;p&gt;To test the app, the user inputs the job description in a text area, which OpenAI API processes. The OpenAI API uses this prompt to generate three questions for the avatar to ask the applicant. &lt;br&gt;
Here's the &lt;a href="https://www.youtube.com/watch?app=desktop&amp;amp;v=X0JVFykPo8c" rel="noopener noreferrer"&gt;video link&lt;/a&gt; to see how the application works: &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/X0JVFykPo8c"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

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

&lt;p&gt;By leveraging SimliClient and OpenAI, this guide provides a comprehensive solution to the problem of time-consuming recruitment by building an application that converts static job descriptions into dynamic, interactive interview videos. Combining these tools is a game-changer because together they can be utilized to automate the initial candidate screening process and ease the stress of going through the traditional recruitment process.&lt;/p&gt;

&lt;p&gt;Simli’s API comes with a free plan. Sign up on Simli today to get started.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>tutorial</category>
      <category>career</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building Secure Private Screens in React</title>
      <dc:creator>Dalu46</dc:creator>
      <pubDate>Thu, 25 Jul 2024 11:43:56 +0000</pubDate>
      <link>https://forem.com/dalu46/building-secure-private-screens-in-react-1ado</link>
      <guid>https://forem.com/dalu46/building-secure-private-screens-in-react-1ado</guid>
      <description>&lt;h1&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Most modern applications require creating private or restricted pages, documents or even features only accessible to authorized users. This could include financial dashboards and collaborative documents to confidential project plans. One thing that is critical in building modern applications is to ensure that only the right users have access to confidential pages in your application.&lt;br&gt;
Traditionally, implementing fine-grained access control in your application requires significant development effort and time. Fortunately, Permit Share-If offers a streamlined solution. By providing pre-built, embeddable UI components for access sharing, Permit Share-If simplifies managing user permissions.&lt;br&gt;
This article will guide you through a step-by-step process of integrating the &lt;a href="http://permit.io/" rel="noopener noreferrer"&gt;Permit.io&lt;/a&gt; request access element into a React application to enable you to build private screens in your application.&lt;/p&gt;
&lt;h1&gt;
  
  
  &lt;strong&gt;Prerequisite&lt;/strong&gt;
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Knowledge of JavaScript, React, and a bit of Nodejs&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://app.permit.io/?utm_source=website&amp;amp;utm_medium=website-home&amp;amp;utm_campaign=nav&amp;amp;_gl=1*1uod5ct*_gcl_au*MjEzNDE4NTIyMC4xNzIxMzg0Njg4*_ga*NTQ3ODI4MTc0LjE3MjEzMTEzMDY.*_ga_SPFKH5NXDE*MTcyMTM4NDU3NC41LjEuMTcyMTM4NDcwOC4wLjAuMA.." rel="noopener noreferrer"&gt;Permit.io account&lt;/a&gt;
# &lt;strong&gt;Authentication vs Authorization&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before going forward, it is important to understand the core difference between authentication and authorization, as some people are still unclear about this.&lt;br&gt;
Authentication is concerned with verifying the identity of a user’s credentials, such as username and password, before granting them access. Conversely, authorization determines what a user can access in a system. Authentication is a prerequisite to authorization.&lt;br&gt;
An example of authentication is logging into an application with a username and password using fingerprints or OTPs. An example of authorization is requesting and getting access to become an editor for a document on Google Docs.&lt;/p&gt;
&lt;h1&gt;
  
  
  &lt;strong&gt;What is&lt;/strong&gt; &lt;a href="http://permit.io/" rel="noopener noreferrer"&gt;&lt;strong&gt;Permit.io&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;?&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://www.permit.io/" rel="noopener noreferrer"&gt;Permit.io&lt;/a&gt; is a full-stack authorization-as-a-service that simplifies managing authorization for any application, eliminating the need for developers to build complex authorization systems from scratch. It supports multiple authorization models, including &lt;a href="https://auth0.com/docs/manage-users/access-control/rbac#:~:text=Role-based%20access%20control%20" rel="noopener noreferrer"&gt;RBAC&lt;/a&gt;(Role-based access control), &lt;a href="https://en.wikipedia.org/wiki/Attribute-based_access_control" rel="noopener noreferrer"&gt;ABAC&lt;/a&gt; (Article-based access control), and &lt;a href="https://en.wikipedia.org/wiki/Relationship-based_access_control" rel="noopener noreferrer"&gt;ReBAC&lt;/a&gt; (Relationship-based access control). It integrates swiftly with your dev workflow, leveraging APIs, Terraform providers, and SDKs for various languages.&lt;br&gt;
In this article, to proceed in building private screens in a react application, we will use a &lt;a href="http://permit.io/" rel="noopener noreferrer"&gt;Permit.io&lt;/a&gt; feature called &lt;a href="https://docs.permit.io/embeddable-uis/element/access-request" rel="noopener noreferrer"&gt;Access Request Element&lt;/a&gt;. This Access-Request Element is a frontend component that enables users to request access to resources, pages on your application, or documents that are restricted or require certain permissions. Once the request is sent from your application, the workspace owner or admin receives it and can decide to approve or deny it.&lt;/p&gt;
&lt;h1&gt;
  
  
  &lt;strong&gt;Implementing Access Request Element&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;As mentioned earlier, the access request element is a frontend component that allows users to send access requests via a dedicated UI embedded into your application. To use the element, you just have to embed it into your frontend application, and then, by clicking the “Request Access” button, users can request access to restricted resources.&lt;br&gt;
Note: It is vital to have a &lt;a href="https://docs.permit.io/embeddable-uis/element/user-management" rel="noopener noreferrer"&gt;User Management Element&lt;/a&gt; in place first to use the Access Request Element. The workspace owner can review submitted access requests in the User Management Element.&lt;br&gt;
To create a User Management element, navigate to the Permit dashboard, and on the sidebar, click on &lt;strong&gt;elements&lt;/strong&gt;, click on C*&lt;em&gt;reate element&lt;/em&gt;* under &lt;strong&gt;User Management.&lt;/strong&gt; Choose the roles for each level and click on &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1%2AX145jN7MyxwfXQWl1gYlVw.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%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1%2AX145jN7MyxwfXQWl1gYlVw.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  &lt;strong&gt;Creating the Request Access Element&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;To create the Request Access Element, navigate to the management element on the new project you just created, click on &lt;strong&gt;elements&lt;/strong&gt; on the sidebar, and under &lt;strong&gt;Access Request,&lt;/strong&gt; click on &lt;strong&gt;Create Element.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1%2ApEDaHqyhcu1HHBUxdumDeA.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%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1%2ApEDaHqyhcu1HHBUxdumDeA.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll be prompted with a dialog box. First, choose the &lt;strong&gt;User Management Element&lt;/strong&gt; that you want to connect to your Access Request Element, in this case, &lt;strong&gt;private-screen&lt;/strong&gt;, then give the element a name and click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1%2Al7a3fcywSFRqtHoyBC1Twg.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%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1%2Al7a3fcywSFRqtHoyBC1Twg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will need to do the same for the User Management element. Navigate to the User Management element and connect the Request Access element.&lt;/p&gt;
&lt;h1&gt;
  
  
  &lt;strong&gt;Generating the iFrame&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;After successfully creating the Request Access element, the next step is to get the generated iframe code and embed it in your application; in this case, your React application.&lt;br&gt;
On the Permit dashboard, under the Access Request element, you’ll find the new &lt;strong&gt;request-access-tut&lt;/strong&gt; element that we created. Click on &lt;strong&gt;Get Code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1%2AJYYmwtphBzLw21jXW71L1w.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%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1%2AJYYmwtphBzLw21jXW71L1w.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;NOTE: It is necessary to select the Tennant appropriate for this element to make it work properly. For this tutorial, we will use the default tenant.&lt;br&gt;
The generated iframe will look similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt;
            &lt;span class="na"&gt;title= &lt;/span&gt;&lt;span class="s"&gt;“Permit&lt;/span&gt; &lt;span class="na"&gt;Element&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;
            &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;https://embed.permit.io/&amp;gt;&amp;lt;ELEMENT_NAME&amp;gt;?envId=&amp;lt;SOME_UNIQUE_ID&amp;gt;&amp;amp;darkMode=false&amp;amp;resourceInstanceKey=&amp;lt;RESOURCE_INSTANCE_KEY&amp;gt;&amp;amp;tenantKey=&amp;lt;TENANT_KEY&amp;gt;"&lt;/span&gt;
            &lt;span class="na"&gt;width= &lt;/span&gt;&lt;span class="s"&gt;“100%”&lt;/span&gt;
            &lt;span class="na"&gt;height= &lt;/span&gt;&lt;span class="s"&gt;“100%”&lt;/span&gt;
            &lt;span class="na"&gt;style= &lt;/span&gt;&lt;span class="s"&gt;“border:&lt;/span&gt; &lt;span class="na"&gt;none&lt;/span&gt;&lt;span class="err"&gt;;”&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The iframe must be embedded after it is generated. You’ll need to establish a login to do that. I’ll guide you through the login process in the next section.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Creating React App&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;To test out the newly generated iframe, let’s bootstrap a simple React app like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-react-app private screen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the required dependency&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install –-save @permitio/permit-js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We first need to configure a backend route using one of the options provided by &lt;a href="http://permit.io/" rel="noopener noreferrer"&gt;permit.io&lt;/a&gt;, which is either cookies, headers, bearer tokens, or a frontend-only method. We’d be using the login with cookie method for this tutorial.&lt;br&gt;
Note: It is important to use the same login method on the backend and frontend.&lt;/p&gt;
&lt;h1&gt;
  
  
  &lt;strong&gt;Creating the backend route&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;I have initialized a backend server using Node.js. In your React application, create a backend folder, and in the backend folder, create a server.js file. Paste the following code into the file, then let’s go over explaining each line of the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Permit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;permitio&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;jav&lt;/span&gt; 
    &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../.env&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&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;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;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;PERMIT_KEY_SECRET&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;PERMIT_KEY_SECRET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// your api key gotten from the permit dashboard&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;USER_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;paul@gmail.com&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;TENANT_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;YOUR_TENANT_ID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// the tenant for your project, usually 'default'&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;permit&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;Permit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PERMIT_KEY_SECRET&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/login_cookie&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;login_cookie&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;ticket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loginAs&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;USER_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TENANT_ID&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;302&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="nx"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redirect_url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Example app listening at &amp;lt;http://localhost&amp;gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the top, I have declared two main variables, which are the PERMIT_KEY_SECRET which is your API key, and TENANT_ID, which is the default tenant. I set the current user as myself but generally, this user will be the one that has been authenticated by your application through an authentication provider.&lt;/p&gt;

&lt;p&gt;To get your Permit API key, go to your dashboard, click on &lt;strong&gt;Projects&lt;/strong&gt;, then click on the project and environment (i.e., development) to connect to. Click on the menu icon on the top right of the environment element and click on &lt;strong&gt;Copy API Key&lt;/strong&gt;. Traditionally, you’d need to store the key in an &lt;a href="https://en.wikipedia.org/wiki/Environment_variable" rel="noopener noreferrer"&gt;env variable&lt;/a&gt;.&lt;br&gt;
After declaring the tenant, the next thing is to initialize Permit, and then define the endpoint /login_cookie using &lt;code&gt;app.get&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, we login the user to Permit using the &lt;code&gt;loginAs&lt;/code&gt; function, which takes two parameters: a &lt;strong&gt;userId&lt;/strong&gt;, and a &lt;strong&gt;tenant_key&lt;/strong&gt; or &lt;strong&gt;tenantId&lt;/strong&gt;.&lt;br&gt;
The route redirects the user to the redirect_url provided in the login ticket using a 302 (Found) status code.&lt;/p&gt;
&lt;h1&gt;
  
  
  &lt;strong&gt;Login the User on the Frontend&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;In the src of the application, create a Login.js file and paste the following code into the file:&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;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="s2"&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;permit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LoginMethod&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;@permitio/permit-js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;btnStyle&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;./Home&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;Login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isLogin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsLogin&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tenantKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default&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;backendUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`http://localhost:8080/login_cookie`&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;login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;loginUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;backendUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tenantKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;loginMethod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LoginMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;})&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;res&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;setIsLogin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;})&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;err&lt;/span&gt;&lt;span class="dl"&gt;"&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="nf"&gt;setIsLogin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="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;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;btnStyle&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}}&lt;/span&gt;
          &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nx"&gt;login&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="nx"&gt;button&lt;/span&gt;
            &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;btnStyle&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nx"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logout&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}}&lt;/span&gt;
          &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nx"&gt;logout&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="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isLogin&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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="nx"&gt;Logged&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&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;Login&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we created an &lt;code&gt;isLogin&lt;/code&gt; state to track when a user is logged in. Then, we use the login method to log in the user to Permit. The login method accepts three parameters: &lt;strong&gt;loginUrl&lt;/strong&gt;, &lt;strong&gt;tenant&lt;/strong&gt;, and a &lt;strong&gt;LoginMethod&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;loginUrl: This is the URL you created in the previous stage (backend).&lt;/li&gt;
&lt;li&gt;LoginMethod: The backend login mechanism that you are utilizing.&lt;/li&gt;
&lt;li&gt;Tenant (Optional): The tenant name that the user belongs to&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the login is successful, we update the i’sLogin “state to true. Finally, we set an onClick handler to enable users to log in and out.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Creating the Access Request and User Management Pages&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Next, create a UserManagement.js file and paste the generated User Management iframe code. The User Management element will be used by the admin to approve or deny users access to the restricted page. After that, create a RequestAccess.js file and paste the generated Request Access iframe code into it.&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="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="err"&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;RequestAccess&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="c1"&gt;// TODO paste your iframe code here&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="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;60vh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;80%&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0 auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;iframe&lt;/span&gt;
            &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Permit Element Name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;https://embed.permit.io/&amp;gt;&amp;lt;ELEMENT_NAME&amp;gt;?envId=&amp;lt;SOME_UNIQUE_ID&amp;gt;&amp;amp;darkMode=false&amp;amp;resourceInstanceKey=&amp;lt;RESOURCE_INSTANCE_KEY&amp;gt;&amp;amp;tenantKey=&amp;lt;TENANT_KEY&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;100%&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;100%&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/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;RequestAccess&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;Now&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;paste&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;following&lt;/span&gt; &lt;span class="nx"&gt;code&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;Home&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;nav&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useNavigate&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;Login&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;div&lt;/span&gt;
            &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
              &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;justifyContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;alignItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&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="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
                &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;btnStyle&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="nf"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/request-access`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}}&lt;/span&gt;
              &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nx"&gt;View&lt;/span&gt; &lt;span class="nx"&gt;Private&lt;/span&gt; &lt;span class="nx"&gt;Screen&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/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;Home&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the Home component, we’ve created a View Private Screen button and set the navigation to the RequestAccess page. Once a user clicks on it, it directs them to first request for access, and when they are granted access, it redirects them to the restricted page.&lt;br&gt;
Now create a &lt;code&gt;PrivatePage.js file&lt;/code&gt; and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="err"&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;PrivatePage&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="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="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="nx"&gt;vh&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nx"&gt;auto&lt;/span&gt;&lt;span class="err"&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="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;This&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;Private&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;You&lt;/span&gt; &lt;span class="nx"&gt;are&lt;/span&gt; &lt;span class="nx"&gt;only&lt;/span&gt; &lt;span class="nx"&gt;here&lt;/span&gt; &lt;span class="nx"&gt;because&lt;/span&gt; &lt;span class="nx"&gt;you&lt;/span&gt; &lt;span class="nx"&gt;were&lt;/span&gt; &lt;span class="nx"&gt;granted&lt;/span&gt; &lt;span class="nx"&gt;access&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;PrivatePage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;Finally&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paste&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;following&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="nx"&gt;into&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="s2"&gt;`App.js`&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nx"&gt;I&lt;/span&gt; &lt;span class="nx"&gt;explain&lt;/span&gt; &lt;span class="nx"&gt;below&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;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;useState&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="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;RequestAccess&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;./RequestAccess&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;PrivatePage&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;./RequestAccess&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;Home&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;./components/Home&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;accessGranted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setAccessGranted&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setMessage&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;COOKIE FROM LOGIN&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// Replace with your actual cookie value &lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;element_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ELEMENTS_CONFIG_ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;project_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{project_id}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// Replace with your project ID &lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;env_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{env_id}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// Replace with your environment ID &lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;access_request_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{access_request_id}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// Replace with your access request ID&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;checkAccess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://api.permit.io/v2/facts/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;envId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/access_requests/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accessRequestId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;element_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;elementId&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access request data:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;approved&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;setAccessGranted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;setAccessGranted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error fetching access request:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setAccessGranted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="nf"&gt;checkAccess&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="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;BrowserRouter&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;Home&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;Routes&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;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;element&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;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="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt; 
&lt;/span&gt;                  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;accessGranted&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/private-page&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;element&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;lt;&lt;/span&gt;&lt;span class="nx"&gt;PrivatPage&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt; &lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt; : &lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt; &amp;lt;Route path="/&lt;/span&gt;&lt;span class="nx"&gt;embed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; element={&amp;lt;RequestAccess /&amp;gt;} /&amp;gt; )} 
              &amp;lt;/Routes&amp;gt;
           &amp;lt;/BrowserRouter&amp;gt; 
         &amp;lt;/div&amp;gt; 
      ); 
    }
    export default App;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we have set up a state to track when the user has been granted access to the restricted page. Next we use the &lt;a href="https://docs.permit.io/api/examples/access-requests/#1-get-an-access-request" rel="noopener noreferrer"&gt;API&lt;/a&gt; provided by Permit to check the status of a request if it is approved or not. This API takes four parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cookie: This is the cookie you have extracted from logging in a user into Permit&lt;/li&gt;
&lt;li&gt;element_id = The element_id is the text after the domain on the iframe’s &lt;code&gt;src&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;project_id: This is the id of your project. Follow &lt;a href="https://docs.permit.io/api/examples/get-project-and-env#get-project-id-or-key" rel="noopener noreferrer"&gt;this link&lt;/a&gt; to get your project id&lt;/li&gt;
&lt;li&gt;env_id = Env id. Follow &lt;a href="https://docs.permit.io/api/examples/get-project-and-env#get-environment-object" rel="noopener noreferrer"&gt;this link&lt;/a&gt; to get your env id&lt;/li&gt;
&lt;li&gt;access_request_id: The id of the access request you want to check. Follow &lt;a href="https://docs.permit.io/api/examples/access-requests#1-get-an-access-request" rel="noopener noreferrer"&gt;this link&lt;/a&gt; to get an access_request id.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once this request is successful, we check to see if the &lt;strong&gt;status&lt;/strong&gt; from the data is “approved” or not and set the &lt;code&gt;accessGranted&lt;/code&gt; state based on the status. Then we check in the UI code to know if a user has been granted access and show them either the Private Page if they have been granted access or the Request Access element if they haven’t.&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%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1%2AJYYmwtphBzLw21jXW71L1w.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%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1%2AJYYmwtphBzLw21jXW71L1w.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An image of the element_id&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Running the Application&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Spin up the local server for the frontend application using &lt;code&gt;npm start&lt;/code&gt; and then spin up the server by navigating to the server folder in your terminal and running the command &lt;code&gt;node server.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1%2ACz2OIKNdSw3DpEX1HegduA.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%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1%2ACz2OIKNdSw3DpEX1HegduA.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the user clicks the &lt;code&gt;View Private Screen&lt;/code&gt; button, the Request Access elements pop up. Once they request access, it is sent to an admin for approval.&lt;br&gt;
After sending the access request with a viewer account, I logged in with an admin account. I navigated to the User Management page just to show how easy it is to accept or reject the viewer account request to access the private screen that I’ve created.&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%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1%2ABCW4LEHHuMIPLj0_9WyU5Q.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%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1%2ABCW4LEHHuMIPLj0_9WyU5Q.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Admin (user management element)&lt;br&gt;
Now, as an admin, I can approve or deny access to my restricted page. Once the admin approves, then the user who requested access gets access the the restricted/private page.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;In this article, we learned how to create private/restricted screens in React leveraging &lt;a href="http://permit.io/" rel="noopener noreferrer"&gt;Permit.io&lt;/a&gt;’s Access Request element which saves the time and effort that would have been spent on trying to implement this feature from scratch.&lt;br&gt;
Resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learn more on &lt;a href="https://docs.permit.io/embeddable-uis/element/access-request" rel="noopener noreferrer"&gt;Access Request&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Learn more about &lt;a href="https://docs.permit.io/embeddable-uis/element/user-management" rel="noopener noreferrer"&gt;User Management&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Learn more about &lt;a href="https://www.permit.io/" rel="noopener noreferrer"&gt;Permit&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;CTA:&lt;/strong&gt;&lt;br&gt;
Ready to revolutionize your application’s access control? Check out &lt;a href="https://www.producthunt.com/posts/permit-share-if" rel="noopener noreferrer"&gt;Permit Share-If on Product Hunt&lt;/a&gt; and learn how it can transform your development process.&lt;/p&gt;

</description>
      <category>react</category>
      <category>authorization</category>
      <category>permissions</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Build a simple contact manager with PostgREST, Railway, and Postgres</title>
      <dc:creator>Dalu46</dc:creator>
      <pubDate>Wed, 28 Feb 2024 10:25:55 +0000</pubDate>
      <link>https://forem.com/hackmamba/build-a-simple-contact-manager-with-postgrest-railway-and-postgres-23n2</link>
      <guid>https://forem.com/hackmamba/build-a-simple-contact-manager-with-postgrest-railway-and-postgres-23n2</guid>
      <description>&lt;p&gt;PostgreSQL is powerful in the world of databases and is known for its robustness, extensibility, and versatility. As a relational database management system, it is the go-to choice for developers seeking data integrity, SQL support, and scalability. However, harnessing the full potential of PostgreSQL sometimes involves creating custom APIs, a task that can quickly become difficult.&lt;/p&gt;

&lt;p&gt;But what if there were a way to seamlessly traverse this hassle, eliminating the need for manual API creation while harnessing the power of your PostgreSQL database? Meet &lt;a href="https://postgrest.org/en/stable/?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;PostgREST&lt;/a&gt; – the game-changer that liberates developers from the intricacies of building custom APIs. PostgREST steps in as your ally, simplifying the integration of your PostgreSQL database with the world of RESTful APIs.&lt;/p&gt;

&lt;p&gt;This comprehensive guide will explore the seamless integration of PostgREST, Neon Postgres, and Railway to create a robust application. You will learn how PostgREST transforms your Postgres database into a RESTful API, how Neon Postgres provides serverless database management, and how Railway simplifies deployment. To illustrate this, we will build a simple contact web application that renders, adds, and deletes contact data from your Neon database.&lt;/p&gt;

&lt;p&gt;This guide will take you through step-by-step instructions to set up Neon, configure PostgREST, and deploy a demo Next.js application on Railway. By the end, you'll have a fully functional application with live interaction between Neon Postgres, PostgREST, and Railway, showcasing the power of these tools in harmony.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What&lt;/strong&gt; &lt;strong&gt;is PostgREST?&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://postgrest.org/en/stable/?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;PostgREST&lt;/a&gt; is a tool that generates a RESTful API from your Postgres database. PostgREST automatically translates database schema, tables, and operations into a RESTful API, reducing the need for custom API development. To modify your Postgres database, you can send HTTP requests to the provided API endpoints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Neon Postgres?&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://neon.tech/docs/introduction?ref=hm" rel="noopener noreferrer"&gt;Neon&lt;/a&gt; is a serverless Postgres database that offers features such as branching, bottomless storage, and more. Neon also provides APIs and an intuitive dashboard console for managing your Postgres database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Railway?&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://railway.app/?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;Railway&lt;/a&gt; is a deployment platform for running and managing your application infrastructure. Railway provides features such as an observability graph, out-of-the-box templates, integrations to popular tools, instant deployment, interactive CLI, cloud hosting, and more.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow along fully, you‘ll need some knowledge on the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.postgresql.org/docs/current/sql.html?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;PostgreSQL&lt;/a&gt; commands&lt;/li&gt;
&lt;li&gt;JavaScript and React fundamentals&lt;/li&gt;
&lt;li&gt;Basic terminal commands&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Set up Neon
&lt;/h2&gt;

&lt;p&gt;To get started, we’ll need to &lt;a href="https://console.neon.tech/sign_in" rel="noopener noreferrer"&gt;create an account&lt;/a&gt; with Neon. After creating an account, choose a project and a database name, then click the &lt;strong&gt;Create Project&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1701396497257_Screen%2BShot%2B2023-12-01%2Bat%2B3.05.31%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1701396497257_Screen%2BShot%2B2023-12-01%2Bat%2B3.05.31%2BAM.png" alt="Create Neon project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, you’ll see a modal containing your database connection string (in our case &lt;em&gt;”postgresql://lawrencefranklin100:&lt;a href="mailto:7hzVnZSiG9yC@ep-old-firefly-95393312.us-west-2.aws.neon.tech"&gt;7hzVnZSiG9yC@ep-old-firefly-95393312.us-west-2.aws.neon.tech&lt;/a&gt;/neondb?sslmode=require”&lt;/em&gt; ), which you will connect to PostgREST later. This string is always available on the dashboard console, so click the &lt;strong&gt;I’ll do this later&lt;/strong&gt; button. Now, you should see your Neon dashboard, which should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1701396922186_Screen%2BShot%2B2023-12-01%2Bat%2B3.15.05%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1701396922186_Screen%2BShot%2B2023-12-01%2Bat%2B3.15.05%2BAM.png" alt="Neon dashboard console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow the steps below to create a table and add sample data to the database:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on the SQL Editor tab&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clear all text and insert the following query:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;contacts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;contacts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'John Doe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'+23344874'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Alice Bob'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'+33349847'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="nv"&gt;`&lt;/span&gt;&lt;span class="se"&gt;``&lt;/span&gt;&lt;span class="nv"&gt;

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


&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;These SQL commands create a table named &lt;strong&gt;contacts&lt;/strong&gt; in the Neon database with three columns— &lt;strong&gt;id&lt;/strong&gt;, &lt;strong&gt;name&lt;/strong&gt;, and &lt;strong&gt;phone&lt;/strong&gt;—and then insert two records into that table. &lt;/p&gt;

&lt;p&gt;After running these SQL commands, you'll have a table named &lt;strong&gt;contacts&lt;/strong&gt; populated with two records for John Doe and Alice Bob. The &lt;strong&gt;id&lt;/strong&gt; column will be automatically populated with unique serial values due to its &lt;strong&gt;SERIAL&lt;/strong&gt; type.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on the &lt;strong&gt;Run&lt;/strong&gt; button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, navigate to the Tables tab to view the sample added data:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1701397758252_Screen%2BShot%2B2023-12-01%2Bat%2B3.28.03%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1701397758252_Screen%2BShot%2B2023-12-01%2Bat%2B3.28.03%2BAM.png" alt="Neon Postgres database"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up PostgREST
&lt;/h2&gt;

&lt;p&gt;To use PostgREST, you have to install it. &lt;a href="https://brew.sh/?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;Homebrew&lt;/a&gt; is a simple way to install PostgREST. Type the following command into your terminal to install PostgREST:&lt;/p&gt;

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

&lt;/div&gt;
&lt;p&gt;Once the installation is completed successfully, you must connect your Neon Postgres database. To do this, create a &lt;code&gt;postgrest.conf&lt;/code&gt; file in any folder of your choice (e.g., documents folder) and add the following content:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    &lt;span class="c"&gt;# postgrest.conf&lt;/span&gt;

    &lt;span class="c"&gt;# The standard connection URI format, documented at&lt;/span&gt;
    &lt;span class="c"&gt;# https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING&lt;/span&gt;
    db-uri &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;

    &lt;span class="c"&gt;# The database role to use when no client authentication is provided.&lt;/span&gt;
    &lt;span class="c"&gt;# Should differ from authenticator&lt;/span&gt;
    db-anon-role &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;

    &lt;span class="c"&gt;## The name of which database schema to expose to REST clients&lt;/span&gt;
    db-schemas &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"public"&lt;/span&gt;


    &lt;span class="c"&gt;# The secret to verify the JWT for authenticated requests with.&lt;/span&gt;
    &lt;span class="c"&gt;# Needs to be 32 characters minimum.&lt;/span&gt;
    jwt-secret &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"reallyreallyreallyreallyverysafe"&lt;/span&gt;
    jwt-secret-is-base64 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;

    &lt;span class="c"&gt;# Port the postgrest process is listening on for http requests&lt;/span&gt;
    server-port &lt;span class="o"&gt;=&lt;/span&gt; 5432
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;NOTE: the &lt;code&gt;postgrest.conf&lt;/code&gt; file can be created anywhere. However, it is important to remember the path to its location as this file will be used to spin up a local server to listen to HTTP requests from the demo application. Also, you can use any port (server-port) you want.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Go to your Neon console and copy your connection string. Note the structure of the string:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1701398871186_Screen%2BShot%2B2023-12-01%2Bat%2B3.47.00%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1701398871186_Screen%2BShot%2B2023-12-01%2Bat%2B3.47.00%2BAM.png" alt="Neon connection string"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the string (wrapped in double quotes) as the &lt;code&gt;db-uri&lt;/code&gt;  value in your &lt;code&gt;Postgrest.conf&lt;/code&gt; file. Also, add the role (as seen in the string structure above) as the &lt;code&gt;db-anon-role&lt;/code&gt; value.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;db-uri &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"postgres://alex:AbC123dEf@ep-cool-darkness-123456.us-east-2.aws.neon.tech/dbname"&lt;/span&gt;

    db-anon-role &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"alex"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The default port, i.e., &lt;code&gt;server-port&lt;/code&gt; is 3000, but since the demo application will be run on port 3000, use port 5432.&lt;/p&gt;

&lt;p&gt;Save the &lt;code&gt;postgrest.conf&lt;/code&gt; file and run the following command in your terminal to start listening for HTTP requests (make sure to use the correct path where the &lt;code&gt;postgrest.conf&lt;/code&gt; file was created e.g., &lt;code&gt;postgrest user/documents/code/postgrest.conf&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;postgrest path/to/postgrest.conf 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;After running the PostgREST command, you should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1701409875249_Screen%2BShot%2B2023-12-01%2Bat%2B6.50.40%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1701409875249_Screen%2BShot%2B2023-12-01%2Bat%2B6.50.40%2BAM.png" alt="PostgREST server host"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This should spin up a local server on the port that allows you to send HTTP requests to &lt;code&gt;http://localhost:5432/contacts&lt;/code&gt;. This endpoint represents your Neon Postgres database table (contacts). A &lt;strong&gt;GET&lt;/strong&gt; request to this endpoint returns the sample data from your Neon Postgres database. To test this, send a &lt;strong&gt;GET&lt;/strong&gt; request using Postman, Curl, or whatever HTTP request client you prefer.&lt;/p&gt;
&lt;h2&gt;
  
  
  Set up a demo project
&lt;/h2&gt;

&lt;p&gt;The demo project is a basic Next.js application that renders a list of contacts that can be added and deleted from your Neon Postgres database. The application uses the endpoint provided by PostgREST to modify data in the Neon Postgres database.&lt;/p&gt;

&lt;p&gt;To set up the demo project, &lt;a href="https://github.com/Dalu46/neon-db?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;clone this repo&lt;/a&gt;. Once you’ve cloned this repo, create a &lt;code&gt;.env.local&lt;/code&gt; file in your project root and add the following environment variable:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_URL=https://localhost:5432/contacts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This variable stores the endpoint created by PostgREST. Save the &lt;code&gt;.env.local&lt;/code&gt; file and run the following command to preview the application:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This spins up a localhost where you can preview the demo application, usually on port 3000, like this:  &lt;code&gt;http://localhost:3000&lt;/code&gt;. Open this URL in your browser, and you should see the demo project with two contacts from your Neon Postgres database:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1701411217202_Screen%2BShot%2B2023-12-01%2Bat%2B7.13.30%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1701411217202_Screen%2BShot%2B2023-12-01%2Bat%2B7.13.30%2BAM.png" alt="Local demo application"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can add or delete contacts at this stage, which should sync with the data in your Neon Postgres database. Go ahead, test it out!&lt;/p&gt;

&lt;p&gt;Create a remote repo on your GitHub and push this project to the remote repo on GitHub. The remote repo will be used to deploy the application on Railway.&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploy demo project to Railway
&lt;/h2&gt;

&lt;p&gt;To deploy to Railway, you need a Railway account. &lt;a href="https://railway.app/login?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;Create a Railway account&lt;/a&gt; (most preferably using your GitHub). After creating an account, you should see your railway dashboard. Type &lt;code&gt;cmd + K&lt;/code&gt; or click the &lt;strong&gt;New Project&lt;/strong&gt; button to create a new project. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1701414408588_Screen%2BShot%2B2023-12-01%2Bat%2B8.05.41%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1701414408588_Screen%2BShot%2B2023-12-01%2Bat%2B8.05.41%2BAM.png" alt="New Railway project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow the steps below to deploy your application: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on &lt;strong&gt;New Project&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;Deploy from GitHub repo&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;Configure GitHub app&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Follow the prompts and grant access to your GitHub repo&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once access is granted, your repo will be listed in Railway’s &lt;strong&gt;New Project&lt;/strong&gt; options. Select your repo and deploy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1701415386496_Screen%2BShot%2B2023-12-01%2Bat%2B8.06.02%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1701415386496_Screen%2BShot%2B2023-12-01%2Bat%2B8.06.02%2BAM.png" alt="Connect to GitHub repo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After deploying, follow the steps below to run the demo application:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on the project.&lt;/li&gt;
&lt;li&gt;Click on the &lt;strong&gt;Settings&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scroll to the &lt;strong&gt;Public Networking&lt;/strong&gt; section and click on the &lt;strong&gt;Generate Domain&lt;/strong&gt; button. This will generate a production URL where you can preview the application. &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%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1702062141069_labelled%2Brailway%2Bguide.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1702062141069_labelled%2Brailway%2Bguide.png"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To access the PostgREST endpoint (running on your localhost server), you must create an environment variable on Railway. Click on the &lt;strong&gt;Variables&lt;/strong&gt; tab, click on the &lt;strong&gt;New Variable&lt;/strong&gt; button, and then add the environment variable just like you have it in your &lt;code&gt;.env.local&lt;/code&gt; file. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1702062307308_Screen%2BShot%2B2023-12-08%2Bat%2B8.04.34%2BPM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1702062307308_Screen%2BShot%2B2023-12-08%2Bat%2B8.04.34%2BPM.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, your production URL should be able to render, add, and remove contacts from your Neon Postgres database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1701416969343_Screen%2BShot%2B2023-12-01%2Bat%2B12.34.33%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_9A181B5474D5142700D8E2D7CBC2F4C29FF949AD505684EAD5E6A90735F1FA2D_1701416969343_Screen%2BShot%2B2023-12-01%2Bat%2B12.34.33%2BAM.png" alt="Production demo application"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s a link to the live video of how the application works:&lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://app.opentape.io/share/b94c8351-b0ac-4c14-bc83-b8d9125e4ac2" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapp.opentape.io%2Fthumbnail%2Fb94c8351-b0ac-4c14-bc83-b8d9125e4ac2" height="auto" class="m-0"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://app.opentape.io/share/b94c8351-b0ac-4c14-bc83-b8d9125e4ac2" rel="noopener noreferrer" class="c-link"&gt;
          NEON Contact list | Opentape
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Lawrence Franklin Chukwudalu - Feb 28th, 8:43am
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapp.opentape.io%2Ffavicon.ico"&gt;
        app.opentape.io
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



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

&lt;p&gt;At this stage, you have learned what PostgREST is and how it can connect to a Postgres database such as Neon Postgres to create RESTful API endpoints. You also learned how to build a Next.js application using the provided endpoints and deploy your application to Railway.&lt;/p&gt;

&lt;p&gt;Neon is fast and can facilitate easy scaling of your application. In addition to its UI features, such as SQL Editor, with which you can modify your data, it also works nicely with PostgREST and Railway to alter and deploy your application.&lt;/p&gt;

&lt;p&gt;Here are a few resources to learn more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://postgrest.org/en/stable/?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;PostgREST Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.railway.app/?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;Railway Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://neon.tech/docs/introduction?ref=hm" rel="noopener noreferrer"&gt;Neon Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Develop a travel expense management app with Nextjs</title>
      <dc:creator>Dalu46</dc:creator>
      <pubDate>Tue, 11 Jul 2023 08:23:30 +0000</pubDate>
      <link>https://forem.com/hackmamba/develop-a-travel-expense-management-app-with-nextjs-11mc</link>
      <guid>https://forem.com/hackmamba/develop-a-travel-expense-management-app-with-nextjs-11mc</guid>
      <description>&lt;p&gt;Creating a robust application has become essential as the need for efficient expense tracking and management becomes essential in the travel industry. Next.js, a popular React framework, provides a solid foundation for building dynamic web applications, while Appwrite simplifies backend development by offering ready-to-use features like user authentication, database management, and cloud functions. &lt;/p&gt;

&lt;p&gt;By leveraging the power of Next.js and Appwrite, we will create a scalable and feature-rich travel expense management app that streamlines the process of tracking, managing, and analyzing travel expenses. &lt;/p&gt;

&lt;p&gt;In this article, we will go through the step-by-step process of building this application. We will also explore &lt;a href="https://appwrite.io/docs/databases-relationships?utm_source=hackmamba&amp;amp;utm_medium=hackmamba-blog"&gt;Relationships in Databases&lt;/a&gt;, a new feature in beta (at the time of this writing) that allows us to read, update, or delete related documents together.&lt;/p&gt;

&lt;p&gt;The complete source code for the application we will build is on &lt;a href="https://github.com/Dalu46/travel-expense-app"&gt;GitHub&lt;/a&gt;; clone and fork it to get started.&lt;/p&gt;

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

&lt;p&gt;To follow along with this tutorial, the following are required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Appwrite instance running on Docker. See this &lt;a href="https://dev.to/hackmamba/create-a-local-appwrite-instance-in-3-steps-19n9?utm_source=hackmamba&amp;amp;utm_medium=hackmamba"&gt;article&lt;/a&gt; to learn how to set up an Appwrite account running on Docker. We can also install Appwrite with one click on &lt;a href="https://marketplace.digitalocean.com/apps/appwrite"&gt;DigitalOcean&lt;/a&gt; or &lt;a href="https://gitpod.io/#https://github.com/appwrite/integration-for-gitpod"&gt;Gitpod&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A basic understanding of JavaScript, React, and Next.js.&lt;/li&gt;
&lt;li&gt;Node installed with &lt;a href="https://npmjs.com/"&gt;node package manager&lt;/a&gt; &lt;a href="https://npmjs.com/"&gt;(&lt;/a&gt;&lt;a href="https://npmjs.com/"&gt;NPM&lt;/a&gt;&lt;a href="https://npmjs.com/"&gt;)&lt;/a&gt;. We can install them &lt;a href="https://nodejs.org/en/"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting started with Appwrite
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://appwrite.io/?utm_source=hackmamba&amp;amp;utm_medium=hackmamba-blog"&gt;Appwrite&lt;/a&gt; is a cross-platform and framework-agnostic backend-as-a-service platform for developers to build full-stack applications. It comes with many APIs and tools, as well as a management console with a fully built-out UI that allows us to build applications much faster and more securely. &lt;/p&gt;

&lt;h2&gt;
  
  
  Project setup
&lt;/h2&gt;

&lt;p&gt;For this project, we will use Next.js for the front end. To bootstrap a Next.js app, navigate to the desired directory and run the command below in the terminal.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-next-app@latest travel-expense-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This command will prompt a few questions about configuring our Next.js application. Here’s our response to each question:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PI2_6sbq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1686006255571_Screen%2BShot%2B2023-06-06%2Bat%2B12.03.32%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PI2_6sbq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1686006255571_Screen%2BShot%2B2023-06-06%2Bat%2B12.03.32%2BAM.png" alt="Nextjs setup" width="800" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;No&lt;/strong&gt; for each question and press &lt;code&gt;enter&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing dependencies
&lt;/h2&gt;

&lt;p&gt;To install Appwrite, run the command below:&lt;/p&gt;

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

&lt;/div&gt;

&lt;p&gt;For this project, we will also use &lt;a href="https://pink.appwrite.io/"&gt;Pink&lt;/a&gt; &lt;a href="https://pink.appwrite.io/"&gt;D&lt;/a&gt;&lt;a href="https://pink.appwrite.io/?utm_source=hackmamba&amp;amp;utm_medium=hackmamba-blog"&gt;esign&lt;/a&gt;, Appwrite's open-source, comprehensive solution for creating consistent and reusable user interfaces, to build an interactive UI for our application. &lt;/p&gt;

&lt;p&gt;To install Pink Design, run the following command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @appwrite.io/pink
&lt;/code&gt;&lt;/pre&gt;

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

&lt;p&gt;To set up the backend part of our project, we need to sign in to Appwrite. Head to &lt;code&gt;localhost:80&lt;/code&gt; on your machine, sign in, and create a new project &lt;code&gt;travel-app&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;PS: This article assumes we’ve already created an Appwrite instance running on Docker.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0YQIyx9Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1685980013606_Screen%2BShot%2B2023-06-05%2Bat%2B10.47.01%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0YQIyx9Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1685980013606_Screen%2BShot%2B2023-06-05%2Bat%2B10.47.01%2BAM.png" alt="Create a project" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can now create our project on any type of framework. Since we’re using Next.js, click on the &lt;code&gt;Web App&lt;/code&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4RAJ1Uno--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1684892399154_Screen%2BShot%2B2023-05-24%2Bat%2B2.38.37%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4RAJ1Uno--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1684892399154_Screen%2BShot%2B2023-05-24%2Bat%2B2.38.37%2BAM.png" alt="Select a platform" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will take us to the &lt;strong&gt;Add a Web Project&lt;/strong&gt; page. Input travel-app as the app's name, put the &lt;code&gt;*&lt;/code&gt; symbol in the localhost input, and then click the &lt;strong&gt;Next&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hfcUvAq5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1684892484222_Screen%2BShot%2B2023-05-23%2Bat%2B11.44.19%2BPM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hfcUvAq5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1684892484222_Screen%2BShot%2B2023-05-23%2Bat%2B11.44.19%2BPM.png" alt="Register the project" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next step is to create a database. Appwrite’s databases service empowers us to establish organized collections of documents, effortlessly query and refine lists of documents, and effectively administer an array of read and write access permissions.&lt;/p&gt;

&lt;p&gt;To create a database, navigate to the &lt;strong&gt;Database&lt;/strong&gt; tab, click on the &lt;strong&gt;Create database&lt;/strong&gt; button, input the name of our database, which is a &lt;code&gt;travel app&lt;/code&gt;, and then click on the &lt;strong&gt;Create&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RbpZBTG8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1685958212847_Screen%2BShot%2B2023-06-05%2Bat%2B10.43.13%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RbpZBTG8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1685958212847_Screen%2BShot%2B2023-06-05%2Bat%2B10.43.13%2BAM.png" alt="Create a database" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After creating the database, create a collection for storing the project details. We will create two separate collections: &lt;code&gt;cost&lt;/code&gt;, and &lt;code&gt;location&lt;/code&gt;. To create a collection, click the &lt;strong&gt;Create collection&lt;/strong&gt; button, input the collection's name (one for cost and one for location), then click the &lt;strong&gt;Create&lt;/strong&gt; button. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JQZcXFIO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1685957654129_Screen%2BShot%2B2023-06-05%2Bat%2B10.29.45%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JQZcXFIO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1685957654129_Screen%2BShot%2B2023-06-05%2Bat%2B10.29.45%2BAM.png" alt="Create a collection" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we need to set up some attributes. We use attributes to define the structure of our documents and help the Appwrite API to validate users' input. We need only two inputs for this application: the location's name and the cost. &lt;/p&gt;

&lt;p&gt;To create an attribute, click the &lt;strong&gt;Create attribute&lt;/strong&gt; button; we will be prompted with two inputs requiring a key and a type. The &lt;strong&gt;key&lt;/strong&gt; is used as an identifier for the attribute, whereas the &lt;strong&gt;type&lt;/strong&gt; is the type of value we expect from the user — that is, string, Boolean, integer, etc. &lt;/p&gt;

&lt;p&gt;We will create a &lt;code&gt;name&lt;/code&gt; attribute whose type will be a string since we expect the user to pass a string as the location's name. We will create this attribute under the &lt;code&gt;location&lt;/code&gt; collection.&lt;br&gt;
Then we will create an &lt;code&gt;amount&lt;/code&gt; attribute under the cost collection. Its type will be an integer. We will set them as required fields so that every location has them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---lW_YaCA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1685957767097_Screen%2BShot%2B2023-06-05%2Bat%2B10.32.33%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---lW_YaCA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1685957767097_Screen%2BShot%2B2023-06-05%2Bat%2B10.32.33%2BAM.png" alt="Create an attribute" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We need to update our collections’ permission. To do this, navigate to the &lt;strong&gt;Settings&lt;/strong&gt; tab in the &lt;code&gt;collection&lt;/code&gt;, scroll down to the &lt;strong&gt;Update Permissions&lt;/strong&gt; section, select a new role for &lt;strong&gt;Any&lt;/strong&gt; user, and click the &lt;strong&gt;Create, Update, Delete&lt;/strong&gt; and &lt;strong&gt;Read&lt;/strong&gt; checkboxes. This will enable anyone (user) to write, update, delete, and read from the database. We will update the permission for both the &lt;code&gt;cost&lt;/code&gt; and &lt;code&gt;location&lt;/code&gt; collections.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IO6SqBL3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1685958006116_Screen%2BShot%2B2023-06-05%2Bat%2B10.39.37%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IO6SqBL3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1685958006116_Screen%2BShot%2B2023-06-05%2Bat%2B10.39.37%2BAM.png" alt="Add permissions" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, create a &lt;strong&gt;Relationship&lt;/strong&gt; attribute for the &lt;strong&gt;location&lt;/strong&gt; collection (select &lt;strong&gt;Relationship&lt;/strong&gt; as the attribute type). Select the &lt;strong&gt;Two-way relationship type&lt;/strong&gt; in the Relationship modal and set &lt;strong&gt;cost&lt;/strong&gt; as the &lt;strong&gt;related collection&lt;/strong&gt; and &lt;strong&gt;cost&lt;/strong&gt; as an &lt;strong&gt;attribute key&lt;/strong&gt;. Select &lt;strong&gt;one-to-one&lt;/strong&gt; as the relation (because we want each location to have a cost and each cost a location). Next, select &lt;strong&gt;Cascade&lt;/strong&gt; as the desired on-delete behavior. Click the &lt;strong&gt;Create&lt;/strong&gt; button to create the Relationship. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CdPX9rYD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1685957909750_Screen%2BShot%2B2023-06-05%2Bat%2B10.38.10%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CdPX9rYD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1685957909750_Screen%2BShot%2B2023-06-05%2Bat%2B10.38.10%2BAM.png" alt="Create a relationship attribute" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go to the &lt;strong&gt;Documents&lt;/strong&gt; section in the &lt;strong&gt;locations&lt;/strong&gt; collection and click &lt;strong&gt;Create Document.&lt;/strong&gt; On the &lt;strong&gt;Create Document&lt;/strong&gt; page, input the location's name — here, Lagos, Nigeria — and click the &lt;strong&gt;Next&lt;/strong&gt; button to create a location manually from the console. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--18Uq_Ytj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1684894474942_Screen%2BShot%2B2023-05-24%2Bat%2B3.09.32%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--18Uq_Ytj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1684894474942_Screen%2BShot%2B2023-05-24%2Bat%2B3.09.32%2BAM.png" alt="Create a location" width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;strong&gt;Documents&lt;/strong&gt; section on the &lt;strong&gt;cost&lt;/strong&gt; collection and repeat the process above to create a cost for the location (Lagos, Nigeria) we just created. While creating this, we can see the &lt;strong&gt;Relationship&lt;/strong&gt; text, which signifies that the &lt;strong&gt;cost&lt;/strong&gt; collection is in “a relationship“ with the &lt;strong&gt;locations&lt;/strong&gt; collection. &lt;/p&gt;

&lt;p&gt;Next, select the &lt;strong&gt;id&lt;/strong&gt; for the location we created (Lagos, Nigeria). Now whenever we make a request to any of the collections, we will get access to the others.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YLF_YK9T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1684895023760_Screen%2BShot%2B2023-05-24%2Bat%2B3.10.22%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YLF_YK9T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1684895023760_Screen%2BShot%2B2023-05-24%2Bat%2B3.10.22%2BAM.png" alt="Create a corresponding cost" width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can now set up our Next.js application to use Appwrite.&lt;/p&gt;
&lt;h2&gt;
  
  
  Fetch and render data from Appwrite
&lt;/h2&gt;

&lt;p&gt;Here, we will fetch the &lt;strong&gt;location&lt;/strong&gt; collection we created from Appwrite using Next.js's &lt;a href="https://nextjs.org/docs/pages/building-your-application/data-fetching/get-server-side-props"&gt;getServerSideProps(&lt;/a&gt;&lt;a href="https://nextjs.org/docs/pages/building-your-application/data-fetching/get-server-side-props"&gt;&lt;strong&gt;)&lt;/strong&gt;&lt;/a&gt; function provided by Next.js. In the &lt;code&gt;index.js&lt;/code&gt; file, just below the &lt;code&gt;Home&lt;/code&gt; function, paste the following code:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Databases&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;appwrite&lt;/span&gt;&lt;span class="dl"&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getServerSideProps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&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;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OUR API ENDPOINT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;setProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OUR PROJECT ID&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;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Databases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&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;locations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listDocuments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OUR DATABASE ID&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;OUR LOCATIONS COLLECTION ID&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="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;locations&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 above code snippet does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Imports the essential modules, &lt;strong&gt;Client&lt;/strong&gt;, &lt;strong&gt;Databases&lt;/strong&gt;&lt;strong&gt;,&lt;/strong&gt; &lt;strong&gt;and ID&lt;/strong&gt;&lt;strong&gt;.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Creates the client connection by setting the endpoints. Then it establishes the database, pulls the location from it, and returns it as props.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;It is important to note that we can get our &lt;code&gt;API Endpoint&lt;/code&gt;&lt;strong&gt;,&lt;/strong&gt; &lt;code&gt;Project ID&lt;/code&gt;&lt;strong&gt;,&lt;/strong&gt;  &lt;code&gt;[DATABASE_ID]&lt;/code&gt;, and &lt;code&gt;[COLLECTION_ID]&lt;/code&gt; from the Appwrite console.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s one way that shows the impressiveness of the &lt;strong&gt;relationships in database&lt;/strong&gt; feature. In the snippet above, we made a request to just the &lt;strong&gt;locations&lt;/strong&gt; collection, but we still get access to the cost collection.&lt;/p&gt;

&lt;p&gt;This feature is very useful if we have many collections, as we will need to make only one request and then get access to the other collections. This saves time and reduces redundancy in code, as we wouldn’t have to make individual calls for different collections.&lt;/p&gt;

&lt;p&gt;Next, we pass the props into the home components as &lt;code&gt;locations&lt;/code&gt; so that we can render the list of locations in the &lt;code&gt;Home&lt;/code&gt; components.&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;useEffect&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;locations&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;locations&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Building the UI
&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;index.js&lt;/code&gt; file inside the &lt;code&gt;pages&lt;/code&gt; component on VS Code, paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@appwrite.io/pink&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@appwrite.io/pink-icons&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;boxShadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hsl(var(--shadow-large))&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h3&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;heading-level-3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Travel&lt;/span&gt; &lt;span class="nx"&gt;Expense&lt;/span&gt; &lt;span class="nx"&gt;Management&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h3&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;addLocation&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;u-flex u-column-gap-24&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
      &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleLocationName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;u-max-width-250 u-min-height-100-percent u-max-width-250&lt;/span&gt;&lt;span class="dl"&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="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="nx"&gt;required&lt;/span&gt;
    &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
      &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handgleCostChange&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;u-remove-input-number-buttons u-min-height-100-percent u-max-width-250&lt;/span&gt;&lt;span class="dl"&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="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="nx"&gt;required&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;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Submit&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;/form&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&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code snippet does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Imports Pink Design and Pink Design icons.&lt;/li&gt;
&lt;li&gt;Creates two input fields (one for cost, and one for location) and a submit button. &lt;/li&gt;
&lt;li&gt;An &lt;code&gt;onChange&lt;/code&gt; handler is set on the &lt;code&gt;cost&lt;/code&gt; input with a reference to the &lt;code&gt;handleChangeCost&lt;/code&gt; function, and on the &lt;code&gt;locations&lt;/code&gt; input with a reference to the &lt;code&gt;handleLocationName&lt;/code&gt; function, to update the &lt;code&gt;cost&lt;/code&gt; state and &lt;code&gt;locations&lt;/code&gt; state, as the user types in each input field.&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;onSubmit&lt;/code&gt; handler is set on the form referencing the &lt;code&gt;addLocation&lt;/code&gt; function we created earlier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pink&lt;/strong&gt; &lt;strong&gt;Design&lt;/strong&gt; provides us with components, utility classes, and more. The Pink Design utility classes used above does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;heading-level-3&lt;/strong&gt;: Makes a text an &lt;code&gt;h3&lt;/code&gt; element&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;u-flex:&lt;/strong&gt; Sets display to flex&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;u-min-height-100-percent and u-max-width-250&lt;/strong&gt;: Sets the minimum height to 100 percent and the maximum width to 15.625rem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;button&lt;/strong&gt;: Gives the basic styling for a button element&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To render the list of locations, create a &lt;code&gt;components&lt;/code&gt; folder above the &lt;code&gt;node modules&lt;/code&gt; folder. Then create a &lt;code&gt;LocationList.js&lt;/code&gt; file and paste the following code into the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LocationList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;locations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deleteLocation&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="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;h2&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;heading-level-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Locations&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;locations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;location&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;card&lt;/span&gt;&lt;span class="dl"&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="cm"&gt;/* style={{ display: 'flex', justifyContent: 'space-between' } */&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;ul&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;list-item u-main-space-between u-flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;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;span&lt;/span&gt;
                 &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;icon-arrow-circle-right u-margin-32 u-cross-center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                    &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;hidden&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text heading-level-4 u-cross-center&lt;/span&gt;&lt;span class="dl"&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;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&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;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tooltip&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;variables info&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;
                  &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;deleteLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
                  &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;icon-trash button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                  &lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tooltip-popup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tooltip&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                  &lt;span class="nx"&gt;Delete&lt;/span&gt; &lt;span class="nx"&gt;travel&lt;/span&gt; &lt;span class="nx"&gt;details&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&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;/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;/li&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;/ul&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="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;LocationList&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code snippet does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accepts &lt;strong&gt;locations&lt;/strong&gt;, and &lt;strong&gt;deleteLocation&lt;/strong&gt; as props. &lt;strong&gt;Locations&lt;/strong&gt; show the list of all the locations in our database, while &lt;strong&gt;deleteLocation&lt;/strong&gt; is a function that will enable a user to delete a location from our application.&lt;/li&gt;
&lt;li&gt;Maps through the list of locations, rendering its name and cost for every location. &lt;/li&gt;
&lt;li&gt;Uses &lt;strong&gt;Pink design&lt;/strong&gt; for styling, and &lt;strong&gt;Pink&lt;/strong&gt; &lt;strong&gt;D&lt;/strong&gt;&lt;strong&gt;esign icons&lt;/strong&gt; to create a delete icon.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can see what our app looks like by running the following command:&lt;/p&gt;

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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wcTY_WpE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1684895771464_Screen%2BShot%2B2023-05-24%2Bat%2B3.34.07%2BAM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wcTY_WpE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropboxusercontent.com/s_EBE692309C35B6A444270A84C8144A174FE72AE694A780E1B2666C891B94811F_1684895771464_Screen%2BShot%2B2023-05-24%2Bat%2B3.34.07%2BAM.png" alt="App preview" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s add the logic to enable users to add both a new location and cost to the application. We’ll also look at the logic allowing users to delete a location. Finally, we’ll add the logic for displaying the total cost of all the locations.&lt;/p&gt;

&lt;p&gt;Update the &lt;code&gt;index.js&lt;/code&gt; file with the following syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// enable user to add new location and cost&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;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLocation&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&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;totalCost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTotalCost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;locationsToRender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setlocationsToRender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;locations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documents&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;totalCostUpdated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTotalCostUpdated&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&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="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;client&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setEndpoint&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_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setProject&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_PROJECT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Subscribe to documents channel&lt;/span&gt;
    &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`databases.&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_DATABASE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.collections.&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_COLLECTION_LOCATION&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.documents`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&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;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;create&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;setlocationsToRender&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;locationsToRender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;locationsToRender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&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="p"&gt;});&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;setlocationsToRender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="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;updateTotalCost&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;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;locationsToRender&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;location&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;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;car&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;acc&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;car&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;setTotalCost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&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="nx"&gt;updateTotalCost&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;totalCostUpdated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;locationsToRender&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;handleLocationName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;setLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handgleCostChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;setCost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addLocation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&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;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Client&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;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Databases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OUR PROJECT ENDPOINT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;setProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OUR PROJECT ID&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OUR DATABASE ID&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;OUR LOCATION COLLECTION ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// generates unique IDs&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="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// the location state,&lt;/span&gt;
      &lt;span class="na"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cost&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// the cost state&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;setTotalCostUpdated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;totalCostUpdated&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;setCost&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;setLocation&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="p"&gt;}),&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="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;deleteLocation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;location&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;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Client&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;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Databases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setEndpoint&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_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setProject&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_PROJECT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deleteDocument&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_DATABASE&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_COLLECTION_LOCATION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$id&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;setTotalCostUpdated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;totalCostUpdated&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;//paste the following code at the end of index.js file just below the list of locations&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LocationList&lt;/span&gt; &lt;span class="nx"&gt;locations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;locationsToRender&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h3&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;heading-level-3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Total&lt;/span&gt; &lt;span class="nx"&gt;Cost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;value&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;/h3&lt;/span&gt;&lt;span class="err"&gt;&amp;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 above code snippet does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates a &lt;code&gt;cost&lt;/code&gt; and &lt;code&gt;location&lt;/code&gt; state to handle user inputs for cost and location, respectively.&lt;/li&gt;
&lt;li&gt;Creates a &lt;code&gt;totalCost&lt;/code&gt; state to calculate the total cost of all the locations.&lt;/li&gt;
&lt;li&gt;Creates a &lt;code&gt;totalCostUpdated&lt;/code&gt; state to check whether the total cost has been updated. It is passed into the dependency array of the &lt;code&gt;useEffect()&lt;/code&gt; hook to ensure that the application rerenders whenever a location is added or removed from the database.&lt;/li&gt;
&lt;li&gt;Creates a &lt;code&gt;locationsToRender&lt;/code&gt; state to hold &lt;code&gt;locations.documents&lt;/code&gt; which is the list of &lt;strong&gt;locations&lt;/strong&gt; passed to the &lt;code&gt;Home&lt;/code&gt; components as props.&lt;/li&gt;
&lt;li&gt;We create a &lt;code&gt;subscription&lt;/code&gt; function in the &lt;code&gt;useEffect()&lt;/code&gt; hook to subscribe to changes in the location collection documents. We then use the &lt;code&gt;try-catch&lt;/code&gt; block to update the &lt;code&gt;locationsToRender&lt;/code&gt; state appropriately whenever the user adds or deletes a location.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;addLocation&lt;/code&gt; function creates a new client connection and establishes a connection to the database. Then it uses the &lt;a href="https://appwrite.io/docs/databases#attributes"&gt;database.createDocument()&lt;/a&gt; method to create a new document in the collection.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;deleteLocation&lt;/code&gt; function uses the &lt;a href="https://appwrite.io/docs/client/databases?sdk=web-default#databasesDeleteDocument"&gt;database.deleteDocument()&lt;/a&gt; method to remove a document from the collection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since we selected &lt;strong&gt;Cascade&lt;/strong&gt; as the desired on-delete behavior while configuring the &lt;strong&gt;Relationship&lt;/strong&gt;, both collections will be gone whenever we delete either collection. Again, that's another cool thing about the &lt;strong&gt;relationships in database&lt;/strong&gt; feature.&lt;/p&gt;

&lt;p&gt;Our final application should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.loom.com/share/99e0544630a34cd99c434e6719a0f52f"&gt;https://www.loom.com/share/99e0544630a34cd99c434e6719a0f52f&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Appwrite's Relationships feature is a powerful tool for managing databases, enabling the elimination of redundant information when working with data sets. Pink Design is a flexible framework that empowers developers to create highly responsive applications while providing extensive customization options. This winning combination enables us to rapidly build applications with captivating UIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pink.appwrite.io/?utm_source=hackmamba&amp;amp;utm_medium=hackmamba-blog"&gt;Pink Design Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://appwrite.io/docs/getting-started-for-web?utm_source=hackmamba&amp;amp;utm_medium=hackmamba-blog"&gt;Appwrite Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/docs"&gt;Next.js Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://appwrite.io/docs/databases-relationships?utm_source=hackmamba&amp;amp;utm_medium=hackmamba-blog"&gt;Appwrites’ Relationship in Database Feature Documentation&lt;/a&gt;
&lt;a href="https://nextjs.org/docs"&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Which Frontend Framework to Pick for Your E-commerce Storefront?</title>
      <dc:creator>Dalu46</dc:creator>
      <pubDate>Thu, 16 Jun 2022 11:17:51 +0000</pubDate>
      <link>https://forem.com/medusajs/which-frontend-framework-to-pick-for-your-e-commerce-storefront-12km</link>
      <guid>https://forem.com/medusajs/which-frontend-framework-to-pick-for-your-e-commerce-storefront-12km</guid>
      <description>&lt;p&gt;An ecommerce storefront should provide users with the best experience to facilitate their purchases. A lot of factors can affect the user’s experience; from the quickly the pages load to the intuitive experience it provides.&lt;/p&gt;

&lt;p&gt;However, not all ecommerce platforms provide you with the flexibility to implement a good experience in your ecommerce storefront, especially if you’re creating a custom experience based on your brand.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/medusajs/medusa" rel="noopener noreferrer"&gt;Medusa&lt;/a&gt; is an open source headless commerce with many ecommerce functionalities like an intuitive admin panel, order management, RMA flows, and much more. &lt;/p&gt;

&lt;p&gt;Unlike other ecommerce platforms, Medusa is completely customizable and extendable, meaning that every business ranging from startups to enterprise-sized businesses can benefit from using Medusa to create personalized experiences for their own businesses and their customers.&lt;/p&gt;

&lt;p&gt;This article introduces you to the most efficient frontend frameworks that can be the best fit for building a storefront for your headless commerce server.&lt;/p&gt;

&lt;h2&gt;
  
  
  8 Frontend Frameworks for your Ecommerce Storefront
&lt;/h2&gt;

&lt;p&gt;Frontend frameworks help ease development processes by providing tools to simplify the creation of repetitive tasks for specific projects.&lt;/p&gt;

&lt;p&gt;There are many frontend frameworks that are fit for building ecommerce storefronts, but it's impossible to cover all of them or a few of them extensively in a single article. For this reason, this article focuses mainly on the functionalities that the listed frameworks possess to facilitate headless ecommerce storefronts.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Next.js&lt;/strong&gt;
&lt;/h3&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feo5n9p1ehkdelv80xozw.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feo5n9p1ehkdelv80xozw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; is a React-based frontend framework that offers you building blocks for creating blazing-fast and user-friendly websites. &lt;/p&gt;

&lt;p&gt;Next.js offers several functionalities such as server-side rendering (SSR) and data fetching to give faster page load and improve your end-user experience. It also offers seamless integration to third-party services which can help you create highly interactive ecommerce stores.&lt;/p&gt;

&lt;p&gt;Medusa has a &lt;a href="https://docs.medusajs.com/starters/nextjs-medusa-starter" rel="noopener noreferrer"&gt;Next.js starter&lt;/a&gt; to help you easily spin up a Next.js ecommerce storefront within a matter of minutes and start selling your products.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros of Next.js&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It offers a lot of features to enhance your website’s speed including image optimization and code-splitting and bundling.&lt;/li&gt;
&lt;li&gt;It offers both SSG and SSR rendering modes.&lt;/li&gt;
&lt;li&gt;It offers API and file system routing.&lt;/li&gt;
&lt;li&gt;It provides &lt;a href="https://nextjs.org/docs/getting-started" rel="noopener noreferrer"&gt;detailed documentation&lt;/a&gt; and a library of &lt;a href="https://nextjs.org/examples" rel="noopener noreferrer"&gt;examples&lt;/a&gt; to learn better how to use Next.js&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Gatsby&lt;/strong&gt;
&lt;/h3&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8axzehn0c5syifey62ir.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8axzehn0c5syifey62ir.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.gatsbyjs.com/" rel="noopener noreferrer"&gt;Gatsby&lt;/a&gt; is a React-based static site generator that combines the functionality of React for customizing your ecommerce website structure and GraphQL to make efficient API calls. &lt;/p&gt;

&lt;p&gt;Gatsby provides you with a &lt;a href="https://www.gatsbyjs.com/plugins/" rel="noopener noreferrer"&gt;library of plugins&lt;/a&gt; that enables you to integrate third-party applications and services related to analytics, CMS, ecommerce, and more.&lt;/p&gt;

&lt;p&gt;Gatsby’s popularity has grown over the years, and as a matter of fact, most e-commerce platforms offer resources to enable you to easily spin up a Gatsby ecommerce store. Medusa as one of them provides a &lt;a href="https://docs.medusajs.com/starters/gatsby-medusa-starter" rel="noopener noreferrer"&gt;Gatsby starter&lt;/a&gt; template to help you quickly bootstrap an ecommerce storefront in a very short time.&lt;/p&gt;

&lt;p&gt;Alternatively, you can use &lt;a href="https://github.com/medusajs/medusa/tree/master/packages/gatsby-source-medusa" rel="noopener noreferrer"&gt;Medusa’s Gatsby source&lt;/a&gt; plugin if you want to develop a Gatsby storefront from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros of Gatsby&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is optimized for great speed and performance.&lt;/li&gt;
&lt;li&gt;It offers great developer experience with &lt;a href="https://www.gatsbyjs.com/docs" rel="noopener noreferrer"&gt;detailed documentation&lt;/a&gt; and libraries of &lt;a href="https://www.gatsbyjs.com/plugins/" rel="noopener noreferrer"&gt;plugins&lt;/a&gt;, &lt;a href="https://www.gatsbyjs.com/starters/" rel="noopener noreferrer"&gt;starters&lt;/a&gt;, and &lt;a href="https://www.gatsbyjs.com/docs/themes/" rel="noopener noreferrer"&gt;themes&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;It is easily customizable with concepts like &lt;a href="https://www.gatsbyjs.com/docs/how-to/plugins-and-themes/shadowing/" rel="noopener noreferrer"&gt;shadowing for themes&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Nuxt.js&lt;/strong&gt;
&lt;/h3&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxsph5ihbxzak3bypnio.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxsph5ihbxzak3bypnio.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nuxtjs.org/" rel="noopener noreferrer"&gt;Nuxt.js&lt;/a&gt; is a Vue framework built to optimize app performance. Nuxt.js offers support for SEO (search engine optimization) settings out of the box and several other functionalities like code-splitting, automatically generated routes*&lt;em&gt;,&lt;/em&gt;* pre-rendering, and Server Side Rendering (SSR). &lt;/p&gt;

&lt;p&gt;You can leverage all of its functionalities to create an overall fast and improved search engine optimized ecommerce store.&lt;/p&gt;

&lt;p&gt;If you’re interested in learning how to create a Nuxt.js storefront for your Medusa server, here’s a &lt;a href="https://dev.to/medusajs/an-open-source-ecommerce-platform-for-nuxtjs-fnl"&gt;tutorial&lt;/a&gt; that will guide you through it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros of Nuxt.js&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It has a small size and it’s great for search engine optimization.&lt;/li&gt;
&lt;li&gt;It includes support for different types of &lt;a href="https://nuxtjs.org/examples/middlewares/router" rel="noopener noreferrer"&gt;middlewares&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;It delivers a great developer experience through &lt;a href="https://nuxtjs.org/docs/get-started/installation" rel="noopener noreferrer"&gt;detailed documentation&lt;/a&gt;, &lt;a href="https://nuxtjs.org/examples/routing/hello-world" rel="noopener noreferrer"&gt;example code snippets&lt;/a&gt;, and libraries of &lt;a href="https://modules.nuxtjs.org" rel="noopener noreferrer"&gt;modules&lt;/a&gt; and &lt;a href="https://nuxtjs.org/themes" rel="noopener noreferrer"&gt;themes&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Vue Storefront&lt;/strong&gt;
&lt;/h3&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqrmmf1x94121fek51620.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqrmmf1x94121fek51620.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.vuestorefront.io/" rel="noopener noreferrer"&gt;Vue Storefront&lt;/a&gt; is an open source frontend framework for headless ecommerce built on top of Vue.js. It provides ready-made integrations to existing ecommerce platforms and allows you to extend the framework to build your own integration.&lt;/p&gt;

&lt;p&gt;It has a small bundle size and only loads what is required, leveraging code-splitting, lazy-loading, and lazy hydration to produce lightning-fast online stores.&lt;/p&gt;

&lt;p&gt;You can alternatively use their &lt;a href="https://docs.storefrontui.io/" rel="noopener noreferrer"&gt;Storefront UI&lt;/a&gt; library. It is a Vue.js design system for ecommerce. It allows you to use Vue Storefront’s UI when building your own Vue.js or Nuxt.js ecommerce storefront&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros of Vue Storefront&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The frontend is independent of what ecommerce platform you use for the backend, allowing you to switch from one to another if necessary.&lt;/li&gt;
&lt;li&gt;It has a lot of plugins and integrations to headless CMS services, payment providers, search engines, and more.&lt;/li&gt;
&lt;li&gt;It is great for building mobile-friendly user experiences and a unified user experience across platforms.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Remix&lt;/strong&gt;
&lt;/h3&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6f8vox0zbwpziamp6n7g.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6f8vox0zbwpziamp6n7g.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://remix.run/" rel="noopener noreferrer"&gt;Remix&lt;/a&gt; has been gaining popularity in the ecommerce space lately. Remix is a React-based framework with a lot of potential features for ecommerce like data loading, built-in routing, built-in forms, client/server-side rendering, and more. &lt;/p&gt;

&lt;p&gt;Remix’s features improve the build time of an ecommerce store. Remix is a good choice for large ecommerce stores with dynamic (always changing) contents like frequent changes in price or changes in stock.&lt;/p&gt;

&lt;p&gt;If you’re interested in creating a Remix storefront with Medusa, you can check out &lt;a href="https://medusajs.com/blog/remix-ecommerce/" rel="noopener noreferrer"&gt;our tutorial&lt;/a&gt; that will guide you through it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros of Remix&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It provides a parallel-loading mechanism for faster loading.&lt;/li&gt;
&lt;li&gt;It enhances the loading of updated data loaded from the server.&lt;/li&gt;
&lt;li&gt;It supports easier error handling to handle errors in unprecedented situations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Angular&lt;/strong&gt;
&lt;/h3&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmqo5rfobbnh8ac2gbjj3.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmqo5rfobbnh8ac2gbjj3.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://angular.io/" rel="noopener noreferrer"&gt;Angular&lt;/a&gt; is a component-based frontend framework and platform built on top of TypeScript. It offers a set of well-integrated libraries with a wide range of functionalities including server-client communication, routing, cross-platform apps, and more. &lt;/p&gt;

&lt;p&gt;Angular’s functionalities facilitate producing highly performant ecommerce stores. It also offers a set of developer tools designed to aid in testing and updating your code to improve the developer experience. It is suitable for building larger full-fledged ecommerce stores.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros of Angular&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Although it has a steep learning curve, It provides a simplified testing function for testing your code to give a better development experience.&lt;/li&gt;
&lt;li&gt;It is great for Model-View-Controller (MVC), a model for creating scalable projects.&lt;/li&gt;
&lt;li&gt;It provides utility functions and features for animation, accessibility, and more.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Ember.js&lt;/strong&gt;
&lt;/h3&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7sazcgvo6rl6g637c296.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7sazcgvo6rl6g637c296.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://emberjs.com/" rel="noopener noreferrer"&gt;Ember.js&lt;/a&gt; is a component-based framework for building ecommerce stores. It offers stability as it has a very short release circle which allows it to add fixes and new features. This is beneficial in the fast-moving world of frontend web development and technology. &lt;/p&gt;

&lt;p&gt;It offers a ready-to-use configuration for build pipelines, testing, routing, and more which makes it optimal for large-scale ecommerce storefronts. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros of Ember.js&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is a good choice for building scalable UIs and ecommerce stores.&lt;/li&gt;
&lt;li&gt;It facilitates building efficient and highly performant websites.&lt;/li&gt;
&lt;li&gt;It has &lt;a href="https://guides.emberjs.com/release/" rel="noopener noreferrer"&gt;detailed documentation&lt;/a&gt;, an &lt;a href="https://api.emberjs.com" rel="noopener noreferrer"&gt;API reference&lt;/a&gt;, and a &lt;a href="https://cli.emberjs.com" rel="noopener noreferrer"&gt;guide for their CLI tool&lt;/a&gt; to provide a better developer experience. It also has a &lt;a href="https://discord.gg/emberjs" rel="noopener noreferrer"&gt;Discord server&lt;/a&gt; to get help instantly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Sapper&lt;/strong&gt;
&lt;/h3&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Farklfaohulc5gi4ynit2.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Farklfaohulc5gi4ynit2.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sapper.svelte.dev" rel="noopener noreferrer"&gt;Sapper&lt;/a&gt; is a frontend framework built on top of Svelte for building dynamic, interactive, and customizable websites with minimal code. &lt;/p&gt;

&lt;p&gt;It offers server-side rendering and code-splitting which facilitates building fast ecommerce storefronts. It includes all of Svelte’s features like reactivity, no virtual DOM, and more to give an overall developer experience and make your ecommerce website load faster. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros of Sapper&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As it is powered by Svelte, your ecommerce storefront will be fast and small.&lt;/li&gt;
&lt;li&gt;It helps you build reactive and highly scalable ecommerce stores.&lt;/li&gt;
&lt;li&gt;It requires less code to build an ecommerce store which makes it a good choice for a better development experience.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Choosing one framework over another for your ecommerce storefront is totally dependent on your use case and the developer experience they offer.&lt;/p&gt;

&lt;p&gt;When picking a framework for your ecommerce storefront, it is important to pick a framework that offers a great development experience, a fast and highly performant storefront, and more customization features. &lt;/p&gt;

&lt;p&gt;If you are interested in starting out an ecommerce project, Medusa is the right platform for you as it provides you with several functionalities and resources to aid integration with popular frontend frameworks. &lt;/p&gt;

&lt;p&gt;Refer to Medusa’s &lt;a href="https://github.com/medusajs/medusa" rel="noopener noreferrer"&gt;quickstart guide&lt;/a&gt; to getting started in minutes. Also, if you have any Medusa-related questions or issues, you can reach out to the Medusa team via &lt;a href="https://discord.gg/medusajs" rel="noopener noreferrer"&gt;discord&lt;/a&gt; for instant help.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
