<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ashwin Hariharan</title>
    <description>The latest articles on DEV Community by Ashwin Hariharan (@booleanhunter).</description>
    <link>https://dev.to/booleanhunter</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%2F108200%2F3f49e017-fd5f-4531-87fe-e4bb39f82949.jpg</url>
      <title>DEV Community: Ashwin Hariharan</title>
      <link>https://dev.to/booleanhunter</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/booleanhunter"/>
    <language>en</language>
    <item>
      <title>Organizing AI Applications: Lessons from traditional software architecture</title>
      <dc:creator>Ashwin Hariharan</dc:creator>
      <pubDate>Mon, 05 Jan 2026 13:30:00 +0000</pubDate>
      <link>https://dev.to/redis/organizing-ai-applications-lessons-from-traditional-software-architecture-mpe</link>
      <guid>https://dev.to/redis/organizing-ai-applications-lessons-from-traditional-software-architecture-mpe</guid>
      <description>&lt;p&gt;When I started learning AI and diving into frameworks like LangGraph, n8n, and the OpenAI APIs, I found plenty of great tutorials. They taught me how to build a simple chatbot, how to make my first LLM call, how to chain a few prompts together. Useful stuff for getting started.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Great for for learning. Less great for shipping.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After the first couple of weeks, I wanted to build an actual production-ready application which goes beyond standard POCs. Something which uses AI but involving dozens of routes, multiple features and services, database operations, caching layers. But those beginner tutorials weren't enough. &lt;em&gt;Where do the embeddings live? How do I structure my agent workflows? Should my API routes call AI directly, or is there supposed to be a layer in between?&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The documentation showed me how to use the framework abstractions and APIs. It didn't show me how to organize them.&lt;/p&gt;

&lt;p&gt;If you're like me, coming from a JavaScript/TypeScript background, you know the language gives you a lot of freedom - no enforced folder structure, no prescribed architecture. You can organize your code however you want. But that freedom comes at a price.&lt;/p&gt;

&lt;p&gt;Without clear patterns to guide where things should go, you might end up with working code in all the wrong places. Calls to OpenAI API scattered everywhere, business logic tangled with your routes, and you just know this is going to be painful to maintain later.&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%2Frcw45hz8q1lti7j8de7b.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%2Frcw45hz8q1lti7j8de7b.png" alt="XKCD comic"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
    &lt;a href="https://xkcd.com/2044/" rel="noopener noreferrer"&gt;The cost of 'just making it work'&lt;/a&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;Here's the thing: software architecture trends come and go. In 2015, everyone said microservices were the future. In 2018, serverless. In 2021, JAMstack. In 2023, everyone quietly went back to monoliths.&lt;/p&gt;

&lt;p&gt;But you know what remained constant through all these trends? The fundamental principle of separating concerns. These same software development principles apply to AI and agentic AI applications. Whether you're building traditional web apps or AI-powered systems, the need for clear architecture remains constant.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Makes AI projects maintainable
&lt;/h2&gt;

&lt;p&gt;Let's establish what properties a good AI project should have:&lt;/p&gt;

&lt;h3&gt;
  
  
  Clear Ownership Boundaries
&lt;/h3&gt;

&lt;p&gt;Clear ownership boundaries define which part of your system is responsible for what. Good boundary means when something breaks or needs extension, you immediately know which component to check.&lt;/p&gt;

&lt;p&gt;Each distinct concern in your application should be handled by a single, well-defined module or component. This way, when something goes wrong or when you need to add a feature, you'll immediately know which part of your codebase is responsible.&lt;/p&gt;

&lt;p&gt;Clear boundaries mean each concern lives in an identifiable place. When something goes wrong, you immediately know which module to check. When you need to add a feature, you know which component to extend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reusability across entry points
&lt;/h3&gt;

&lt;p&gt;Reusability means writing logic once and calling it from anywhere.&lt;/p&gt;

&lt;p&gt;Your core business logic should work the same way regardless of how it's triggered. Whether called from a web API, an AI agent, a scheduled job, a command-line tool, a message queue, or a test suite, the same functionality should be available without rewriting it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it matters&lt;/strong&gt;: Today it's a chat API. Tomorrow you may want a Slack bot. Next week, batch processing. And who knows? Maybe you'll discover that your users actually prefer the regular search over your fancy AI chatbot anyway. If your AI code is tied to say, your controllers, you might have to rewrite it each time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testability
&lt;/h3&gt;

&lt;p&gt;Testability is the degree to which a piece of software can be tested easily and effectively. It describes how simple it is to check that the software works as intended. High testability means tests can be written quickly, run reliably, and give clear results, while low testability leads to tests that are difficult, slow, or unclear.&lt;/p&gt;

&lt;p&gt;AI applications have many moving parts. Vector search slow? Cache not hitting? Agent hallucinating? Embedding generation failing? When you're debugging, you shouldn't have to hunt through your entire codebase to find the problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Provider Independence
&lt;/h3&gt;

&lt;p&gt;Swapping from GPT-4 to Claude to Gemini shouldn't require changing business logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it matters&lt;/strong&gt;: AI models evolve weekly. Today's best model is next month's deprecated one. Providers change pricing. Features get sunset. Your architecture should make provider switching very straightforward.&lt;/p&gt;




&lt;p&gt;If you're a software engineer who has started building AI applications and you want to move beyond simple code snippets and demos, let me show you what worked for me. In the following sections, I'll walk you through patterns I extracted from a real project - patterns that apply whether you're building e-commerce, SaaS tools, content platforms, or any AI-powered application.&lt;/p&gt;

&lt;p&gt;For illustration, I'll use an AI-powered restaurant discovery application. Think of a platform where users search for restaurants, browse by cuisine or location, and make reservations - the typical flow you'd see in apps like Zomato or Yelp. Now imagine you decide to add a chat feature to make restaurant discovery more conversational - a dining assistant in a corner chat window that enables finding the perfect restaurant through natural conversation.&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%2Fqud2lsewg66yjrrap737.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%2Fqud2lsewg66yjrrap737.png" alt="A simplified AI powered restaurant discovery app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
    &lt;a href="https://github.com/redis-developer/restaurant-discovery-ai-agent-demo" rel="noopener noreferrer"&gt;View the implementation on GitHub&lt;/a&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;So now, instead of just filtering by &lt;em&gt;"Italian cuisine"&lt;/em&gt;, diners can now also ask questions like &lt;em&gt;"I need a romantic spot with live music for an anniversary dinner"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;What this really comes down to:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How do you add these AI capabilities without major refactoring of your existing system? And without over-engineering a solution that's way more complex than it needs to be?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Throughout the code examples, I will use Node.js / JavaScript as its syntax is widely familiar to anyone building apps for the web and generalizes well to other languages. But these architectural patterns apply equally to Python, Java, or any other language you're working with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architectural patterns for AI apps
&lt;/h2&gt;

&lt;p&gt;After some research and quite a few experiments and trying out different approaches, I settled on few patterns that actually work. Before we dive in, know that these aren't necessarily AI-specific. They're the same principles that make any large application maintainable. But when you build with these patterns from the start, adding and testing AI features later becomes straightforward.&lt;/p&gt;

&lt;p&gt;Let's look at what they look like:&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 1: Structure by business components or modules
&lt;/h3&gt;

&lt;p&gt;Rather than organizing code by technical function (all controllers together, all models together), organize by business components. Each module represents a bounded context with its own API, logic, and data access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why This Matters for AI&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;AI applications typically have multiple distinct domains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conversation management (chat history, session state)&lt;/li&gt;
&lt;li&gt;AI workflow orchestration (agents, tools, prompts)&lt;/li&gt;
&lt;li&gt;Business entities (restaurants, users, reservations)&lt;/li&gt;
&lt;li&gt;Data processing (embeddings, vector search)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mixing these concerns creates cognitive overload and makes it hard to reason about the system and maintain it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Bad: Organized by technical layers&lt;/span&gt;

&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;controllers&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;chatController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;restaurantController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;reservationController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;services&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;chatService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;restaurantService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;reservationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;repositories&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
    &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;chatRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
    &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;restaurantRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
    &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;reservationRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Problem: To understand the "chat" feature, you jump between three different directories. Adding a new feature touches files across the entire codebase.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Colocation&lt;/strong&gt;: For a feature, put related code close together. Code that changes for the same feature should be neighbors and a short navigation away.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is also popularly known as &lt;strong&gt;"Domain-driven design"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you add a new feature to the chat system, you typically need to modify the API endpoint, update the business logic, and adjust the data access layer. With domain-driven design, all these files are in the same &lt;code&gt;chat/&lt;/code&gt; directory-you never leave that folder. Without it, you're jumping between &lt;code&gt;controllers/&lt;/code&gt;, &lt;code&gt;services/&lt;/code&gt;, and &lt;code&gt;repositories/&lt;/code&gt; directories, trying to remember which pieces connect.&lt;/p&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%2Fcrjchqqpzuttzq6zyasl.png" alt="Domain-Driven Design Architecture showing chat, restaurants, and reservations modules"&gt;Each domain is self-contained with its own API, Domain, and Data layers
  


&lt;p&gt;Benefit: Everything related to "chat" lives in one place. Each business component is self-contained.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Good: Organized by business components&lt;/span&gt;

&lt;span class="nx"&gt;modules&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;chatController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;chatService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;chatRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;restaurants&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;restaurantController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;restaurantService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;restaurantRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;reservations&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
    &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;reservationController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
    &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;reservationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
    &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;reservationRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When you need to modify how restaurants are searched, you go directly to &lt;code&gt;modules/restaurants/&lt;/code&gt;. When you need to add a new AI tool, it goes in &lt;code&gt;modules/ai/agentic/tools&lt;/code&gt;. There's no guessing, no hunting through dozens of files.&lt;/p&gt;

&lt;p&gt;This also means you could extract any of these services into a separate microservice later without major refactoring - the boundaries are already defined.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pattern 2: Layer your feature modules with 3-tier architecture
&lt;/h3&gt;

&lt;p&gt;While Pattern 1 is about grouping by business domain, Pattern 2 applies the same concept of &lt;em&gt;colocation&lt;/em&gt; &lt;em&gt;within&lt;/em&gt; each domain. The API, domain, and data layers for a feature stay together in the same feature module folder, not scattered across the codebase.&lt;/p&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%2Fsqiat09pmg9v4ofmtjqk.png" alt="Three-tier architecture pattern showing entry points, domain logic, and data access layers"&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%2Fujddszzifba01n7ztpcf.png" alt="Three-tier architecture pattern showing entry points, domain logic, and data access layers"&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%2Fiias83cq8nwmtx6vljf9.png" alt="Three-tier architecture pattern showing entry points, domain logic, and data access layers"&gt;Three-tier architecture
  


&lt;p&gt;Within each module, maintain clear separation between three concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Entry Points&lt;/strong&gt; (API routes, message queue consumers, scheduled jobs)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain Logic&lt;/strong&gt; (business rules, workflows, services)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Access&lt;/strong&gt; (database queries, external API calls) - this is also called &lt;em&gt;repository-pattern&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&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%2Frsoojgm0ismted7g8by0.png" alt="Restaurants module three-tier architecture with controller, service, and repository layers"&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%2Ffwm8h5jfoubd1gdy05dk.png" alt="Reservations module three-tier architecture with controller, service, and repository layers"&gt;


&lt;p&gt;&lt;strong&gt;The Critical Rule:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Never pass framework-specific objects (Express &lt;code&gt;Request&lt;/code&gt;/&lt;code&gt;Response&lt;/code&gt;, HTTP headers, etc.) into your domain layer. Domain logic should be pure and reusable across different entry points.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Don't do this: Domain logic coupled to Express&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;Request&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="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="nf"&gt;handleChatMessage&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;Request&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;Response&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;sessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionId&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;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// AI workflow logic mixed with HTTP handling&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;await&lt;/span&gt; &lt;span class="nx"&gt;aiAgent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&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;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;response&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The problem with the above code is that this function would only work with Express. But AI workflows often need to be triggered from multiple sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP API requests (user chat)&lt;/li&gt;
&lt;li&gt;Scheduled jobs (batch processing)&lt;/li&gt;
&lt;li&gt;Message queues (async workflows)&lt;/li&gt;
&lt;li&gt;Tests (validation)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your AI logic is tightly coupled to your HTTP layer, you can't reuse it elsewhere, and you won't be able to call it from a scheduled job, test, or CLI tool.&lt;/p&gt;

&lt;p&gt;Your AI workflow orchestration should be completely separate from the HTTP layer. Here's an example:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Good: Clean separation&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;Request&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getResponseFromAgent&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;@modules/chat&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="nf"&gt;handleChatMessage&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;Request&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;Response&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;sessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="k"&gt;as&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;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="k"&gt;as&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;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;await&lt;/span&gt; &lt;span class="nf"&gt;getResponseFromAgent&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;sessionId&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;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;response&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ business logic - no HTTP dependencies&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="nf"&gt;getResponseFromAgent&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionId&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="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;await&lt;/span&gt; &lt;span class="nx"&gt;aiAgent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionId&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;response&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;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;toolsUsed&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;tools&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;Now, &lt;code&gt;getResponseFromAgent()&lt;/code&gt; can be called from anywhere - HTTP endpoints, scheduled jobs, tests, or CLI scripts. &lt;/p&gt;

&lt;p&gt;The API layer now focuses only on handling HTTP concerns - receiving the request, extracting the session ID and message, and returning a response - while delegating all business logic to the domain layer.&lt;/p&gt;

&lt;p&gt;Similarly, use the &lt;a href="https://martinfowler.com/eaaCatalog/repository.html" rel="noopener noreferrer"&gt;Repository pattern&lt;/a&gt; to prevent database details from leaking into business logic:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Don't put this in your services / domain logic&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;searchRestaurants&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;redis&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;redisClient&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="nx"&gt;results&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;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;idx:restaurants&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// transform query response...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Good&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;searchRestaurants&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;queryEmbeddings&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;generateEmbeddings&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;restaurantRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vectorSearchRestaurants&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queryEmbeddings&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ restaurant-repository.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RestaurantRepository&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;vectorSearchRestaurants&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchVector&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="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Redis-specific implementation hidden&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&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;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transformToRestaurants&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&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 domain layer remains pure and independent of HTTP and database implementation, returning structured results. Now if you need to switch databases based on performance or cost, your domain services don't change - only the repository implementation does.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pattern 3: Tools and prompts should call domain logic, not implement it
&lt;/h3&gt;

&lt;p&gt;When you're building AI agents with LangChain, LangGraph, or similar frameworks, you define "tools" - functions the AI can call to perform actions. Need to search products? Create a tool for that. Add items to cart? Tool. Get user preferences? Tool.&lt;/p&gt;

&lt;p&gt;There are two common anti-patterns where business logic ends up in the wrong place:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anti-pattern 1: Business logic in prompts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
You are a restaurant discovery assistant. Follow these rules:
1. Only show budget-friendly restaurants (under ₹500 per person) for budget users
2. Apply member discounts on reservations for gold members
3. Suggest fine dining establishments to premium members
...
`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Problem: Your business rules now live in natural language. They're non-deterministic, untestable, and invisible to code review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anti-pattern 2: Business logic in tools&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It feels natural to write business logic directly in tool functions. During a conversation in the chat interface, the user wants to search products, so you write the search logic right there in the tool. You need database access, so you import the database client. You need to validate the search query, calculate relevance scores, apply business rules - all of it goes into the tool.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Bad: Business logic inside AI tool&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;tool&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;@langchain/core/tools&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;z&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;zod&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;createClient&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;redis&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchRestaurantsTool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tool&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;query&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;// Database access directly in tool&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;await&lt;/span&gt; &lt;span class="nx"&gt;createClient&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="nx"&gt;embedding&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;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&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;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;idx:restaurants&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;// Business logic in tool&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filtered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;priceFor2&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2000&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;sorted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rating&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rating&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;sorted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&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="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;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;search_restaurants&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Search for restaurants&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The problem with the above code is that the core logic is locked in the AI tool. Few  months later, you might realize you need that same search logic in a REST API endpoint, or in a scheduled job, or in a different agent. But it's tightly coupled to the AI framework you used.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tools should be thin wrappers that translate AI intent into domain service calls.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's it. The tool's job is simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receive parameters from the AI&lt;/li&gt;
&lt;li&gt;Validate/transform them if needed&lt;/li&gt;
&lt;li&gt;Call the appropriate domain service&lt;/li&gt;
&lt;li&gt;Return the result in a format the AI understands&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The actual business logic? That lives in domain services, completely independent of any AI framework.&lt;/p&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%2Fn90q9ju5bukzkkt25l3m.png" alt="AI Tools Pattern"&gt;AI tools as thin wrappers calling domain services
  


&lt;p&gt;Here's how you can write it:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Good: logic lives separately&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="nf"&gt;searchRestaurants&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&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;queryEmbeddings&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;generateEmbeddings&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchVector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;queryEmbeddings&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;restaurants&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;restaurantRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vectorSearchRestaurants&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;searchVector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;limit&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;restaurants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rating&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rating&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Tool simply calls domain service&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;searchRestaurants&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;@modules/restaurants&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;tool&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;@langchain/core/tools&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;z&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;zod&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchRestaurantsTool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tool&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;query&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;// Just a thin wrapper&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;restaurants&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;searchRestaurants&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;limit&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;restaurants&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;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;search_restaurants&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Search for restaurants&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Benefit: Your business logic is now independent, just plain functions that take input and return output - you can reuse it everywhere. &lt;code&gt;searchRestaurants()&lt;/code&gt; can now be called from AI tools, HTTP endpoints, CLI scripts, or tests.&lt;/p&gt;

&lt;p&gt;Ask yourself: &lt;em&gt;"If I needed this logic in a REST API endpoint tomorrow, would I have to copy-paste code or could I just import a function?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If the answer is copy-paste, your logic is in the wrong place.&lt;/p&gt;

&lt;p&gt;When your tools are thin adapters to domain services, you can test the core behavior locally without calling the LLM at all. This makes tests fast, reliable, and deterministic. Your code becomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reusable across HTTP, CLI, scheduled jobs, tests&lt;/li&gt;
&lt;li&gt;Testable without AI framework mocking&lt;/li&gt;
&lt;li&gt;Maintainable because business logic lives in one place&lt;/li&gt;
&lt;li&gt;Flexible because you can change AI frameworks without rewriting business logic&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Pattern 4: Dependency Inversion
&lt;/h3&gt;

&lt;p&gt;High-level policy (your business logic) should not depend on low-level details (specific frameworks, providers, or databases). This follows the &lt;a href="https://en.wikipedia.org/wiki/Dependency_inversion_principle" rel="noopener noreferrer"&gt;Dependency Inversion Principle&lt;/a&gt; from Robert C. Martin's SOLID principles: your code should depend on abstractions, not on concrete implementations.&lt;/p&gt;

&lt;p&gt;In simpler terms: when you use external services-whether it's OpenAI, AWS Bedrock, LangChain, or Redis-hide them behind interfaces that match &lt;em&gt;your domain's&lt;/em&gt; needs, not theirs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this matters for AI:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI applications introduce volatile dependencies that change frequently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI Providers switch constantly&lt;/strong&gt;: Today's OpenAI becomes tomorrow's Anthropic or AWS Bedrock&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Models evolve rapidly&lt;/strong&gt;: GPT-4 becomes GPT-4 Turbo becomes GPT-4o&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frameworks shift&lt;/strong&gt;: LangChain might become LangGraph, or you might switch to a different agentic framework entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vector databases compete&lt;/strong&gt;: Pinecone, Weaviate, Redis Vector Search - requirements change.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without DIP, switching any of these requires changes across your entire codebase. With DIP, you change one file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-world example: switching AI providers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consider an embeddings service. Without DIP, you'd scatter OpenAI SDK calls throughout your codebase:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Bad: Tightly coupled to OpenAI everywhere&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;OpenAI&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;openai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;searchRestaurants&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;openai&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;OpenAI&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;OPENAI_KEY&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;embedding&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;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;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;text-embedding-3-small&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&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="c1"&gt;// search logic using embedding...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now imagine your company decides to switch to AWS Bedrock for cost savings. You'd need to find and modify every place that calls OpenAI's embedding API.&lt;/p&gt;

&lt;p&gt;With DIP, high level policy does not depend on low level details:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Good: embeddings.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;OpenAI&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;openai&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="nf"&gt;generateEmbeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;texts&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="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Implementation hidden behind interface&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;searchRestaurants&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Generate embedding for the search query&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;textEmbeddings&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;generateEmbeddings&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// Use repository for pure data operations&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;restaurantRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vectorSearchRestaurants&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textEmbeddings&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 implementation file changes based on your provider, but the &lt;em&gt;interface&lt;/em&gt; stays the same:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BedrockEmbeddings&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;@langchain/aws&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="nf"&gt;generateEmbeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;texts&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;embeddings&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;BedrockEmbeddings&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="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;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;region&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;awsRegion&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;embedQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// openai-version/embeddings.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;OpenAI&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;openai&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="nf"&gt;generateEmbeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;texts&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;openai&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;OpenAI&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;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;openAiApiKey&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;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;text-embedding-3-small&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&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;texts&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;embedding&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;Your domain services import from the abstraction:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;generateEmbeddings&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;@modules/ai/helpers&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="nf"&gt;findRestaurantsBySemanticSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;queryEmbeddings&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;generateEmbeddings&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchVector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;queryEmbeddings&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;restaurantRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vectorSearchRestaurants&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchVector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Switching providers now means changing one file&lt;/strong&gt;, not hunting through your entire codebase. The LLM becomes just one dependency behind a clear interface, which we can replace with mocks or fixtures during testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example: Abstracting agent frameworks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The same principle applies to agentic AI frameworks. Your business logic shouldn't know whether you're using LangGraph, CrewAI, or AutoGen.&lt;/p&gt;

&lt;p&gt;Instead of spreading LangGraph-specific code everywhere, isolate your agentic AI workflow implementation in its own module:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Good&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;HumanMessage&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;@langchain/core/messages&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;restaurantReservationsWorkflow&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;@modules/ai/workflows&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="nf"&gt;processUserQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionId&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="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;await&lt;/span&gt; &lt;span class="nx"&gt;restaurantReservationsWorkflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HumanMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
    &lt;span class="nx"&gt;sessionId&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;response&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;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;cacheStatus&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;cacheStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;toolsUsed&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;toolsUsed&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;With Dependency Inversion, you can experiment with different models without touching business logic and move between frameworks gradually as your needs evolve. It does introduce extra abstraction, which can sometimes feel like over-engineering, but it's most valuable when dependencies are volatile, you’re evaluating multiple options, or vendor lock-in is a real risk.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pattern 5: Use environment-aware, secure, and hierarchical config
&lt;/h3&gt;

&lt;p&gt;AI applications have complex configuration needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple AI provider credentials (OpenAI, Anthropic, AWS), model identifiers and versions&lt;/li&gt;
&lt;li&gt;Rate limits and timeouts&lt;/li&gt;
&lt;li&gt;Feature flags for different AI capabilities&lt;/li&gt;
&lt;li&gt;Vector database connections&lt;/li&gt;
&lt;li&gt;Caching configuration&lt;/li&gt;
&lt;li&gt;Guardrail settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So your configuration should be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Environment-aware&lt;/strong&gt;: Different values for dev/staging/production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure&lt;/strong&gt;: Secrets never committed to version control&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validated&lt;/strong&gt;: Fail fast on startup if config is invalid&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hierarchical&lt;/strong&gt;: Organized for easy discovery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type-safe (optional)&lt;/strong&gt;: Preferably with TypeScript or runtime validation
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Bad: Secrets hardcoded, scattered config&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;openai&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;OpenAI&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;sk-abc123...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Hardcoded 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;bedrockModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;anthropic.claude-v2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Magic string&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redisHost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Where's production config?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The problem with the configuration above is that the configuration is scattered everywhere, and there’s no clean way to switch between models and environments.&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;// ✅ Good: Centralized, validated, environment-aware&lt;/span&gt;
&lt;span class="c1"&gt;// config.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dotenv&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;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;dotenv&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requiredEnvVars&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;OPENAI_API_KEY&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;AWS_ACCESS_KEY_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="s1"&gt;REDIS_HOST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// Fail fast on startup if config is missing&lt;/span&gt;
&lt;span class="nx"&gt;requiredEnvVars&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;varName&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;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;varName&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Missing required environment variable: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;varName&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="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="c1"&gt;// AI Providers&lt;/span&gt;
  &lt;span class="na"&gt;openAi&lt;/span&gt;&lt;span class="p"&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;OPENAI_API_KEY&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="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;OPENAI_MODEL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gpt-4o&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseInt&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;OPENAI_TIMEOUT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;30000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;region&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;AWS_REGION&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;accessKeyId&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;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;secretAccessKey&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;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;bedrockModelId&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;BEDROCK_MODEL_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;anthropic.claude-v2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="c1"&gt;// Databases&lt;/span&gt;
  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;host&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;REDIS_HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseInt&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;REDIS_PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;6379&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;password&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;REDIS_PASSWORD&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .env (never committed to git)&lt;/span&gt;
&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk-abc123...
&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;AKIA...
&lt;span class="nv"&gt;REDIS_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;localhost

&lt;span class="c"&gt;# .env.production (deployed separately)&lt;/span&gt;
&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk-prod123...
&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;AKIA...
&lt;span class="nv"&gt;REDIS_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;redis.production.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The benefit now is that all configuration lives in one place, it's automatically validated on startup, switching between environments is easy, and secrets are no longer hardcoded in the codebase.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pattern 6: Separate persistent data from agent memory
&lt;/h3&gt;

&lt;p&gt;AI agents need to remember things during conversations, but not everything should live in your main database. User preferences? Database. The fact that someone just asked about "cozy sweaters" 30 seconds ago? Agent memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this matters for AI:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mixing persistent and ephemeral data leads to bloated databases and slow AI responses. Different types of data have different storage requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Database&lt;/strong&gt;: User profiles, restaurant catalog, reservation history - things that need to persist indefinitely&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent Memory&lt;/strong&gt;: Conversation context, temporary preferences, session state - things that can expire&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vector Store&lt;/strong&gt;: Restaurant embeddings, semantic search indexes - specialized AI data structures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Implementation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;services/
├── chat/
│   ├── services/
│   │   ├── workflow.js         &lt;span class="c"&gt;# LangGraph orchestration&lt;/span&gt;
│   │   └── memory.js           &lt;span class="c"&gt;# Session state, conversation context&lt;/span&gt;
│   └── data/
│       └── session-store.js    &lt;span class="c"&gt;# Fast, ephemeral storage&lt;/span&gt;
├── restaurants/
│   └── data/
│       ├── restaurant-repository.js    &lt;span class="c"&gt;# Persistent restaurant vector store&lt;/span&gt;
└── &lt;span class="nb"&gt;users&lt;/span&gt;/
    └── data/
        └── user-store.js       &lt;span class="c"&gt;# User profiles, preferences&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster AI responses (no database queries for temporary data)&lt;/li&gt;
&lt;li&gt;Cleaner main database (only persistent data)&lt;/li&gt;
&lt;li&gt;Automatic cleanup (session data expires)&lt;/li&gt;
&lt;li&gt;Better performance (right storage for each data type)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Getting the storage layer right is crucial for AI performance.&lt;/p&gt;

&lt;p&gt;
  summary
  &lt;p&gt;Check out &lt;strong&gt;&lt;a href="https://university.redis.io/learningpath/hbykf3qrnhwccy?tab=details" rel="noopener noreferrer"&gt;Redis for AI learning path&lt;/a&gt;&lt;/strong&gt; for hands-on experience implementing these memory patterns and vector search capabilities. &lt;/p&gt;

&lt;/p&gt;



&lt;p&gt;Your final project architecture could look something like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;modules/
│
├── restaurants/             &lt;span class="c"&gt;# Restaurants Domain&lt;/span&gt;
│   ├── api/                   &lt;span class="c"&gt;# HTTP layer&lt;/span&gt;
│   ├── service/                &lt;span class="c"&gt;# Business logic&lt;/span&gt;
│   └── data/                  &lt;span class="c"&gt;# Data access&lt;/span&gt;
│
├── reservations/            &lt;span class="c"&gt;# Reservations Domain&lt;/span&gt;
│   ├── api/
│   ├── service/
│   └── data/
│
├── chat/                    &lt;span class="c"&gt;# Conversation Domain&lt;/span&gt;
│   ├── api/
│   ├── service/
│   └── data/
├── ai/                      &lt;span class="c"&gt;# AI Domain&lt;/span&gt;
│   ├── agentic-restaurant-workflow/
│   │   ├── index.js          &lt;span class="c"&gt;# Workflow orchestration&lt;/span&gt;
│   │   ├── nodes.js          &lt;span class="c"&gt;# Agent definitions&lt;/span&gt;
│   │   ├── tools.js          &lt;span class="c"&gt;# AI tools&lt;/span&gt;
│   │   └── state.js          &lt;span class="c"&gt;# State management&lt;/span&gt;
│   └── helpers/
│       ├── embeddings.js     &lt;span class="c"&gt;# Embedding generation&lt;/span&gt;
│       └── caching.js        &lt;span class="c"&gt;# Cache logic&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fgal43ra1n3vehd9fsvg9.png" alt="Complete Architecture with All Patterns"&gt;Complete architecture showing all six patterns working together
  


&lt;p&gt;Check out the example implementation on Github!&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/redis-developer" rel="noopener noreferrer"&gt;
        redis-developer
      &lt;/a&gt; / &lt;a href="https://github.com/redis-developer/restaurant-discovery-ai-agent-demo" rel="noopener noreferrer"&gt;
        restaurant-discovery-ai-agent-demo
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An Agentic AI restaurant discovery platform that combines Redis's speed with LangGraph's intelligent workflow orchestration. Get personalized restaurant recommendations, make reservations, and get lightning-fast responses through semantic caching.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🍽️ Restaurant Discovery AI Agent&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Redis-powered restaurant discovery with intelligent dining assistance.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An AI-powered restaurant discovery platform that combines Redis's speed with LangGraph's intelligent workflow orchestration. Get personalized restaurant recommendations, smart dining suggestions, and lightning-fast responses through semantic caching.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;App screenshots&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/redis-developer/restaurant-discovery-ai-agent-demo/./docs/screenshots/home-screen.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fredis-developer%2Frestaurant-discovery-ai-agent-demo%2F.%2Fdocs%2Fscreenshots%2Fhome-screen.png" alt="App home page"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Tech Stack&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://nodejs.org/" rel="nofollow noopener noreferrer"&gt;Node.js (v24+)&lt;/a&gt;&lt;/strong&gt; + &lt;strong&gt;Express&lt;/strong&gt; - Backend runtime and API framework&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://redis.io" rel="nofollow noopener noreferrer"&gt;Redis&lt;/a&gt;&lt;/strong&gt; - Restaurant store, agentic AI memory, conversational history, and semantic caching&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://redis.io/langcache/" rel="nofollow noopener noreferrer"&gt;Redis LangCache API&lt;/a&gt;&lt;/strong&gt; - Semantic caching for LLM responses&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LangGraph&lt;/strong&gt; - AI workflow orchestration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://platform.openai.com/account/api-keys" rel="nofollow noopener noreferrer"&gt;OpenAI API&lt;/a&gt;&lt;/strong&gt; - GPT-4 for intelligent responses and embeddings for vector search&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTML + CSS + Vanilla JS&lt;/strong&gt; - Frontend UI&lt;/li&gt;
&lt;/ul&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Product Features&lt;/h2&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Smart Restaurant Discovery&lt;/strong&gt;: AI-powered assistant helps you find restaurants, discover cuisines, and manage your reservations. Both text and vector-based search across restaurants&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dining Intelligence&lt;/strong&gt;: Get restaurant recommendations with detailed information for any cuisine or occasion using RAG (Retrieval Augmented Generation)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demo Reservation System&lt;/strong&gt;: Reservation management…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/redis-developer/restaurant-discovery-ai-agent-demo" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;Applied AI is quite fluid today - patterns, frameworks, and libraries are changing constantly. Teams are expected to deliver features fast. In this environment, your architecture needs flexibility. You can't afford to lock your code into rigid structures that make change expensive.&lt;/p&gt;

&lt;p&gt;I have been working with the patterns we discussed for quite some time and they really helped. Remember, these are not rigid rules. Learn what works for your project, and adapt. Think of your project not as a monolithic whole, but as independent, composable features with clear interfaces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If your code is scattered across technical layers, start grouping domains together grouping domains together.&lt;/li&gt;
&lt;li&gt;If your AI tools contain database queries, extract them into domain services.&lt;/li&gt;
&lt;li&gt;If you're tightly coupled to OpenAI, add an abstraction layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're working in the Node.js ecosystem, frameworks like &lt;a href="https://nestjs.com/" rel="noopener noreferrer"&gt;NestJS&lt;/a&gt; can help you implement many of these patterns out of the box - modules, dependency injection, layered architecture. But here's the thing: these patterns aren't tied to any specific framework or even any specific language.&lt;/p&gt;

&lt;p&gt;Choose the tools that work for your team, and these patterns will help you organize things in such a way that refactoring is something that is performed casually on a daily basis. It will also help pave the way to full-blown microservices in the future once your app grows. You'll likely refactor anyway after you've built something real.&lt;/p&gt;




&lt;p&gt;Thanks to &lt;a href="https://www.linkedin.com/in/raphaeldelio" rel="noopener noreferrer"&gt;Raphael De Lio&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/bhavana-anant-giri-0a43109b" rel="noopener noreferrer"&gt;Bhavana Giri&lt;/a&gt; for the detailed technical review and great suggestions on the article's structure.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to use Elasticsearch to Create an Exceptional User Experience in Retail</title>
      <dc:creator>Ashwin Hariharan</dc:creator>
      <pubDate>Fri, 12 May 2023 07:59:24 +0000</pubDate>
      <link>https://dev.to/egeninc/how-to-use-elasticsearch-to-create-an-exceptional-user-experience-in-retail-4c3</link>
      <guid>https://dev.to/egeninc/how-to-use-elasticsearch-to-create-an-exceptional-user-experience-in-retail-4c3</guid>
      <description>&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is Elasticsearch&lt;/li&gt;
&lt;li&gt;Search in retail&lt;/li&gt;
&lt;li&gt;How is search used in online shopping&lt;/li&gt;
&lt;li&gt;What you will learn in this tutorial (watch the demo!)&lt;/li&gt;
&lt;li&gt;
Data exploration

&lt;ul&gt;
&lt;li&gt;Data types and field mappings&lt;/li&gt;
&lt;li&gt;Kibana developer console&lt;/li&gt;
&lt;li&gt;Search ranking and score&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Searching for information

&lt;ul&gt;
&lt;li&gt;Text search for a product&lt;/li&gt;
&lt;li&gt;Text search for a product of a specific brand&lt;/li&gt;
&lt;li&gt;Search for a particular category of products&lt;/li&gt;
&lt;li&gt;Search for products within a certain category&lt;/li&gt;
&lt;li&gt;Search and filter for products by ratings&lt;/li&gt;
&lt;li&gt;Filter by pricing parameters&lt;/li&gt;
&lt;li&gt;Sorting search results&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Improving search relevance&lt;/li&gt;
&lt;li&gt;Enhancing search experiences on mobile devices&lt;/li&gt;
&lt;li&gt;A day in the life of an Elasticsearch Architect&lt;/li&gt;
&lt;li&gt;Resources&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;You just went to the Google home page. What do you see?&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%2F7u79uwy7gzqcirfbxiep.jpg" 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%2F7u79uwy7gzqcirfbxiep.jpg" alt="Screenshot of Google's home page" width="800" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You saw a search box, inviting you to type something. Well I’m a programmer, so I’ll just go ahead and type in the word &lt;strong&gt;recursion&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%2Fgewi33i5d1hpvizwhduf.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%2Fgewi33i5d1hpvizwhduf.png" alt="Screenshot of google's search suggestion" width="800" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Haha. A recursion joke. You’re funny, Google. But jokes aside, let’s look at the top 5 results:&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%2Fo5xh22kz9kzv0o5llokc.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%2Fo5xh22kz9kzv0o5llokc.png" alt="Screenshot of a typical google search results" width="800" height="655"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It probably took less than a second for Google to return me documents from the web that are about recursion. All ordered neatly in order of relevance.&lt;/p&gt;

&lt;p&gt;If you're reading this on your phone or laptop right now, you've likely heard of the term &lt;strong&gt;search engine&lt;/strong&gt;. A search engine is a software component that allows you to find information in a computer. It usually consists of an interface that lets you enter a search query - where you  specify criteria about an item of interest. This could be in the form of words relevant to the specific topic (such as COVID-19, or The Matrix), and the search engine uses these words to find the relevant information from a database. The search results are usually presented in a list and are commonly called &lt;strong&gt;hits&lt;/strong&gt;, which are displayed to the user.&lt;/p&gt;

&lt;p&gt;Often referred to as a  Web Search engine, Google took "&lt;em&gt;Seek, and you shall find&lt;/em&gt;" to a whole new level. But that's not the only place where they're used. Any domain or industry that relies on the power of computing can benefit immensely from a search engine. In particular, the ability to find documents or information within a system is critical to creating a great user experience for users. Hence, search engines are used in many customer-facing applications across web, desktop and mobile devices.&lt;/p&gt;

&lt;p&gt;For instance, in e-commerce platforms such as Amazon, search is used to find products across multiple categories such as clothing, electronics and furniture. Search is also used in blogs and news publication platforms to find articles or sources of information for research. And in entertainment platforms like Netflix, search allows you to find content that you like from a massive catalogue of movies, TV shows and music.&lt;/p&gt;

&lt;p&gt;Let's look at a popular open-source software that powers the search feature for some of the world's most widely used enterprise software.&lt;/p&gt;

&lt;h2&gt;
  
  
  Elasticsearch
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.elastic.co/elasticsearch/" rel="noopener noreferrer"&gt;Elasticsearch&lt;/a&gt; is a free and open-source search engine allowing you to search for nearly any kind of information - including textual, numerical and geospatial data. It emerged from an earlier open source project called &lt;a href="https://lucene.apache.org/" rel="noopener noreferrer"&gt;Lucene&lt;/a&gt;, primarily to provide the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handle terabytes of data.&lt;/li&gt;
&lt;li&gt;Do large scale parallel searching really fast.&lt;/li&gt;
&lt;li&gt;Use inexpensive commodity hardware to scale and increase search speed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In order to achieve the above, what started out as a search box within applications has become a NoSQL database in its own right. Elasticsearch is popularly used along with the &lt;em&gt;ELK Stack&lt;/em&gt;, or simply the &lt;strong&gt;Elastic Stack&lt;/strong&gt;. It is a collection of products offered by the Elastic company, and consists of the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.elastic.co/elasticsearch/" rel="noopener noreferrer"&gt;Elasticsearch&lt;/a&gt;, which powers the other components as a distributed JSON-based search engine.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.elastic.co/logstash/" rel="noopener noreferrer"&gt;Logstash&lt;/a&gt;: As Elasticsearch started to see widespread usage, users wanted a way to search for logs and compile data for analysis. Logstash is an open server-side data processing pipeline engine which helps with a variety of data analysis and transformation related tasks.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.elastic.co/kibana/" rel="noopener noreferrer"&gt;Kibana&lt;/a&gt;: A visual user-interface that provides an admin dashboard and data visualization tool for managing and debugging data within Elasticsearch. Users can use Kibana to create bar, line and scatter plots, or pie charts and maps on top of large volumes of data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of the most popular use-cases built using the Elastic Stack are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Powering complicated search feature within applications which involve sophisticated querying and filtering.&lt;/li&gt;
&lt;li&gt;Telemetry, logging and metrics.&lt;/li&gt;
&lt;li&gt;Business Analytics.&lt;/li&gt;
&lt;li&gt;Forecasting and anomaly detection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's an excellent &lt;a href="https://www.elastic.co/about/story-of-search/" rel="noopener noreferrer"&gt;visual story&lt;/a&gt; on how search can be leveraged in a myraid of ways.&lt;/p&gt;

&lt;p&gt;Let's examine how the Elastic Stack can be used in more detail by considering a use-case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Search in Retail
&lt;/h2&gt;

&lt;p&gt;In retail platforms, search is a powerful and an integral part of the shopping experience. Here are some interesting statistics:&lt;/p&gt;

&lt;h3&gt;
  
  
  Search Statistics
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A recent &lt;a href="https://www.forrester.com/report/MustHave-eCommerce-Features/RES89561" rel="noopener noreferrer"&gt;report&lt;/a&gt; from Forrester indicates that over &lt;strong&gt;43%&lt;/strong&gt; of users on retail websites &lt;a href="https://www.forrester.com/report/MustHave-eCommerce-Features/RES89561" rel="noopener noreferrer"&gt;go directly to the search bar&lt;/a&gt; to shop for products.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://econsultancy.com/is-site-search-less-important-for-niche-retailers/" rel="noopener noreferrer"&gt;Screen Pages&lt;/a&gt; analyzed search usage across 21 e-commerce websites and found that the average revenue from visitors using search were significantly higher than those by non-search users.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.statista.com/statistics/277125/share-of-website-traffic-coming-from-mobile-devices/" rel="noopener noreferrer"&gt;More than half&lt;/a&gt; of all internet traffic comes from native mobile applications, meaning the search experience plays a crucial role in helping users find what they're looking for without exiting the application. This is especially the case when mobile users spend nearly &lt;strong&gt;20x&lt;/strong&gt; more time shopping than website users!&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Elasticstack in Retail
&lt;/h3&gt;

&lt;p&gt;If you're reading this, you're probably wondering whether your app or business can benefit from Elasticsearch. The Elastic Stack is used by some of the world's top e-commerce and retail business in-order to enhance the shopping experience for customers. Here are a few examples:&lt;/p&gt;

&lt;p&gt;Ebay leverages Elasticsearch to help users find relevant products over &lt;a href="https://www.elastic.co/videos/ebay-and-elasticsearch-this-is-not-small-data" rel="noopener noreferrer"&gt;&lt;strong&gt;800&lt;/strong&gt; million records&lt;/a&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%2Fav9nv7o9jnrvdk9jbcdi.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%2Fav9nv7o9jnrvdk9jbcdi.png" alt="Screenshot of the search features on Ebay.com" width="800" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But this isn't necessarily limited to only shopping. Shopify uses Elasticsearch to add a powerful &lt;a href="https://www.elastic.co/customers/shopify" rel="noopener noreferrer"&gt;search feature&lt;/a&gt; for the documentation part of their website!&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%2F97qbbzfmfziay8zlpwao.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%2F97qbbzfmfziay8zlpwao.png" alt="Screenshot of Shopify documentation" width="800" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find several more usecases on retail &lt;a href="https://www.elastic.co/customers/success-stories?usecase=All&amp;amp;industry=retail" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;At Egen, we work with some of the world's largest retail brands in-order to modernize their IT architecture and help scale. We've put together a short tutorial to demonstrate and help you understand how you can use the ELK stack for your own retail business or application.&lt;/p&gt;

&lt;p&gt;Let's first explore what a search experience looks like in online shopping.&lt;/p&gt;

&lt;h2&gt;
  
  
  Search Experience in online shopping
&lt;/h2&gt;

&lt;p&gt;Here's how a typical search looks like on Amazon or any other online shopping platform. There are generally 3 main categories of search:&lt;/p&gt;

&lt;h3&gt;
  
  
  Text Search
&lt;/h3&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%2F4bm2yj0zdxehkgez12qz.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%2F4bm2yj0zdxehkgez12qz.png" alt="Screenshot of a text search for smartphones on Amazon" width="800" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The home page of Amazon presents me with a search input box. If I want to check out smartphones, I could simply enter "smartphone" in the search box.&lt;/p&gt;

&lt;p&gt;This search functionality allows a search across multiple departments or categories.&lt;/p&gt;

&lt;h3&gt;
  
  
  Search within departments or categories
&lt;/h3&gt;

&lt;p&gt;Alternatively, users can also search for products within a certain department.&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%2Fevsekxukmndwghvieqvm.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%2Fevsekxukmndwghvieqvm.png" alt="Screenshot of a search across departments on Amazon" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Filtering
&lt;/h3&gt;

&lt;p&gt;Users also have the option to apply a variety of filters to include and exclude products based on multiple criteria.&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%2F5op3a0t72efxzk7lmgy2.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%2F5op3a0t72efxzk7lmgy2.png" alt="Search filters on Amazon.com" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are some common filter types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Yes/no or boolean type filters. For example, fetch products which have single day deliveries.&lt;/li&gt;
&lt;li&gt;Multi select filters which include products belonging to certain brands.&lt;/li&gt;
&lt;li&gt;Range filters which include searching for products within a certain price range, or above a certain customer rating score.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Sort search results
&lt;/h3&gt;

&lt;p&gt;Users can also decide how they'd like the search results to be sorted, based on factors such as pricing or average reviews:&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%2Fdipbyayfmm2rbibk7tqu.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%2Fdipbyayfmm2rbibk7tqu.png" alt="Sorting search results on Amazon based on various criteria" width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Let's look at how Elasticsearch can help you provide similar such search experience for your retail application.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you will learn
&lt;/h2&gt;

&lt;p&gt;After much tinkering and exploration, I managed to build this sweet e-commerce website, with search and filtering powered by Elasticsearch:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/booleanhunter-tech-blog" rel="noopener noreferrer"&gt;
        booleanhunter-tech-blog
      &lt;/a&gt; / &lt;a href="https://github.com/booleanhunter-tech-blog/e3-retail" rel="noopener noreferrer"&gt;
        e3-retail
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An E-commerce retail website demonstrating Elasticsearch
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/public/images/index.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fpublic%2Fimages%2Findex.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;E³Retail&lt;/h2&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;An E-commerce products-list website that demonstrates the application of Elasticsearch in retail &amp;amp; online shopping.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;App Screenshots&lt;/h3&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;Home Page&lt;/h4&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/public/images/e3-retail_home.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fpublic%2Fimages%2Fe3-retail_home.png" alt="Screenshot of home page"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;Products filter page&lt;/h4&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/public/images/e3-retail_search.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fpublic%2Fimages%2Fe3-retail_search.png" alt="Screenshot of products filter page"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Features&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Responsive on web and mobile.&lt;/li&gt;
&lt;li&gt;Built using &lt;a href="https://en.wikipedia.org/wiki/Progressive_enhancement" rel="nofollow noopener noreferrer"&gt;Progressive enhancement&lt;/a&gt; philosophy.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;Search features&lt;/h4&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Search for a product by typing in the search input.&lt;/li&gt;
&lt;li&gt;Search within a category.&lt;/li&gt;
&lt;li&gt;Search across multiple categories.&lt;/li&gt;
&lt;li&gt;Filter by brand, pricing and ratings.&lt;/li&gt;
&lt;li&gt;Sort by relevance, ratings or price.&lt;/li&gt;
&lt;li&gt;Paginate through search results.&lt;/li&gt;
&lt;li&gt;Search input autocomplete suggestions (requires JavaScript to be enabled).&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Requirements&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;A laptop or a desktop computer.&lt;/li&gt;
&lt;li&gt;Chrome browser.&lt;/li&gt;
&lt;li&gt;Internet connectivity.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Instructions to install&lt;/h3&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Requires &lt;strong&gt;&lt;a href="https://aws.amazon.com/opensearch-service/" rel="nofollow noopener noreferrer"&gt;AWS OpenSearch&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Initialize environment variables. If you're running the app locally, create a &lt;code&gt;.env&lt;/code&gt; file:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;    NODE_ENV=development
    ELASTICSEARCH_HOST=your-elasticsearch-domain
    ELASTICSEARCH_PORT=443
    ELASTICSEARCH_USER=your-elasticsearch-master-user
    ELASTICSEARCH_PASSWORD=your-elasticsearch-master-password
    ELASTICSEARCH_INDEX=index-to-search-on&lt;/pre&gt;

&lt;/div&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Install &lt;a href="https://nodejs.org/" rel="nofollow noopener noreferrer"&gt;Node.js&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Clone or fork this repo&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;npm install&lt;/code&gt; in your project root&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;npm start&lt;/code&gt; to start the app server&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Common commands&lt;/h3&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;Creating an index&lt;/h4&gt;

&lt;/div&gt;
&lt;p&gt;&lt;code&gt;npm run create-index your-index-name&lt;/code&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;Bulk uploading Data&lt;/h4&gt;

&lt;/div&gt;
&lt;p&gt;Syntax…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/booleanhunter-tech-blog/e3-retail" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Wanna know how it works? Watch the demo:&lt;/p&gt;

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

&lt;p&gt;You’ll learn the features of Elasticsearch used to create the above website and similar such search experiences!&lt;/p&gt;




&lt;p&gt;For the purposes of this tutorial, I will be using a dataset from Kaggle, that contains pre-crawled information of products from &lt;a href="https://flipkart.com" rel="noopener noreferrer"&gt;Flipkart&lt;/a&gt;, a leading Indian eCommerce platform similar to &lt;a href="https://amazon.com" rel="noopener noreferrer"&gt;Amazon&lt;/a&gt;. You'll learn how to leverage Elasticsearch's powerful APIs in online shopping to search for relevant products that you can purchase from an e-commerce platform.&lt;/p&gt;

&lt;p&gt;In-order to use the Elastic Stack, you have the following options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.elastic.co/start" rel="noopener noreferrer"&gt;Install&lt;/a&gt; Elasticsearch, LogStash and Kibana on your own hardware or cloud service provider.&lt;/li&gt;
&lt;li&gt;Use the &lt;a href="https://www.elastic.co/cloud/" rel="noopener noreferrer"&gt;managed Elasticsearch cloud&lt;/a&gt; offering from the company Elastic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you've installed, open up the Kibana console, and import your dataset:&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%2F44x4032hh8x89zozqb55.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%2F44x4032hh8x89zozqb55.png" alt="Screenshot: Uploading data on Kibana" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Exploration
&lt;/h2&gt;

&lt;p&gt;When you begin the process of uploading your data, Kibana scans through the first 1000 columns or documents and returns a summary of the file contents in the visual data explorer.&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%2Fr3tugkgerrsgdu3d6dti.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%2Fr3tugkgerrsgdu3d6dti.png" alt="Screenshot: Kibana showing file contents before importing data" width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The explorer shows you the fields in your dataset, the number of distinct values in them, along with the distribution of those values.&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%2Fgd25aj6oje7yv3f3ajao.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%2Fgd25aj6oje7yv3f3ajao.png" alt="Screenshot: Kibana showing column details while importing data" width="800" height="548"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you proceed with importing your data, Kibana will ask you to create an index.&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%2Fy8ul936tg8ylnzo56db6.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%2Fy8ul936tg8ylnzo56db6.png" alt="Screenshot: Creating index on Kibana" width="800" height="253"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you go with the simple import, kibana automatically assigns a default data-type to the individual fields in your dataset.&lt;/p&gt;

&lt;p&gt;But I personally find the advanced import to be more useful, as it allows me to view and choose the most appropriate data-type for the fields in my data.&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%2Fdt2bbi5cyts5yiiqjlez.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%2Fdt2bbi5cyts5yiiqjlez.png" alt="Screenshot: Advanced option for indexes in Kibana" width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Data types and field mappings
&lt;/h3&gt;

&lt;p&gt;Elasticsearch provides common data-types like &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/text.html" rel="noopener noreferrer"&gt;text&lt;/a&gt;, &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/boolean.html" rel="noopener noreferrer"&gt;boolean&lt;/a&gt;, &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/keyword.html" rel="noopener noreferrer"&gt;keyword&lt;/a&gt; and &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/number.html" rel="noopener noreferrer"&gt;numbers&lt;/a&gt;, and also some advanced data types like &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/object.html" rel="noopener noreferrer"&gt;object&lt;/a&gt;, &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/range.html" rel="noopener noreferrer"&gt;range&lt;/a&gt;, &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/date.html" rel="noopener noreferrer"&gt;date&lt;/a&gt; and geo-spatial. Here's the &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html" rel="noopener noreferrer"&gt;complete list&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the advanced import section, you'll be able to review the data-type mappings for the fields/columns in your dataset and modify them if necessary.&lt;/p&gt;

&lt;p&gt;Here are the types Kibana assigned to my dataset fields by default:&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%2Fs6kn7tr9gkfr2gw85zrw.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%2Fs6kn7tr9gkfr2gw85zrw.png" alt="Screenshot: Default types assigned by Kibana while importing data" width="385" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To understand what data-type fits best, let's study our fields in more detail:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;product_name&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The name of the product. This is of the &lt;strong&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/text.html#text-field-type" rel="noopener noreferrer"&gt;text&lt;/a&gt;&lt;/strong&gt; type.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;description&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As the name implies, this contains the description of a product. This too, is of the &lt;strong&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/text.html#text-field-type" rel="noopener noreferrer"&gt;text&lt;/a&gt;&lt;/strong&gt; type.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;category&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The department or category that a product belongs to. When I uploaded my dataset into Kibana first and checked the mappings, I found that Kibana had assigned it a text type. But it is better to assign it as a &lt;strong&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/keyword.html" rel="noopener noreferrer"&gt;keyword&lt;/a&gt;&lt;/strong&gt; type instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is this important?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Elasticsearch optimizes certain fields to perform well for certain kinds of queries.&lt;/p&gt;

&lt;p&gt;In our dataset, the category field is useful when its made into a keyword rather than ordinary text. The reason being - shoppers often look for products within a certain category, and these categories always contain the same value.&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%2Fh9j91g8aswrae238r0n3.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%2Fh9j91g8aswrae238r0n3.png" alt="Screenshot: Shopping by departments on Amazon" width="800" height="367"&gt;&lt;/a&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%2F2u35taoe11w5yqbrnsak.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%2F2u35taoe11w5yqbrnsak.png" alt="Screenshot: Shopping by departments on Flipkart" width="800" height="73"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keyword types are also useful in aggregation queries - for instance, how many products are in each product category?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;brand&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The brand name of the product. By default, this was assigned a keyword type.&lt;/p&gt;

&lt;p&gt;In e-commerce platforms, shoppers often use filters while searching for a product across different brands - for example, &lt;em&gt;Adidas&lt;/em&gt;. In which case, a keyword type fits well.&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%2Fxoa9al1kgmoz85al0q3j.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%2Fxoa9al1kgmoz85al0q3j.png" alt="Screenshot: Filtering by brand on Amazon" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the other hand, shoppers also often search for specific brands by directly entering the brand name on the search bar, like &lt;em&gt;"Adidas shoes"&lt;/em&gt;. In which case, a text field fits well.&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%2Fy2m0dlzi38c62ig2a1ta.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%2Fy2m0dlzi38c62ig2a1ta.png" alt="Screenshot: Search for a specific brand on Amazon's search" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And so for such use-cases, Elasticsearch also allows you to assign &lt;strong&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/multi-fields.html" rel="noopener noreferrer"&gt;multiple types&lt;/a&gt;&lt;/strong&gt; to a field. Here's how you do it in the Kibana console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"brand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"keyword"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"raw"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="w"&gt;      
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;has_next_day_delivery&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a &lt;strong&gt;boolean&lt;/strong&gt;-type field that tells whether the product can be delivered within the next day.&lt;/p&gt;

&lt;p&gt;Elasticsearch optimizes numeric fields, such as integer or float, for range queries.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;retail_price&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kibana erroneously assigned this a &lt;em&gt;keyword&lt;/em&gt; type, which I changed to &lt;strong&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/number.html" rel="noopener noreferrer"&gt;float&lt;/a&gt;&lt;/strong&gt;. This will be useful when a user wants to search for products within a certain price range.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;product_rating&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A number between 0 to 5, where 5 indicates the highest rating on average given by customers, and 1 being the lowest. 0 indicates that the product hasn't been rated yet. I changed the type of this field to &lt;strong&gt;half_float&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;Once you've imported your dataset, you can look at the mappings by going to the Index Management section in Kibana.&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%2Fy7lmu1zr57vfp643zwmn.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%2Fy7lmu1zr57vfp643zwmn.png" alt="Screenshot: Index management in Kibana" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the JSON of the mappings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mappings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"_doc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"_meta"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"created_by"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"file-data-visualizer"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"brand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"keyword"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"raw"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"keyword"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"discounted_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"float"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"has_next_day_delivery"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"boolean"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"long"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"keyword"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"overall_rating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"half_float"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"pid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"keyword"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"product_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"product_rating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"half_float"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"product_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"keyword"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"retail_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"float"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"uniq_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"keyword"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Kibana Developer Console
&lt;/h3&gt;

&lt;p&gt;The Kibana Devtools has a console that allows you to write and test different kinds of queries on your dataset.&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%2Fkddnbn3e7oh7jkiqg0hm.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%2Fkddnbn3e7oh7jkiqg0hm.png" alt="Screenshot: Devtools console on Kibana" width="800" height="184"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's try out one.&lt;/p&gt;

&lt;h4&gt;
  
  
  Get information about documents in an index
&lt;/h4&gt;

&lt;p&gt;Type &lt;code&gt;GET products/_search&lt;/code&gt; on the console, and hit the run icon on the right to execute the query. This tells Elasticsearch to fetch some sample documents from the &lt;code&gt;products&lt;/code&gt; index.&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%2F2zsaks2jsz962lo5ydx5.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%2F2zsaks2jsz962lo5ydx5.png" alt="Screenshot: A simple search query on Kibana Devtools console" width="800" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, Elasticsearch returns a JSON response with a sample list of documents from that index, along with plenty of useful metadata as well. Here's a quick breakdown of what each of those fields mean:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;hits&lt;/code&gt; contains some metadata along with the actual hits found in your dataset.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;total&lt;/code&gt;: Indicates how many number of actual documents were identified as hits. By default, Elasticsearch returns 10000 in the response. So if your dataset contains more than 10000 documents, this field would still indicate 10000.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;relation&lt;/code&gt;: This indicates whether the actual number of hits are equal to, less than, or greater than the total hits returned in the response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By default, this query returns an array of &lt;strong&gt;10&lt;/strong&gt; hits. Within each hit, you'll find some additional metadata:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;_score&lt;/code&gt;: This is the value assigned by Elasticsearch's ranking algorithm to determine how relevant the document is. Since this API returns only a sample, all documents contain the same value of 1.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_index&lt;/code&gt;: Indicates the field that the document was retrieved from.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This an example of a basic search query using Kibana. Let's try another one.&lt;/p&gt;

&lt;h4&gt;
  
  
  Find the number of products in each category
&lt;/h4&gt;

&lt;p&gt;On the console, type this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;products/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"aggs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"products"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"terms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&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%2Fdc7mwj92it7ki0kbe7jp.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%2Fdc7mwj92it7ki0kbe7jp.png" alt="Screenshot: A simple aggregation query in Kibana" width="800" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It'll return the following response:&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%2F8r05jjw48tm88k9qkptn.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%2F8r05jjw48tm88k9qkptn.png" alt="Screenshot: Aggregate by categories query results in Kibana" width="800" height="708"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Common terminologies
&lt;/h3&gt;

&lt;p&gt;If you're just getting acquainted with search engines, you will frequently come across terms such as ranking and score. These terms are often inter-related to one another. It's useful to understand what they mean and stand for before you begin to dive into querying in Elasticsearch.&lt;/p&gt;

&lt;h4&gt;
  
  
  Ranking
&lt;/h4&gt;

&lt;p&gt;Ranking determines the order in which search results are returned, by assigning each hit a relevance score. Results are then ordered in decreasing order of their scores - with the highest scores at the top of the list, and the least scored items at the bottom of the list.&lt;/p&gt;

&lt;h4&gt;
  
  
  Score
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html#relevance-scores" rel="noopener noreferrer"&gt;Score&lt;/a&gt; is a value that implies how relevant the document is to the user. Higher the score, more relevant is the item, and vice-versa. A variety of factors are involved in determining the score of a document, such as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Term frequency&lt;/strong&gt;: How many times the search term, or a subset of the search term, appears in the document.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inverse Term frequency&lt;/strong&gt;: Often in the query, there might be words or phrases that occur too often in several documents, such as "How to", or "the". When such terms are encountered within a document, inverse term frequency lowers the weight for that document, and increases the weight for terms that occur rarely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Searching for information
&lt;/h2&gt;

&lt;p&gt;Elasticsearch exposes a set of REST APIs to allow users to query for information. A search query is given in the form of a &lt;code&gt;GET&lt;/code&gt; request, after which Elasticsearch processes it and returns the results as a JSON response.&lt;/p&gt;

&lt;p&gt;There are two main ways to search in Elasticsearch:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-your-data.html" rel="noopener noreferrer"&gt;Queries&lt;/a&gt; retrieve documents that match the specified criteria.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html" rel="noopener noreferrer"&gt;Aggregations&lt;/a&gt; present the summary of your data as metrics, statistics, and other analytics.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Simple Text Queries
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Query #1: Text search for a product
&lt;/h4&gt;

&lt;p&gt;This is done using a &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html" rel="noopener noreferrer"&gt;match&lt;/a&gt; query. Here's the syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name-of-index/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Column/field within which to search"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Search keywords"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

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

&lt;/div&gt;



&lt;p&gt;Here's a query that searches for shoes in the &lt;code&gt;product_name&lt;/code&gt; field and returns upto a 100 products:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;products/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"product_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shoes"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

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

&lt;/div&gt;



&lt;p&gt;Here's the response:&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%2Fewnx0vaj6e4if21yl4ks.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%2Fewnx0vaj6e4if21yl4ks.png" alt="Screenshot: Search query results in Kibana" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's a sample structure of the JSON response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"hits"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"_score"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;7.9427953&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"_source"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"product_name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"WorldWearFootwear Running Shoes, Walking Shoes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"pid"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SHOEG7FXJFJDFPBD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"retail_price"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"999.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"discounted_price"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"380.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"image"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"http://img5a.flixcart.com/image/shoe/s/3/p/white-ast-212-worldwearfootwear-8-original-imaeg7fsg67hxgss.jpeg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"has_next_day_delivery"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"false"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Key Features of WorldWearFootwear Running Shoes, Walking Shoes Material: Mesh Occasion: Sports Color: Multicolor Heel Height: 0,Specifications of WorldWearFootwear Running Shoes, Walking Shoes General Ideal For Men, Boys Occasion Sports Shoe Details Weight 200 g (per single Shoe) - Weight of the product may vary depending on size. Heel Height 0 inch Outer Material Mesh Color WHITE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"product_rating"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"overall_rating"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"brand"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"WorldWearFootwear"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Footwear"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"_index"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"products"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"_type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"_doc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"_id"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xg5Fw30BkBYPif_fIlZO"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"_score"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;7.9427953&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"_source"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"529"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"uniq_id"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"b168c7b936c3ad9d9a02f1bfafa4926d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"product_url"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://www.flipkart.com/steppings-running-shoes-casuals/p/itmebg6znr8nbwth?pid=SHOEBG6ZUGTSDGBU"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"product_name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Steppings Running Shoes Casuals Shoes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"pid"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SHOEBG6ZUGTSDGBU"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"retail_price"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3699.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"discounted_price"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2589.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"image"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"http://img6a.flixcart.com/image/shoe/k/t/6/black-8120-2-steppings-39-1000x1000-imaebg2fcsnjff8h.jpeg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"has_next_day_delivery"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"false"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Adidas Running Shoes Casuals Shoes. Price: Rs. 2,589.    .   Feel on the top of the world with this pair of Shoes by Adidas. Let the sun go down for uncomfortable sandals, opt for this pair of Flats that is crafted using a comfortable sole. Team this pair with a midi skirt and a floral crop top for an ultimate look..Feel on the top of the world with this pair of Shoes by Adidas. Let the sun go down for uncomfortable sandals, opt for this pair of Flats that is crafted using a comfortable sole. Team this pair with a midi skirt and a floral crop top for an ultimate look."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"product_rating"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"overall_rating"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"brand"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Adidas"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Footwear"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But sometimes, the word &lt;em&gt;shoes&lt;/em&gt; may not always appear in the product's name. Often, sellers may name them &lt;em&gt;sneakers&lt;/em&gt; or something in the name, but will put in the word shoes in the product's description.&lt;/p&gt;

&lt;p&gt;In such a case, you'll need to search across multiple columns/fields. That's where the &lt;strong&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html" rel="noopener noreferrer"&gt;multi-match query&lt;/a&gt;&lt;/strong&gt; can help.&lt;/p&gt;

&lt;p&gt;Here's the syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;products/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"multi_match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Search keywords"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"field1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"field2"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So here's how you can retrieve products containing the word &lt;em&gt;shoes&lt;/em&gt; in both the name and the description:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;products/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"multi_match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shoes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"product_name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Query #2: Text search for a product with a brand name or a certain phrase
&lt;/h4&gt;

&lt;p&gt;Sometimes, a product name contains 2 or more words, in which case the order of the words that you type become important. Here are a few examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Microsoft Surface&lt;/li&gt;
&lt;li&gt;United Colors of Benetton&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a search phrase containing 2 or more words need to appear in the same order, and they must appear next to each other, a &lt;strong&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query-phrase.html" rel="noopener noreferrer"&gt;Match phrase&lt;/a&gt;&lt;/strong&gt; query is useful.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name-of-index/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"match_phrase"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Column/field within which to search"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Search phrase"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a query that searches for &lt;em&gt;Kindle Paperwhite&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;products/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"match_phrase"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"product_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Kindle Paperwhite"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this need not be limited to just the &lt;code&gt;product_name&lt;/code&gt; field. Here's the syntax for searching for a phrase across multiple fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name-of-index/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"multi_match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Search phrase"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"field_1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"field_2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"field_3"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"phrase"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;products/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"multi_match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Kindle Paperwhite"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"product_name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"phrase"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Query #3: Search for a particular category of products
&lt;/h4&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%2Fagl8rci5wzvp8tiubqzp.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%2Fagl8rci5wzvp8tiubqzp.png" alt="Screenshot: Search by department on Flipkart" width="800" height="73"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Often, shoppers will click on a particular category on the home page to search for products and browse through them, as opposed to typing a particular search phrase. This can be done using a &lt;strong&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/6.8/query-dsl-term-query.html" rel="noopener noreferrer"&gt;term&lt;/a&gt;&lt;/strong&gt; query. Here's the syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name-of-index/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name-of-field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"keyword"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our dataset, the &lt;code&gt;category&lt;/code&gt; field is of the &lt;em&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/keyword.html" rel="noopener noreferrer"&gt;keyword&lt;/a&gt;&lt;/em&gt; type, which makes it possible for us to use a term query.&lt;/p&gt;

&lt;p&gt;So in the above picture, when a user clicks on the Grocery category, the following query can be used to fetch and display grocery items:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;products/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"term"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Grocery"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; 
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Compound Queries
&lt;/h3&gt;

&lt;p&gt;Compound queries allow you to combine 2 or more queries into more sophisticated ones. For instance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a text search to look up products, but only within a certain category or categories.&lt;/li&gt;
&lt;li&gt;Look up a product within a certain price range, or above a certain rating.&lt;/li&gt;
&lt;li&gt;Or all of the above!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this, a &lt;strong&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/6.8/query-dsl-bool-query.html" rel="noopener noreferrer"&gt;bool&lt;/a&gt;&lt;/strong&gt; query comes in handy. It allows you to combine multiple queries into a single one wih the help of boolean clauses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;must&lt;/code&gt;: All queries in the must clause &lt;strong&gt;must be satisfied&lt;/strong&gt; for a document to be returned as a hit.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;should&lt;/code&gt;: The &lt;code&gt;should&lt;/code&gt; allows you to specify &lt;strong&gt;nice-to-haves&lt;/strong&gt;. In other words, it will still return documents that don't match those nice-to-have or &lt;strong&gt;desirable&lt;/strong&gt; conditions, but if they do, they will be ranked above than the ones that don't match.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;must_not&lt;/code&gt;: You can tell elasticsearch to &lt;strong&gt;not include&lt;/strong&gt; certain products if they match some conditions.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;filter&lt;/code&gt;: This behaves exactly like the &lt;code&gt;must&lt;/code&gt; query, except that the score of the matching documents isn't computed. When the score doesn't matter and all you care about is whether documents match the filter criteria, it is recommended to use this query. Elasticsearch &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html" rel="noopener noreferrer"&gt;automatically caches&lt;/a&gt; frequently used filter queries to give them a performance boost.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Let's look at how we can use compound queries to map out a user's search journey.&lt;/p&gt;

&lt;p&gt;Here's a user searching for the smartphone "Redmi Note 10" in the Smartphones category on Amazon:&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%2Frizdjqb2fg2pvbfeod6z.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%2Frizdjqb2fg2pvbfeod6z.png" alt="Screenshot: Shopping for smartphones on Amazon" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Query #4: Text search for products within a certain category
&lt;/h4&gt;

&lt;p&gt;In-order to make search results relevant, shoppers often search for products within a certain department.&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%2Fdvul1xlyhm43cr6nrhn7.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%2Fdvul1xlyhm43cr6nrhn7.png" alt="Screenshot: Searching across departments on Amazon" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As an example, the following query can be used to search for "Redmi Note 10" in the "Smartphones" category:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;products/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"must"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"match_phrase"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"product_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Redmi Note 10"&lt;/span&gt;&lt;span class="w"&gt; 
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Smartphones"&lt;/span&gt;&lt;span class="w"&gt; 
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the general syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name-of-index/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"must"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"match/match_phrase/term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name-of-field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value"&lt;/span&gt;&lt;span class="w"&gt; 
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"match/match_phrase/term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name-of-field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value"&lt;/span&gt;&lt;span class="w"&gt; 
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Query #5: Search and filter for products by ratings
&lt;/h4&gt;

&lt;p&gt;For this, we can use the &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-range-query.html" rel="noopener noreferrer"&gt;range&lt;/a&gt; query. Here's the general syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name-of-index/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"must"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"match/match_phrase/term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enter the value you are looking for"&lt;/span&gt;&lt;span class="w"&gt; 
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"range"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"field-name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"gte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enter lowest value of the range here"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"lte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enter highest value of the range here"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's how we apply it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;products/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"must"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"match_phrase"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"product_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Redmi Note 10"&lt;/span&gt;&lt;span class="w"&gt; 
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Smartphones"&lt;/span&gt;&lt;span class="w"&gt; 
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"range"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"product_rating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"gte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Query #6: Filter by pricing parameters
&lt;/h4&gt;

&lt;p&gt;Similarly, applying a price range works the same way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;products/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"must"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"match_phrase"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"product_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Redmi Note 10"&lt;/span&gt;&lt;span class="w"&gt; 
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Smartphones"&lt;/span&gt;&lt;span class="w"&gt; 
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"range"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"product_rating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"gte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"range"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"retail_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"gte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"lte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;450&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sorting search results
&lt;/h3&gt;

&lt;p&gt;By default, Elasticsearch returns a list of documents sorted by most relevant to least relevant. In online shopping however, shoppers like to view search results sorted by pricing, or customer ratings as well.&lt;/p&gt;

&lt;p&gt;Here's the general syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;products/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sort"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"field-name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"order"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"specify-a-sort-order"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's how we can apply it in the previous query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;products/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sort"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"retail_price"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"order"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"asc"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"must"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"match_phrase"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"product_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Redmi Note 10"&lt;/span&gt;&lt;span class="w"&gt; 
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Smartphones"&lt;/span&gt;&lt;span class="w"&gt; 
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"range"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"product_rating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"gte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"range"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"retail_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"gte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"lte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;450&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Improving search relevance
&lt;/h2&gt;

&lt;p&gt;When you search for something, you want to see the most relevant search results on the top. It can be frustrating when the search results are not exactly what you were looking for.&lt;/p&gt;

&lt;p&gt;Search engines like the one that you see on Google's home page determine the relevance of your query using a variety of criteria - such as the text input, geolocation, accuracy of your spelling, and even the intent by analyzing your browsing patterns using machine learning techniques. In-fact, they can be so sophisticated that they fall in a category of their own - &lt;strong&gt;recommendation engines&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Search relevance is the measure of accuracy of the relationship between the search query and the search results. Which raises the question -&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do we measure the relevance of the results returned from a search query?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To understand this better, let's look at the characteristics of the data that may or may not be returned by the search engine.&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%2Fh438uyrr60ucp95ahbdi.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%2Fh438uyrr60ucp95ahbdi.png" alt="Illustration showing the difference between Sensitivity and specificity" width="563" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the above diagram, the dots represent the entire dataset stored in our database. When we search for something using a search engine, the dots within the circle indicate the document that is selected by the search engine as being relevant, also called &lt;strong&gt;hits&lt;/strong&gt;. A ranking algorithm assigns each of these hits a &lt;strong&gt;score&lt;/strong&gt;, after which they are returned back to the user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;True positives&lt;/strong&gt;: These are data that are truly relevant to the user, and are also identified by the search engine as relevant and returned to the user as the response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;False positives&lt;/strong&gt;: These are data that are not relevant to the search query, but are still marked by the search engine as being relevant and returned back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;True negatives&lt;/strong&gt;: These are documents that are not relevant, and are correctly omitted by the search engine amongst the returned results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;False negatives&lt;/strong&gt;: These are documents that are in-fact relevant to the search query, but were not identified by the search engine. Therefore they were not returned back in the search results, when they should have been returned.&lt;/p&gt;

&lt;p&gt;Broadly speaking, there are two factors - precision and recall - that are used to measure the relevancy of search results.&lt;/p&gt;

&lt;h3&gt;
  
  
  Precision
&lt;/h3&gt;

&lt;p&gt;Precision consists of the dots within the circle in the above diagram. It tells us what proportion of results that were identified as relevant by the search engine truly &lt;em&gt;are relevant&lt;/em&gt;. It is a ratio of true positives (documents identified as relevant, and which are actually relevant) to all positives (all documents identified as relevant, irrespective of whether that was correct). In other words, it is the ratio of True Positives/(True Positives + False Positives).&lt;/p&gt;

&lt;h3&gt;
  
  
  Recall
&lt;/h3&gt;

&lt;p&gt;Recall or &lt;strong&gt;sensitivity&lt;/strong&gt;, is another metric that's about the dots within the left part of the above diagram. It tells us what proportion of documents that actually are relevant to our search query, were &lt;em&gt;identified&lt;/em&gt; by the search engine as being relevant. It is a ratio of true positives (documents identified by the search engine as relevant, and which are actually relevant) to &lt;em&gt;all&lt;/em&gt; the data that are relevant (irrespective of whether the search engine identified them correctly). The formula for this metric is given by - True Positives/(True Positives + False Negatives).&lt;/p&gt;




&lt;p&gt;Precision helps you understand what portion of data returned, &lt;em&gt;are actually relevant&lt;/em&gt;&lt;em&gt;. Whereas on the other hand, recall helps you understand what portion of relevant data are *actually being returned&lt;/em&gt;. Together, these two factors determine the relevancy and quality of your search results.&lt;/p&gt;

&lt;p&gt;In Elasticsearch, you can fine-tune your queries to be more precise, or have more recall. Since these two metrics are at odds with each other, fine-tuning for precision would make the search engine err on the side of returning &lt;em&gt;less&lt;/em&gt; documents and focus more on quality, whereas whereas fine-tuning for relevance would make it focus more on quantity.&lt;/p&gt;

&lt;p&gt;Let's look at how we can improve the relevance of our search:&lt;/p&gt;

&lt;h3&gt;
  
  
  Boost the relevance of individual fields
&lt;/h3&gt;

&lt;p&gt;If a search term occurs in specific fields of a document, you can tell Elasticsearch to boost their relevance score higher. Let's see how this can be used within the context of grocery shopping.&lt;/p&gt;

&lt;p&gt;I'm in the mood to have some strawberries. Let's see if I can find any fresh ones on Walmart! 😋&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%2Fc2pttd8cmlq7b0z1w2sv.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%2Fc2pttd8cmlq7b0z1w2sv.png" alt="Screenshot of Walmart showing results for strawberries" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice how the search results show the actual fruits at the top, and shows products like mixed fruits, dried fruit etc towards the bottom?&lt;/p&gt;

&lt;p&gt;When I click on the product titled &lt;em&gt;Mixed fruit&lt;/em&gt;, here's what the description reads:&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%2Fhhom7b5al6icpzr96o9p.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%2Fhhom7b5al6icpzr96o9p.png" alt="Screenshot of Walmart showing product description for mixed fruit" width="650" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It seems like when you search for something like strawberries on Walmart, it searches across both the product name as well as the description fields. But its also clever enough to rank the products that contain the word &lt;em&gt;strawberries&lt;/em&gt; in their &lt;strong&gt;titles&lt;/strong&gt; higher, than the ones that have this word in their &lt;strong&gt;descriptions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Why? Because products mentioning the word "strawberries" in their &lt;strong&gt;titles&lt;/strong&gt; are more likely to be related to my search, than the products that mention "strawberries" in their description.&lt;/p&gt;

&lt;p&gt;Elasticsearch allows you to &lt;strong&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-boost.html" rel="noopener noreferrer"&gt;boost&lt;/a&gt;&lt;/strong&gt; individual fields in-order to improve the &lt;em&gt;precision&lt;/em&gt; of your search results. So if we were to apply boosting on our dataset, here's what it would look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;products/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"multi_match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"strawberries"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"product_name^2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This basically tells Elasticsearch to increase the weight of the &lt;code&gt;product_name&lt;/code&gt; by a factor of 2. Here's the general syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name-of-index/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"multi_match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"search term"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"field1^2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"field2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"field3"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Decrease the relevance of documents for certain search terms
&lt;/h3&gt;

&lt;p&gt;There might be instances when you'd like to decrease the relevance of certain documents if they contain some search terms, without excluding them from the search results. Here's an example:&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%2F6zf2n57cvqcec91qwsqo.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%2F6zf2n57cvqcec91qwsqo.png" alt="Screenshot of Amazon showing results for laptop" width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The top three products are indeed laptops, but the 4th one isn't. Notice how the search lowered the relevance of a related product that contains the word laptop, but isn't actually one? But Amazon very aptly thinks that this is something that I might be interested to buy, so it didn't exclude it from the search results!&lt;/p&gt;

&lt;p&gt;Elasticsearch allows you to apply negative boosting to demote certain documents without excluding them from the search results, with the help of the &lt;strong&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-boosting-query.html" rel="noopener noreferrer"&gt;boosting query&lt;/a&gt;&lt;/strong&gt;. Here's how its done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;products/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"boosting"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"positive"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"laptop"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"negative"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"usb"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"negative_boost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the general syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name-of-index/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"boosting"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"positive"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"search term"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"negative"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"term to demote"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"negative_boost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: Use the &lt;code&gt;should&lt;/code&gt; clause if you want to boost documents that contain certain additional terms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enhancing e-shopping experience even further
&lt;/h2&gt;

&lt;p&gt;Most shoppers prefer using mobile devices. But typing on the search box isn't nearly so easy as it is on a desktop! It take a lot more effort, and you're more prone to making typos. A &lt;a href="https://www.grammarly.com/blog/mobile-communication-study/" rel="noopener noreferrer"&gt;study from Grammarly&lt;/a&gt; shows that mobile users are nearly &lt;strong&gt;5&lt;/strong&gt; times more likely to make mistakes while typing on a phone than on a regular PC!&lt;/p&gt;

&lt;p&gt;Here are some cool features of Elasticsearch that you can leverage to further improve search on mobile:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.16/search-suggesters.html#completion-suggester" rel="noopener noreferrer"&gt;Suggesters&lt;/a&gt;&lt;/strong&gt;: This adds an &lt;strong&gt;autocomplete&lt;/strong&gt; suggestion feature, providing relevant search results as the user keeps typing on their search box!&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%2F5ni56jvlsqamd5dadhl7.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%2F5ni56jvlsqamd5dadhl7.png" alt="Screenshot of Wallmart's website showing a search box with autocomplete suggestions" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/5.1/common-options.html#fuzziness" rel="noopener noreferrer"&gt;Fuzzy queries&lt;/a&gt;&lt;/strong&gt;: Useful for handling typos and to provide &lt;strong&gt;"Did you mean this instead"&lt;/strong&gt; feature. Guessing what the user actually intended to search for rather than returning no results at all is a great way to further enhance the shopping experience.&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%2Fmyazrorxayotxn95bdf5.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%2Fmyazrorxayotxn95bdf5.png" alt="Screenshot of Amazon's website auto-correcting a typo" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summing up
&lt;/h2&gt;

&lt;p&gt;If you have a retail platform, providing a means to search for products quickly and easily can immensely enhance the shopping experience for your users. This translates to more frequent purchases, better customer reviews and increased profits.&lt;/p&gt;

&lt;p&gt;At Egen, we use Elasticsearch along with several other technologies to create captivating customer experiences for our retail customers. Here's a sneak peak into the life of an Elasticsearch Architect in our organization:&lt;/p&gt;

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

&lt;p&gt;Looking to create an amazing user experience for your retail application? Or have any questions related to this post? Do leave your thoughts on the comment section!&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.elastic.co/about/story-of-search/" rel="noopener noreferrer"&gt;The story of search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://egen.solutions/videos/elasticsearch-for-beginners-and-sql-developers/" rel="noopener noreferrer"&gt;Elasticsearch for beginners SQL developers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://egen.solutions/videos/elasticsearch-bottoms-up-2/" rel="noopener noreferrer"&gt;Elasticsearch, bottoms-up - Videos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=gS_nHTWZEJ8" rel="noopener noreferrer"&gt;Beginner's Crash Course to Elastic Stack - Video Series&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/LisaHJung/Beginners-Crash-Course-to-Elastic-Stack-Series-Table-of-Contents" rel="noopener noreferrer"&gt;Beginner's Crash Course to Elastic Stack - Github documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.elastic.co/blog/found-elasticsearch-from-the-bottom-up" rel="noopener noreferrer"&gt;Indexing in Elasticsearch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tutorial</category>
      <category>programming</category>
      <category>elasticsearch</category>
      <category>ux</category>
    </item>
    <item>
      <title>What the heck is a Service Mesh, anyway?</title>
      <dc:creator>Ashwin Hariharan</dc:creator>
      <pubDate>Thu, 23 Mar 2023 10:38:38 +0000</pubDate>
      <link>https://dev.to/egeninc/what-the-heck-is-a-service-mesh-anyway-4aj2</link>
      <guid>https://dev.to/egeninc/what-the-heck-is-a-service-mesh-anyway-4aj2</guid>
      <description>&lt;p&gt;Software applications can be thought as an ever-evolving organism. As the number of active users for your apps increase, at some point you're going to eventually consider the problem of scale.&lt;/p&gt;

&lt;p&gt;One of the solutions that gradually evolved over the years to manage the challenges of scale were &lt;strong&gt;microservices&lt;/strong&gt;. The design principle of a micro-service based architecture is to treat the application as a collection of individual, loosely-coupled services. This offers several advantages over monoliths-faster development, better deployment, fault tolerance, security and more to name a few.&lt;/p&gt;

&lt;p&gt;But designing a microservice-based architecture comes with its own set up challenges. For instance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do you manage and visualize &lt;strong&gt;traffic flows&lt;/strong&gt; between microservices?&lt;/li&gt;
&lt;li&gt;How do you keep the data flow between microservices &lt;strong&gt;secure&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability&lt;/strong&gt; - how do  measure the internal states of a particular service?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring&lt;/strong&gt; and &lt;strong&gt;tracing&lt;/strong&gt; of network requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this, you need something called &lt;strong&gt;Service Mesh&lt;/strong&gt;. But first, let's first look at how a typical cluster is implemented using Kubernetes.&lt;/p&gt;

&lt;h2&gt;
  
  
  A conceptual container orchestration setup
&lt;/h2&gt;

&lt;p&gt;Here is a diagram of a typical cluster in Kubernetes:&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%2Fz0m3flmflrw05dhcbo83.jpg" 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%2Fz0m3flmflrw05dhcbo83.jpg" alt="Diagram showing a typical cluster of microservices" width="741" height="161"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, &lt;em&gt;microservice A&lt;/em&gt; could for instance, use the services of &lt;em&gt;microservice B&lt;/em&gt;, which in-turn could rely on another &lt;em&gt;microservice C&lt;/em&gt;. A typical architecture built using Kubernetes would involve the Pod A making a &lt;strong&gt;network request&lt;/strong&gt; to Pod B, and in-turn would make a request to Pod C.&lt;/p&gt;

&lt;p&gt;A container orchestration tool like Kubernetes helps you manage and handle the pods, but it has little control over the actual data or &lt;strong&gt;network flow&lt;/strong&gt; between pods. What's going on at the &lt;em&gt;network connections&lt;/em&gt; when each individual pod makes a request to another pod?&lt;/p&gt;

&lt;p&gt;In a cluster with dozens or even hundreds of pods, you will need visibility on the &lt;em&gt;interconnections&lt;/em&gt; between the pods themselves. Here is where a service mesh comes into play.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Service Mesh?
&lt;/h2&gt;

&lt;p&gt;A Service Mesh is an extra layer of abstraction that you add to your Cluster of services, in-order to efficiently manage and monitor them.&lt;/p&gt;

&lt;p&gt;For instance, if you're using Kubernetes, you can think of a service mesh as this layer of software that resides &lt;em&gt;underneath&lt;/em&gt; your kubernetes pods. This software takes over control over every network request made between pods in the entire cluster.&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%2Fi2t8v3infdurbgfyp6hs.jpg" 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%2Fi2t8v3infdurbgfyp6hs.jpg" alt="Conceptual diagram of a service mesh" width="741" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Wait, but why though?
&lt;/h3&gt;

&lt;p&gt;The service mesh software intercepts each incoming network request, and re-routes it to the desired container. In-doing so, it can implement some function or some &lt;em&gt;mesh logic&lt;/em&gt;, that uses the network request and do some pretty interesting stuff. For instance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Measure the &lt;strong&gt;time&lt;/strong&gt; taken for the network request to complete.&lt;/li&gt;
&lt;li&gt;Collate &lt;strong&gt;network errors&lt;/strong&gt; that occur in any part of the cluster.&lt;/li&gt;
&lt;li&gt;Implement &lt;strong&gt;security&lt;/strong&gt; - for instance, all network calls must be  encrypted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-route requests&lt;/strong&gt; based on a given  criteria or when certain conditions are met.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visualize&lt;/strong&gt; and manage network traffic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without a service mesh, it would be pretty difficult to implement the above.&lt;/p&gt;

&lt;p&gt;Indeed, the most useful features of a service mesh involves &lt;strong&gt;telemetry&lt;/strong&gt;. Telemetry involves monitoring the health and security of microservices, measuring their performance, and collecting useful statistics such as the memory overhead, CPU utilization and more.&lt;/p&gt;

&lt;p&gt;Another feature of a service mesh is &lt;strong&gt;tracing&lt;/strong&gt;. This would allow you to visualize the network flow and trace the &lt;strong&gt;chain of requests&lt;/strong&gt; within the context of a particular feature or service.The service mesh could  gather such useful information that would enable for faster debugging and bug resolution, or even run a time series analysis to predict future outages of a particular service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Istio
&lt;/h2&gt;

&lt;p&gt;Istio is a Kubernetes native solution, used by several large scale corporations like Google, IBM and Microsoft. It has support for K8s and also virtual machines.&lt;/p&gt;

&lt;p&gt;There are many different ways by which a service mesh could be implemented. Here's how Istio implements 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%2Fp69pj2gik2yl7qydq9fa.jpg" 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%2Fp69pj2gik2yl7qydq9fa.jpg" alt="Service mesh implemented using Istio" width="741" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For each of the pods in the cluster, Istio injects its own container, also known as a &lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Proxy_pattern" rel="noopener noreferrer"&gt;proxy&lt;/a&gt;&lt;/strong&gt;. Each individual proxy takes over and handles all incoming and outgoing network requests for that pod. This proxy contains the mesh logic to further process the network request for tracing, telemetry, and re-direct it to the target container proxy.&lt;/p&gt;

&lt;p&gt;Istio is designed to be loosely coupled. The individual containers have no knowledge about the proxy injected by Istio, and neither do they need to contain any code for traffic management, tracing or other features provided by Istio. The individual pods continue to run and make network calls as though they were directly communicating to other pods.&lt;/p&gt;

&lt;p&gt;This design of service mesh implemented by Istio is also known as the &lt;strong&gt;&lt;a href="https://www.oreilly.com/library/view/designing-distributed-systems/9781491983638/ch02.html" rel="noopener noreferrer"&gt;Sidecar pattern&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other implementations
&lt;/h2&gt;

&lt;p&gt;Similar to Istio, there are other popular implementations as well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://linkerd.io/" rel="noopener noreferrer"&gt;Linkerd&lt;/a&gt;&lt;/strong&gt;: Claims to be the lightest and simpler than any other service mesh implementation. It's K8s only. Has its own proxy written in Rust.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.consul.io/" rel="noopener noreferrer"&gt;Consul&lt;/a&gt;&lt;/strong&gt;: A full-feature service management system. Runs as a Daemon on every K8s node and works with Envoy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's an &lt;a href="https://platform9.com/blog/kubernetes-service-mesh-a-comparison-of-istio-linkerd-and-consul/" rel="noopener noreferrer"&gt;article&lt;/a&gt; that compares these implementations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Istio under the hood
&lt;/h2&gt;

&lt;p&gt;Istio actually assembles together several third party components in-order to construct a service-mesh for your cluster.&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%2Fxac1hgekd1x29p3ab79s.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%2Fxac1hgekd1x29p3ab79s.png" alt="Istio architecture" width="767" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Istio uses &lt;strong&gt;&lt;a href="https://www.envoyproxy.io/" rel="noopener noreferrer"&gt;Envoy&lt;/a&gt;&lt;/strong&gt;, an open-source cloud-native service proxy under the hood for the Proxy containers within your pods. These proxies constitute the data plane layer of Istio.&lt;/p&gt;

&lt;h3&gt;
  
  
  Control plane
&lt;/h3&gt;

&lt;p&gt;The control plane consists of majorly 3 components:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Galley&lt;/strong&gt;: If you're using Kubernetes, Galley reads your Kubernetes YAML file and transforms it into a format that Istio understands. It can also work with other orchestration frameworks such as &lt;a href="http://mesos.apache.org/" rel="noopener noreferrer"&gt;Mesos&lt;/a&gt; and Consul.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pilot&lt;/strong&gt;: This component takes the configurations and then converts them into a format that Envoy understands, in-order to deploy the proxies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Citadel&lt;/strong&gt;: Responsible for certificate management and enabling TLS / SSL across your entire cluster.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Since &lt;a href="https://istio.io/latest/blog/2020/istiod/" rel="noopener noreferrer"&gt;Istio version 1.5&lt;/a&gt;, the above components have been combined into a single binary called &lt;code&gt;istiod&lt;/code&gt;. &lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;At Egen, we've put together a video in-order to understand fully well how Istio works, along with use-cases showing what you can do with it. We also show you a real-world example of a service not working as intended, and how Istio can help you narrow down the problem and apply an appropriate fix.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/IvgE_PZui3M"&gt;
&lt;/iframe&gt;
&lt;br&gt;
&lt;strong&gt;Thanks for reading&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We host several such meetups discussing scalability, distributed systems, application engineering and more. If you're interested in learning these topics, do give us a follow on &lt;a href="http://collective.ac" rel="noopener noreferrer"&gt;collective.ac&lt;/a&gt; to get notified for future events.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://egen.solutions/videos/role-of-service-mesh-in-kubernetes-explained-do-you-need-istio/" rel="noopener noreferrer"&gt;Does your organization need Istio&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=z6WjVJ1XelY" rel="noopener noreferrer"&gt;What is Istio Service Mesh&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=16fgzklcF7Y" rel="noopener noreferrer"&gt;Istio and Service Mesh - simply explained in 15 minutes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>microservices</category>
      <category>kubernetes</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>DevRel job roles - Content Creator or Content Developer?</title>
      <dc:creator>Ashwin Hariharan</dc:creator>
      <pubDate>Sat, 12 Mar 2022 13:37:22 +0000</pubDate>
      <link>https://dev.to/booleanhunter/devrel-job-roles-content-creator-vs-content-45n7</link>
      <guid>https://dev.to/booleanhunter/devrel-job-roles-content-creator-vs-content-45n7</guid>
      <description>&lt;p&gt;&lt;strong&gt;Developer Relations&lt;/strong&gt; is a niche field in software engineering. It is an umbrella term for a lot of activities that are geared towards building relationships with developers.&lt;/p&gt;

&lt;p&gt;I've been in this field for almost 2 years, and one of the most significant activities as part of my work is to &lt;strong&gt;create content&lt;/strong&gt;, mostly in the form of &lt;a href="https://dev.to/egeninc"&gt;technical deep dives&lt;/a&gt;. But to say that I'm a &lt;em&gt;content writer&lt;/em&gt; or &lt;em&gt;technical writer&lt;/em&gt; seems like an understatement, and I feel like I might be under-selling myself.&lt;/p&gt;

&lt;p&gt;There are several more activities that I (or anyone else in this field) could be involved in. Here are some examples (in no particular order):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Before even beginning to create content, one would need to do a lot of &lt;strong&gt;research&lt;/strong&gt;, and planning what they create and how they'll execute it in the first place.&lt;/li&gt;
&lt;li&gt;Sometimes the content might be talking about tech, but isn't necessarily a coding tutorial. For example - if my organization is into Retail, I would need to write articles like &lt;em&gt;"What challenges do online retailers face when trying to scale up?"&lt;/em&gt; That again involves research and acquiring &lt;strong&gt;industry knowledge&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;For the actual content, it's not always text-based. Often someone who's doing the writing also might be involved in creating &lt;strong&gt;infographics&lt;/strong&gt;, and even explore &lt;strong&gt;other formats&lt;/strong&gt; like videos.&lt;/li&gt;
&lt;li&gt;Working with Director of Talent for coming up with creative &lt;strong&gt;job ads&lt;/strong&gt; for hiring developers.&lt;/li&gt;
&lt;li&gt;Working with the Head of Marketing to decide on factors like &lt;strong&gt;SEO&lt;/strong&gt;, and the right platforms to publish the content on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if a person working in DevRel does the above activities and need to describe themselves in short, what should they call themselves? Would &lt;strong&gt;Content Creator&lt;/strong&gt; make more sense, or &lt;strong&gt;Content Developer&lt;/strong&gt;? Or are there are other titles that would work better?&lt;/p&gt;




&lt;p&gt;What do you think? Let's discuss in the comments below - I'd love to know your thoughts! 😄&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>devrel</category>
      <category>career</category>
      <category>watercooler</category>
    </item>
    <item>
      <title>How to build your own Social Audio Chat Application - Part 2</title>
      <dc:creator>Ashwin Hariharan</dc:creator>
      <pubDate>Wed, 23 Feb 2022 11:28:15 +0000</pubDate>
      <link>https://dev.to/egeninc/how-to-build-your-own-social-audio-chat-application-part-2-4jjo</link>
      <guid>https://dev.to/egeninc/how-to-build-your-own-social-audio-chat-application-part-2-4jjo</guid>
      <description>&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What you will learn and build&lt;/li&gt;
&lt;li&gt;Components for enabling real-time communication using audio or video&lt;/li&gt;
&lt;li&gt;How to implement a Signaling Server&lt;/li&gt;
&lt;li&gt;What are web sockets, and how do they work&lt;/li&gt;
&lt;li&gt;Feature #1: Enter a room to participate in ongoing conversations&lt;/li&gt;
&lt;li&gt;Feature #2: Leave a room&lt;/li&gt;
&lt;li&gt;
Next steps

&lt;ul&gt;
&lt;li&gt;Some features that are worth replicating&lt;/li&gt;
&lt;li&gt;Architecture, Scaling and costs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;References and resources&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Welcome to Part 2 of this series on how to build a social-networking app that enables you to have conversations in real-time using &lt;strong&gt;audio&lt;/strong&gt;. In this tutorial, you'll continue to build a social audio application similar to Clubhouse and Spotify Greenrooms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To recap, here's the list of features:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature #1:&lt;/strong&gt; Discover new rooms in the hallway. You already built this in Part 1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature #2:&lt;/strong&gt; Users can enter a room to talk, and have conversations with people with similar passions and interests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature #3:&lt;/strong&gt; A user can opt to leave a room anytime they feel like on the click of a button&lt;/p&gt;

&lt;p&gt;Make sure to read part 1 thoroughly before you proceed!&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/egeninc" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&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%2Forganization%2Fprofile_image%2F4696%2Ff904b21d-9fd4-4740-85aa-f48e9ea1992e.png" alt="Egen" width="800" height="450"&gt;
      &lt;div class="ltag__link__user__pic"&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%2Fuser%2Fprofile_image%2F108200%2F3f49e017-fd5f-4531-87fe-e4bb39f82949.jpg" alt="" width="800" height="1200"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/egeninc/how-to-build-your-own-social-audio-chat-application-4b88" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;How to build your own Social Audio chat application&lt;/h2&gt;
      &lt;h3&gt;Ashwin Hariharan for Egen ・ Feb 23 '22&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#tutorial&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#postgres&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  What you will learn and Build
&lt;/h2&gt;

&lt;p&gt;In this tutorial, you'll learn how to mimic and build the feature that enables users to join a room and interact with each other via audio in real time! Here's a side-by-side comparison of this room feature across different apps — Clubhouse, Spotify Greenrooms and Twitter Spaces:&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%2Fq2vd5ntbve6zbqgl8luy.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%2Fq2vd5ntbve6zbqgl8luy.png" alt="A side-by-side comparison of Clubhouse, Spotify Greenrooms and Twitter Spaces" width="800" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Components for enabling real-time communication using audio or video
&lt;/h2&gt;

&lt;p&gt;For enabling real-time communication, you need a few key components. Here are the most basic components for a &lt;strong&gt;peer-to-peer network architecture&lt;/strong&gt; &lt;sup id="fnref1"&gt;1&lt;/sup&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  A Media Input and Output Device
&lt;/h3&gt;

&lt;p&gt;If you're reading this on a computer or a smartphone, your gadget very likely comes along with a microphone, speakers and a webcam. Most modern browsers also can &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia" rel="noopener noreferrer"&gt;request&lt;/a&gt; the user for permission to access one of these input devices using JavaScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  WebRTC
&lt;/h3&gt;

&lt;p&gt;A technology that is built in open web standards, and is part of most modern browsers. Using &lt;a href="https://webrtc.org/" rel="noopener noreferrer"&gt;WebRTC&lt;/a&gt;, you can transmit audio, video and generic data directly between different clients.&lt;/p&gt;

&lt;p&gt;WebRTC can be a bit complicated to use. So for this tutorial, you'll make use of &lt;a href="http://peerjs.com/" rel="noopener noreferrer"&gt;&lt;em&gt;PeerJS&lt;/em&gt;&lt;/a&gt;, a library that abstracts away all the underlying implementation complexity so that you can focus on building the functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  A signaling server
&lt;/h3&gt;

&lt;p&gt;For WebRTC to work, each client (a web browser, or an Android or an iOS application) will need the &lt;strong&gt;peer information&lt;/strong&gt; &lt;sup id="fnref2"&gt;2&lt;/sup&gt; of every other client in the room or network in-order to establish a &lt;em&gt;webrtc peer connection&lt;/em&gt;. A signaling server's job is to let peers in the room or network find each other by providing information about each peer in the room, so that they can establish a WebRTC connection.&lt;/p&gt;

&lt;p&gt;The WebRTC specification does not provide APIs to implement this signaling mechanism. So it is left up-to us developers to figure out a way to provide information of the peers to every other peer in the network, so that they can communicate with each other. But fret not, there are a lot of techniques you could use!&lt;/p&gt;

&lt;h2&gt;
  
  
  How to implement a Signaling Server
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Technique #1: Using a database that supports real time updates
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://webrtc.org/getting-started/firebase-rtc-codelab" rel="noopener noreferrer"&gt;Codelab&lt;/a&gt; in the official WebRTC website describes how to implement the signaling server using this very technique, with the help of the &lt;a href="https://firebase.google.com/docs/firestore/query-data/listen" rel="noopener noreferrer"&gt;Cloud Firestore&lt;/a&gt; database. Here's a brief description of how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;On the client-side — when you join a room, a new entry is made in the database with your user information such as your username, name, and most importantly — your &lt;strong&gt;peer information&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Other users (clients) within the room are connected to this database, and are &lt;a href="https://firebase.google.com/docs/firestore/query-data/listen" rel="noopener noreferrer"&gt;listening&lt;/a&gt; for changes and updates. When they detect that a new participant (a.k.a you) has been added to the database, they receive your information along with your peer information.&lt;/li&gt;
&lt;li&gt;Now using this peer information, these clients can establish a peer connection with you using WebRTC protocols.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Any database that allows &lt;a href="https://supabase.io/docs/reference/javascript/subscribe" rel="noopener noreferrer"&gt;subscribing&lt;/a&gt; or listening to database changes or updates can be used as a signalling server to transmit the peer information between clients.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technique #2: Using regular HTTP requests
&lt;/h3&gt;

&lt;p&gt;Here's how this technique could work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Upon joining a room, the client makes a POST request to create a new entry in the database with the new particpant's details and peer information.&lt;/li&gt;
&lt;li&gt;Other clients in the room would either:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Use a regular polling technique, where they keep making network requests to an HTTP API endpoint at every given time-interval, to check if any new participant has joined or not. OR&lt;/li&gt;
&lt;li&gt;Use a long-polling technique, where the server keeps the connection alive for as long as it can until it can send some new data to the client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;As you can very well imagine, regular HTTP requests have several drawbacks:&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Drawbacks
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Long-polling can be highly resource intensive especially when there are multiple clients connected with the server.&lt;/li&gt;
&lt;li&gt;Regular polling technique isn't really “real time”. And again, the shorter the intervals, the more resource intensive it can be!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Traditional HTTP requests like GET and POST initiate a new connection request and later close the connection after the server sends back the response. If you were to attempt building a real time app using HTTP, the client would have to initiate requests at regular intervals to check for new information (which may or may not be available). This is because of the fact that the server itself is unable to &lt;strong&gt;push&lt;/strong&gt; information on its own.&lt;/p&gt;

&lt;p&gt;And this is highly inefficient — the client would waste resources in constantly interrupting the server and saying &lt;em&gt;“Hi, I'm XYZ — let's shake hands. Do you have something new for me?”&lt;/em&gt;, and the server will be like — &lt;em&gt;“Hi (shaking hands). No I don't. Good-bye!”&lt;/em&gt; over and over again, which means even the server is wasting resources!&lt;/p&gt;

&lt;p&gt;So moving on to another technique —&lt;/p&gt;

&lt;h3&gt;
  
  
  Technique #3: Using Web sockets
&lt;/h3&gt;

&lt;p&gt;This is perhaps the most common way to implement a signalling server.&lt;/p&gt;

&lt;h4&gt;
  
  
  What are Web Sockets, and how do they work?
&lt;/h4&gt;

&lt;p&gt;Using Sockets, you can send and receive information via &lt;em&gt;events&lt;/em&gt;, or in other words &lt;em&gt;signals&lt;/em&gt;. There can be different types of such signals, and if the parties involved know what kind of signal to ‘listen' to, then there can be an exchange of information.&lt;/p&gt;

&lt;p&gt;Unlike HTTP requests, web-sockets create a persistent connection between a client and the server. So this way the client need not keep asking the server, the server can &lt;strong&gt;push&lt;/strong&gt; information when it needs to. This method is much more efficient for building real time applications.&lt;/p&gt;

&lt;p&gt;For implementing the signalling server, you'll make use of web-sockets with the help of &lt;a href="http://socket.io/" rel="noopener noreferrer"&gt;&lt;em&gt;socket.io&lt;/em&gt;&lt;/a&gt;. Here's how this technique would work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Upon joining a room, the client emits a web-socket event with its peer information and other details. Let's call this event &lt;code&gt;user-joined-room&lt;/code&gt; .&lt;/li&gt;
&lt;li&gt;The web-socket enabled server listens for any &lt;code&gt;user-joined-room&lt;/code&gt; events. When it receives the event, it makes a new entry in the database, and adds the new participant's socket to the room or channel. Next, it broadcasts a web-socket event and sends the new participant's info and peer details to all connected clients within that channel.&lt;/li&gt;
&lt;li&gt;Any clients within the room are also listening for a &lt;code&gt;user-joined-event&lt;/code&gt;. As soon as the server broadcasts the new participant's information from the previous step, they receive the peer details and then can use it to initiate a WebRTC connection.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Feature #1: Enter a room to participate in ongoing conversations
&lt;/h2&gt;

&lt;p&gt;In the last tutorial, you replicated the hallways feature, where you see a list of rooms from the clubs that you follow. Now when a user clicks on a room, they should be able to enter the room, listen to other speakers within that room, and be able to participate in the conversation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step #1: Adding details of a new participant in the database, and sharing their peer information with all clients in the room
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Backend
&lt;/h4&gt;

&lt;p&gt;First, we'll write a function that adds takes in information of the new participant and creates a new entry in the participants table.participant to the Add the following code inside &lt;em&gt;/models/participants.js&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addParticipant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
            INSERT INTO participants (room_id, user_id, role, peer_id)
            VALUES ($1, $2, $3, $4)
            ON CONFLICT (room_id, user_id)
            DO
                UPDATE
                    SET join_date = NOW(), peer_id = $4
                WHERE participants.room_id = $1 AND participants.user_id = $2
            RETURNING *
        `&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;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;room_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;peer_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;res&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;db&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="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;values&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;res&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;  

    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the participant already exists, we just update their join-date field to the current timestamp.&lt;/p&gt;

&lt;p&gt;Next, you'll configure our web server to listen for any &lt;code&gt;user-joined-room&lt;/code&gt; events. When it receives an event:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You add/&lt;a href="https://socket.io/docs/v3/rooms/index.html" rel="noopener noreferrer"&gt;subscribe&lt;/a&gt; the new participant's socket to the room that they wish to join.&lt;/li&gt;
&lt;li&gt;Next, you'll use the above function to add the participant information in the database.&lt;/li&gt;
&lt;li&gt;Then, you emit a &lt;code&gt;user-joined-room&lt;/code&gt; event to the rest of the clients within that room. That way, any clients that are subscribed to this room and listening to this event will receive the participant's details.&lt;/li&gt;
&lt;li&gt;Finally, you'll update the participant that they've joined the room by using a &lt;em&gt;callback&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Open &lt;em&gt;socket-events.js&lt;/em&gt; and write the following logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;socket&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="s1"&gt;user-joined-room&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;eventInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&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;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roomId&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;participant&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addParticipant&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;room_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;eventInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;eventInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SPEAKER&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;peer_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;eventInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;peer_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;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;eventInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;room_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-joined-room&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;participant&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;blockquote&gt;
&lt;p&gt;For keeping things simple, we'll add a new participant as a &lt;strong&gt;speaker&lt;/strong&gt; by default. In the real Clubhouse app however, when a new user joins a room, they are part of the &lt;strong&gt;audience&lt;/strong&gt; and need to request the room moderators in-order to be added to the speaker panel.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Front-end
&lt;/h4&gt;

&lt;p&gt;First, you'll use the &lt;em&gt;peerjs&lt;/em&gt; &lt;a href="https://peerjs.com/docs.html#peer" rel="noopener noreferrer"&gt;constructor&lt;/a&gt; to issue the current client a unique &lt;em&gt;Peer ID&lt;/em&gt;. This ID will be necessary for establishing a webrtc connection with another peer.&lt;/p&gt;

&lt;p&gt;Inside &lt;em&gt;modules/webrtc.js&lt;/em&gt;, add 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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;peer&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;Peer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;host&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;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;port&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;port&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;protocol&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;443&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;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/peerjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;peer&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="s1"&gt;open&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;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="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;My peer ID is: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;updatePeerId&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="c1"&gt;//save this ID&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Our NodeJS web-server is configured to issue a new Peer ID upon calling new Peer&lt;/li&gt;
&lt;li&gt;When a connection to the Peer-server is established, it generates a unique random identifier string. We'll save this ID in a variable &lt;code&gt;PEER_ID&lt;/code&gt;, which we can use later.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Next, you'll write a function &lt;code&gt;joinEvent&lt;/code&gt; that you'll call when a room is selected. Here's how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Emit a socket event &lt;code&gt;user-joined-room&lt;/code&gt;, and send the room-id, the user information, and the &lt;code&gt;PEER_ID&lt;/code&gt; .&lt;/li&gt;
&lt;li&gt;The signalling logic that you wrote earlier will receive this event, and create a new entry in the participants table, and will notify the client once its done.&lt;/li&gt;
&lt;li&gt;Once the client has been notified, you'll make a network request to fetch the room info, and then display it in the UI. The user is now officially in the room!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Add the following code inside &lt;em&gt;/modules/index.js&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;joinRoom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-joined-room&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;roomId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;USER_INFO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;peer_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PEER_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="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="c1"&gt;//callback&lt;/span&gt;
        &lt;span class="nf"&gt;updateParticipationInfo&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;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;PARTICIPATION_INFO&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PARTICIPATION_INFO&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nf"&gt;fetchRoomDetails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomId&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;result&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 room info&lt;/span&gt;
            &lt;span class="nf"&gt;renderRoomDetailsSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ROOM_INFO&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// display room&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;Now, you'll just call the above method when a room card is clicked:&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onRoomSelect&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="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;USER_INFO&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;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;PEER_ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;selectRoomNode&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;currentTarget&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Highlights the selected room&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roomId&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;currentTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;joinRoom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomId&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;ol&gt;
&lt;li&gt;Similarly, other clients connected to this room would also need to know if a new participant has joined. Add the following code:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;socket&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="s1"&gt;user-joined-room&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleNewParticipantJoin&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;handleNewParticipantJoin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;participantInfo&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="s1"&gt;user-joined-room&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;participantInfo&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;participantInfo&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="s1"&gt;AUDIENCE&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;ROOM_INFO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audience&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;participantInfo&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;audienceList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;audience&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;displayParticipant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audienceList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;participantInfo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;ROOM_INFO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;panel&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;participantInfo&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;panelMembersList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;panel-members&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;displayParticipant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;panelMembersList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;participantInfo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Try testing this out!&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open two new browser tabs on Chrome: &lt;a href="http://localhost:3000?username=rick" rel="noopener noreferrer"&gt;http://localhost:3000?username=rick&lt;/a&gt; and &lt;a href="http://localhost:3000?username=elon" rel="noopener noreferrer"&gt;http://localhost:3000?username=elon&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Scroll through the list of rooms, and select one that's common between the two to join.&lt;/li&gt;
&lt;li&gt;You should be able to see the names of the participants appear one-by-one, in the order by which they join.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that you've successfully managed to exchange participants information with each other in a room! Next, you can use this peer information to stream information back-and-forth:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step #2: Connect with the peers in the room, and stream their audio
&lt;/h3&gt;

&lt;p&gt;In the previous step, we merely iterated through the list of speakers in the panel and displayed their names in the UI. Along with displaying their name as a text, we'll now also do the following:&lt;/p&gt;

&lt;p&gt;Basically, any new participant that joins the room will also be the one to initiate the peer connection. So upon joining the room, you'll iterate through the list of participants. If they're not the current client/user:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Initiate a call using the participant's peer-id. If the current user is part of the panel-list, then stream their audio as well&lt;/li&gt;
&lt;li&gt;If the participant is also part of the panel, then add an audio element in the UI, and attach their audio stream to this element so that the current user can hear them speaking.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Add the following code inside &lt;em&gt;modules/helpers.js&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;displayAndConnectWithRoomParticipant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;containerElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;participantInfo&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;participantInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;peer_id&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;participant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;displayParticipant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;containerElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;participantInfo&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// establish peer connection&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;participantInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;USER_INFO&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="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;mediaStream&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;PARTICIPATION_INFO&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="s1"&gt;AUDIENCE&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;mediaStream&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;openUserMedia&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;mediaStream&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;MediaStream&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;participantInfo&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="s1"&gt;AUDIENCE&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;participantAudio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createParticipantAudio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;participantInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;participantAudio&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;call&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nx"&gt;participantInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;peer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;mediaStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="na"&gt;participantInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PARTICIPATION_INFO&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;call&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="s1"&gt;stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;attachAudioStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;participantInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;participantAudio&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="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;call&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nx"&gt;participantInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;peer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;mediaStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="na"&gt;participantInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PARTICIPATION_INFO&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;      
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When the other participants receive the peer connection request, they'll need to accept the call as well.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add the following inside &lt;em&gt;modules/webrtc.js&lt;/em&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="nx"&gt;peer&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="s1"&gt;call&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;call&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;participantUserId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;participantInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_id&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;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`call request from &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;participantUserId&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="c1"&gt;// if call requester part of speaker panel,stream their audio&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;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;participantInfo&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="s1"&gt;AUDIENCE&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;participant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`participant-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;participantUserId&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;participantAudio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createParticipantAudio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;participantUserId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;participantAudio&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;call&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="s1"&gt;stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nf"&gt;attachAudioStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;participantUserId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;participantAudio&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;// if part of speaker panel, send own audio stream&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;PARTICIPATION_INFO&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="s1"&gt;AUDIENCE&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;localStream&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;openUserMedia&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;Answer the call&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;localStream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;answer&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 way, the other peer responds to a connection request and similarly exchanges their own audio information with the other peer. And we have a 2 way communication / audio flow established!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try testing this out!&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;This time, you'll need the help of a friend to test the feature. You can use &lt;a href="https://github.com/localtunnel/localtunnel" rel="noopener noreferrer"&gt;&lt;em&gt;localtunnel&lt;/em&gt;&lt;/a&gt; to generate a unique URL that exposes your localhost server temporarily to the internet.&lt;/li&gt;
&lt;li&gt;Just like earlier, Open a browser tab &lt;a href="http://localhost:3000?username=rick" rel="noopener noreferrer"&gt;localtunnelurl?username=rick&lt;/a&gt; on Chrome, and share another URL &lt;a href="http://localhost:3000?username=elon" rel="noopener noreferrer"&gt;localtunnelurl?username=elon&lt;/a&gt; with your friend so that they may open it on their browser.&lt;/li&gt;
&lt;li&gt;When you both join the same room, you should be able to talk to each other!&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Feature #2: Leave a room
&lt;/h2&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%2Fxk1w1tmfdmx2ikvyi4bv.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%2Fxk1w1tmfdmx2ikvyi4bv.png" alt="Screenshot of an XKCD comic strip" width="696" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here's the breakdown of the application flow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Upon leaving a room, the client emits a web-socket event &lt;code&gt;user-left-room&lt;/code&gt; , with its peer information and other details. It'll also close all the peer connections between the rest of the participants in the room.&lt;/li&gt;
&lt;li&gt;The web-socket enabled server listens for any &lt;code&gt;user-left-room&lt;/code&gt; events. When it receives the event, it'll unsubscribe the user's socket from the room, and remove the participant from the database. Next, it broadcasts a web-socket event to notify the other participants in the room that the client has left.&lt;/li&gt;
&lt;li&gt;Any clients within the room are also listening for a &lt;code&gt;user-left-room&lt;/code&gt; event. As soon as they receive the notification from the server, they simply remove the user from the UI.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Front-end
&lt;/h3&gt;

&lt;p&gt;On the current client, upon leaving a room you'll emit a &lt;code&gt;user-left-room&lt;/code&gt; event, close the existing peer connections, and update the UI and reset the store:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;leaveRoom&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;PARTICIPATION_INFO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_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;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-left-room&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PARTICIPATION_INFO&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;closeConnections&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;updateParticipationInfo&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="nf"&gt;resetRoomDetails&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;unselectRoomNodes&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;leave-room&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;leaveRoom&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The other clients would also need to be notified when a participant leaves, and update their UI as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;socket&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="s1"&gt;user-left-room&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleParticipantLeave&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;handleParticipantLeave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;participantInfo&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="s1"&gt;user-left-room&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;participantInfo&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;participantInfo&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="s1"&gt;AUDIENCE&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;ROOM_INFO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audience&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ROOM_INFO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audience&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;participantInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;ROOM_INFO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;panel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ROOM_INFO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;panel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;participantInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nf"&gt;removeParticipantNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;participantInfo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Backend
&lt;/h3&gt;

&lt;p&gt;You'll need two functions in &lt;em&gt;models/participants.js:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A function &lt;code&gt;removeParticipant&lt;/code&gt; that deletes a participant from the database&lt;/li&gt;
&lt;li&gt;A function &lt;code&gt;updateParticipant&lt;/code&gt; that updates a participant's peer information in the database and sets it to &lt;em&gt;null&lt;/em&gt;. We'll use this method if the user is the room's host.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;removeParticipant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
            DELETE FROM participants
            WHERE room_id = $1 AND user_id = $2
            RETURNING *
        `&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;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&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;res&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;db&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="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;values&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;res&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateParticipant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;peerId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`  
        UPDATE participants SET peer_id = $1
        WHERE room_id = $2 AND user_id = $3
        RETURNING *
        `&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;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;peerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&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;res&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;db&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="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;values&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;res&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;  

    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&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;Be sure to specify the &lt;code&gt;WHERE&lt;/code&gt; clause when you delete an entry from the table, else you risk deleting all the data!&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%2Fjxk2j58nz3ltg0clapgw.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%2Fjxk2j58nz3ltg0clapgw.gif" alt="GIF showing what would happen in-case of a reckless delete operation" width="350" height="146"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, your signalling server logic in &lt;em&gt;routes/socket-events.js&lt;/em&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="nx"&gt;socket&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="s1"&gt;user-left-room&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;eventInfo&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;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;leave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;room_id&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;roomParticipant&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;eventInfo&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="s1"&gt;HOST&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;roomParticipant&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeParticipant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;room_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;eventInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;roomParticipant&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateParticipant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;room_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;eventInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_id&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="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;room_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-left-room&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;roomParticipant&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;And that's pretty much it. You might need the help of your friends to test it out!&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%2F5hbr9dmt88dw8qzh1nm6.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%2F5hbr9dmt88dw8qzh1nm6.png" alt="XKCD comic strip" width="296" height="399"&gt;&lt;/a&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%2Fk8letbaolkn5r5kz1oc6.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%2Fk8letbaolkn5r5kz1oc6.png" alt="Screenshot of Clubhive app" width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Obviously, what you build here is a hyper-simplified version of Clubhouse. But you can use this as a starting point and replicate more features.&lt;/p&gt;

&lt;h3&gt;
  
  
  Some features that are worth replicating
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Creating a new room
&lt;/h4&gt;

&lt;p&gt;Notice that in the bottom of the UI screen there's a button that says “Start a room”? Well, you can code the functionality for it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;On the UI, display a form where you can enter in the name of the new room, and optionally assign it to an existing Club.&lt;/li&gt;
&lt;li&gt;On the backend, create an API that takes in the name of the room, and adds a new room entry in the database&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Creating a new club
&lt;/h4&gt;

&lt;p&gt;Similarly, you could also add a feature that let's people start their own clubs. Might even throw in an invite feature that lets club creators invite other users to follow a member or become a member.&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%2Fqfa77cf43bztfzfskai6.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%2Fqfa77cf43bztfzfskai6.png" alt="XKCD comic strip" width="210" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Memberships
&lt;/h4&gt;

&lt;p&gt;Remember how a user can be a follower in other club? Well, turns out that in the actual app, a user can also be a &lt;em&gt;member&lt;/em&gt; of many different clubs. Starting a room &lt;strong&gt;within&lt;/strong&gt; a specific club requires the user to be a member of that club.&lt;/p&gt;

&lt;p&gt;When you incorporate memberships in your ERD, it would 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%2Fdfshx40zphohtcgsn73s.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%2Fdfshx40zphohtcgsn73s.png" alt="ERD diagram of Clubhouse showing the Memberships associative entities" width="786" height="986"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any guesses on what type of relationship exists between a user (member) and a club? &lt;strong&gt;HINT:&lt;/strong&gt; It's similar to the followers relationship that you read in the previous article&lt;/p&gt;

&lt;h4&gt;
  
  
  And several more features!
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mute/un-mute&lt;/strong&gt; yourself within a room&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request to speak&lt;/strong&gt; — new participants get added to the audience list by default, and will get added to the speaker panel on request.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A Chat messaging feature&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When deploying an app in production for real users, there are several other considerations as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architectures, Scaling and Costs
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Peer-to-peer (P2p) network
&lt;/h4&gt;

&lt;p&gt;What we covered in the tutorial was an example of a WebRTC implementation using a &lt;strong&gt;peer-to-peer (P2P)&lt;/strong&gt; or a &lt;strong&gt;mesh network architecture&lt;/strong&gt;. In this, each peer or participant that is a speaker upstreams their media (audio, video or both) to &lt;code&gt;n-1&lt;/code&gt; other peers in the network. Every peer also needs to downstream the media from other peers so that they may consume it. This is &lt;strong&gt;&lt;em&gt;decentralized&lt;/em&gt;&lt;/strong&gt; in nature, as no central server is being used to route media information.&lt;/p&gt;

&lt;h5&gt;
  
  
  Advantages
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Easy to understand and setup.&lt;/li&gt;
&lt;li&gt;Low set-up costs and less resource intensive on the server side, because the server isn't being used to process media streams.&lt;/li&gt;
&lt;li&gt;Better privacy due to decentralization — because the data (audio) is upstreamed and downstreamed directly by the clients in the network, without passing via a server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But this setup also has drawbacks&lt;/p&gt;

&lt;h5&gt;
  
  
  Drawbacks
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;A mesh network suffers in streaming data reliably as the number of peers or participants in the network increase. More number of peers will overwhelm the bandwidth because they'd need to stream their audio or video to n-1 other peers. Besides, cameras that can capture video in 4K are becoming commonplace — and if you're building a video streaming platform, then this can cause issues like audio and video lags.&lt;/li&gt;
&lt;li&gt;Besides, most browsers can only reliably support a fixed number of connections — for instance as of today, the officially supported limit for Google Chrome is &lt;a href="https://chromium.googlesource.com/chromium/src/third_party/+/1b6aa2b5c4d9ef48255799dc6f4b4d16426c0207/blink/renderer/modules/peerconnection/rtc_peer_connection.cc" rel="noopener noreferrer"&gt;500 connections&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Although less resource intensive on the server side, a mesh network can be more CPU intensive on the client side, because each peer would need to process data while upstreaming / downstreaming for &lt;code&gt;(n-1)&lt;/code&gt; connections in the network&lt;/li&gt;
&lt;li&gt;Better privacy also comes at a drawback of not being able to store/record conversations either.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So commercial products mostly don't rely on a peer-to-peer architecture. In-fact, Google Hangouts uses a peer-to-peer connection &lt;a href="https://support.google.com/hangouts/answer/6334301?hl=en#:%7E:text=To%20improve%20audio%20and%20video,through%20one%20of%20Google%27s%20servers" rel="noopener noreferrer"&gt;only for 2 participants&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So if you're trying to build and deploy a product for commercial purposes, and you anticipate a large number of persistent peer connections, there are other network architecture that need to be considered, which implement &lt;strong&gt;centralized&lt;/strong&gt; video bridging techniques:&lt;/p&gt;

&lt;h4&gt;
  
  
  Selective Forwarding Unit (SFU)
&lt;/h4&gt;

&lt;p&gt;Unlike the peer-to-peer architecture, in this network each participant sends their encrypted media streams only once to a centralized server, called an MCU server. This server then forwards those streams to the other participants, without any processing.&lt;/p&gt;

&lt;h5&gt;
  
  
  Advantages
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;The bandwidth to upstream media becomes quite less — the participant only needs to upload the information to the central server.&lt;/li&gt;
&lt;li&gt;More scalable than peer-to-peer network due to the less number of connections required.&lt;/li&gt;
&lt;li&gt;Easier to manage and store media data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Disadvantages
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Although more scalable than the peer-to-peer network, it still has limits.&lt;/li&gt;
&lt;li&gt;Some of the computation power is now offloaded to the server side. So the setup costs become a lot more.&lt;/li&gt;
&lt;li&gt;Not ideal for privacy, as media streams are being passed via a central server.&lt;/li&gt;
&lt;li&gt;Having a central server also means that it can potentially be a single point-of-failure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Multipoint Conferencing Unit (MCU)
&lt;/h4&gt;

&lt;p&gt;Also called &lt;a href="https://en.wikipedia.org/wiki/Videotelephony#Multipoint_control" rel="noopener noreferrer"&gt;Multipoint Control Unit&lt;/a&gt;, this architecture is similar to that of SFU — each client is connected to a centralized server. Unlike SFU however, the MCU server decodes, rescales, and mixes all incoming streams into a single new stream and then encodes and sends it to all clients.&lt;/p&gt;

&lt;p&gt;So any client in the network only need to upstream and downstream a single media stream.&lt;/p&gt;

&lt;h5&gt;
  
  
  Advantages
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Extremely easy on bandwidth.&lt;/li&gt;
&lt;li&gt;Much more scalable than peer-to-peer architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Disadvantages
&lt;/h5&gt;

&lt;p&gt;Just like the SFU architecture, the drawbacks of the MCU architecture are similar. In addition to it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extremely CPU intensive on the server side — since the server not only just streams media information to all the connected peers, but now also needs to transform media streams from all participants before sending it.&lt;/li&gt;
&lt;li&gt;Very high set-up costs&lt;/li&gt;
&lt;li&gt;Since all media streams are combined into a single one, the user can't control or disable audio/video reception from a specific participant.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You might want to leverage a cloud-based WebRTC service provider for enabling real time communication, rather than implement your own WebRTC logic. That way, your app could easily support large numbers of users. It isn't uncommon for a Clubhouse room to have several thousands of users, especially &lt;a href="https://techcrunch.com/2021/01/31/elon-musk-goes-live-on-clubhouse-but-with-the-room-full-fans-stream-audio-on-youtube/" rel="noopener noreferrer"&gt;when a celebrity&lt;/a&gt; is speaking!&lt;/p&gt;

&lt;p&gt;There are several WebRTC service providers, like &lt;a href="https://www.agora.io" rel="noopener noreferrer"&gt;Agora.io&lt;/a&gt;, &lt;a href="https://www.twilio.com/" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; and &lt;a href="https://aws.amazon.com/connect/" rel="noopener noreferrer"&gt;Amazon Connect&lt;/a&gt; that offer SDKs with APIs to integrate WebRTC into your app. In-fact, when Clubhouse launched in 2020, they seem to have relied on &lt;a href="https://www.bloomberg.com/news/articles/2021-02-11/clubhouse-mania-drove-6-billion-to-this-chinese-company" rel="noopener noreferrer"&gt;Agora&lt;/a&gt; for powering their audio chat!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I'm curious to see what you build next!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Thank you for reading.&lt;/p&gt;




&lt;p&gt;Here's the &lt;a href="https://github.com/booleanhunter-tech-blog/clubhive" rel="noopener noreferrer"&gt;complete source code&lt;/a&gt; for reference. In case you have any questions regarding the tutorial, please leave a comment below!&lt;/p&gt;

&lt;p&gt;If you're a committee or an organization and would like help in building more such solutions, reach out at &lt;strong&gt;&lt;a href="mailto:digital@egen.solutions"&gt;digital@egen.solutions&lt;/a&gt;.&lt;/strong&gt; As a digital-native company, we at &lt;a href="https://www.egen.solutions/" rel="noopener noreferrer"&gt;Egen&lt;/a&gt; know how to scale up and deliver fast, which means that we can help you create and launch such apps in days instead of months!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One good conversation can shift the direction of change forever.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  References &amp;amp; Resources
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.red5pro.com/blog/3-key-approaches-for-scaling-webrtc-sfu-mcu-and-xdn/" rel="noopener noreferrer"&gt;Approaches for scaling WebRTC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webrtcglossary.com/sfu/" rel="noopener noreferrer"&gt;Comparison between peer-to-peer, SFU and MCU&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://testrtc.com/different-multiparty-video-conferencing/" rel="noopener noreferrer"&gt;How Different WebRTC Multiparty Video Conferencing Technologies Look Like on the Wire&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://trueconf.com/blog/wiki/mcu-video-conferencing-architecture" rel="noopener noreferrer"&gt;MCU architecture advantages and disadvantages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=p2HzZkd2A40" rel="noopener noreferrer"&gt;Video presentation on Real-time communication with WebRTC: Google I/O 2013&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Videotelephony" rel="noopener noreferrer"&gt;Video teleconference Wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zoom.us/docs/doc/Zoom%20Connection%20Process%20Whitepaper.pdf" rel="noopener noreferrer"&gt;Whitepaper on Zoom's connection process&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.zoom.us/zoom-can-provide-increase-industry-leading-video-capacity/" rel="noopener noreferrer"&gt;How Zoom Provides Industry-Leading Video Capacity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.discord.com/how-discord-handles-two-and-half-million-concurrent-voice-users-using-webrtc-ce01c3187429" rel="noopener noreferrer"&gt;How Discord Handles Two and Half Million Concurrent Voice Users using WebRTC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.wainhouse.com/files/papers/wr-mp-vc-cloud.pdf" rel="noopener noreferrer"&gt;Whitepaper on Real World Options for Multipoint Videoconferencing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webrtchacks.com" rel="noopener noreferrer"&gt;WebRTC Hacks&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;In other architectures, you'd also need several other components, such as a &lt;strong&gt;media server&lt;/strong&gt; for encoding and decoding media streams. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;In the native WebRTC spec, this peer information is referred to as &lt;a href="https://webrtc.org/getting-started/peer-connections#ice_candidates" rel="noopener noreferrer"&gt;&lt;strong&gt;ICE&lt;/strong&gt; &lt;strong&gt;candidates&lt;/strong&gt;&lt;/a&gt;, standing for &lt;strong&gt;Internet Connectivity Establishment&lt;/strong&gt;. The &lt;em&gt;PeerJS&lt;/em&gt; library abstracts away all these details and instead provides you with a simple peerID string, which clients can use to establish a WebRTC connection. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>tutorial</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>How to build your own Social Audio chat application</title>
      <dc:creator>Ashwin Hariharan</dc:creator>
      <pubDate>Wed, 23 Feb 2022 11:23:16 +0000</pubDate>
      <link>https://dev.to/egeninc/how-to-build-your-own-social-audio-chat-application-4b88</link>
      <guid>https://dev.to/egeninc/how-to-build-your-own-social-audio-chat-application-4b88</guid>
      <description>&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is Social Audio&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;What you will build&lt;/p&gt;

&lt;p&gt;2.1 What you will learn&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Relational database design&lt;/li&gt;
&lt;li&gt;Real time communication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2.2 Tech stack&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Designing the Entity-Relationship Model&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using Postgres to query for information&lt;br&gt;
4.1 List all rooms&lt;br&gt;
4.2 List rooms for those clubs that the user follows&lt;br&gt;
4.3 Retrieve information for a single room&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Building a web service&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;If you're reading this article, you mostly likely discovered it via some form of social media. They are platforms that enable people across the world to interact with each other and share information on what's happening in their own corners of the world. And for more than 3 decades since their advent, social media apps allowed sharing information mostly in the form of text or images, particularly when it comes to sharing content in real time.&lt;/p&gt;

&lt;p&gt;In early 2020, an app called Clubhouse popped up in the iOS app store. It was a social media app, where the primary form of content was not photos, videos or text, but &lt;strong&gt;audio&lt;/strong&gt;. By the end of the year in the midst of the COVID-19 pandemic, when social distancing and remote work had become the norm, the app exploded in popularity - gaining over half a million users!&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%2Filtj0gp18aag07z0qr7y.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%2Filtj0gp18aag07z0qr7y.png" alt="An XKCD comic strip" width="233" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And just like usual when a new feature in an app goes viral, tech giants in the social media space take a crack at cloning and offering it in an attempt to enamor their users! Ever since Clubhouse's first release in 2020, companies like &lt;a href="https://media.twitter.com/en/articles/products/2021/twitter-spaces" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://about.fb.com/news/2021/06/live-audio-rooms-and-podcasts-on-facebook/" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt;, Slack, Spotify, Discord and even Reddit have either been creating products that compete directly with Clubhouse, or adding Clubhouse-like features to their existing platforms. These features are collectively known as &lt;strong&gt;social audio&lt;/strong&gt; - a new subclass of social media. Let's have look at what those features are.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Social Audio?
&lt;/h2&gt;

&lt;p&gt;A Social Audio application can be thought of as part-radio, part house party, part conference-call, part-podcast — all rolled into one. Once you're inside such an app, you can listen to conversations happening within a room, or start one yourself on a topic or theme of your choice. The theme could be anything — interviewing a celebrity or an expert, a discussion on a niche topic like the future of AI, or simply chit-chat.&lt;/p&gt;

&lt;p&gt;Here's a side-by-side comparison of Clubhouse and one of its competitor app, Spotify Greenroom —&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature #1: Discover conversations
&lt;/h3&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%2Fx4wplh1mgl4laidg65ge.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%2Fx4wplh1mgl4laidg65ge.png" alt="A side-by-side comparison of the room discovery feature in Clubhouse and Spotify Greenroom" width="800" height="708"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The home-screen on both the apps display a curated list of rooms for you based on your interests, and the clubs or groups that you follow. These rooms are spaces where conversations occur in real-time. There are some slight differences between what information is shown — but both apps show the room name, the number of participants, and give you a tiny preview of the list of speakers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature #2: Participate in conversations
&lt;/h3&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%2Fvsewpcnpr854k7jkid03.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%2Fvsewpcnpr854k7jkid03.png" alt="A side-by-side comparison of a room in Clubhouse and Spotify Greenroom" width="800" height="708"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you click on a room in the previous screen, the app takes you to the room and you'll be able to listen to ongoing conversations. On the top, you see the panel members, followed by people who are part of the audience. If you're a host, or invited as a speaker or moderator, you'll be able to speak as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature #3: Leave a conversation
&lt;/h3&gt;

&lt;p&gt;Both these apps offer the user to leave the room anytime at the click of a button. This will have the effect of returning you to the home screen.&lt;/p&gt;




&lt;p&gt;That's pretty much the core features! Plenty of these apps also have additional features common in social media platforms, like exchanging chat messages, subscribing to topics that interest you, and also following other users and clubs.&lt;/p&gt;

&lt;p&gt;Using your own voice to interact with people just feels very natural. This explains why during COVID-19 lockdowns, friends, relatives and colleagues started relying on apps like Zoom, Whatsapp Video, and Google Hangouts for connecting from time to time.&lt;/p&gt;

&lt;p&gt;While the question of whether social audio apps will continue to remain popular remains to be seen, it's very interesting to learn the underlying engineering that makes them work! How does Clubhouse, for example, enable users to join rooms and socialize using their voice?&lt;/p&gt;

&lt;p&gt;So if you're a developer and feel the need to scratch that programming itch, keep reading!&lt;/p&gt;

&lt;h2&gt;
  
  
  What you will build
&lt;/h2&gt;

&lt;p&gt;In this tutorial, you'll learn to clone some of the most salient features of Clubhouse. You won't focus on scale or performance right now, but you'll develop an understanding of what core technologies make applications like these possible. At the end of this tutorial series, you will build the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You'll attempt to mimic the &lt;em&gt;Hallways&lt;/em&gt; feature in the Clubhouse app, where users are able to browse and discover active rooms.&lt;/li&gt;
&lt;li&gt;Selecting a room to join it, where you can see all the other participants in the room and participate in the conversation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This will be a programming tutorial mainly focused on functionality, so your focus will not be much on web design, (though you're welcome to do so if you wish!).&lt;/p&gt;

&lt;h3&gt;
  
  
  What you will learn
&lt;/h3&gt;

&lt;h4&gt;
  
  
  #1: Relational Database Design
&lt;/h4&gt;

&lt;p&gt;You'll learn how to design and architecture a relational database system for representing the most essential data entities required for this app.&lt;/p&gt;

&lt;h4&gt;
  
  
  #2: Real Time Communication
&lt;/h4&gt;

&lt;p&gt;You'll learn how to implement WebRTC to enable real time communication of audio, to enable live conversations within a club room.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technologies that you will learn and use
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;On the front-end you'll use HTML, CSS and JavaScript to build the UI. You'll also use PeerJS — a library that allows you to easily for implement Real Time Transmission of audio/video in WebRTC.&lt;/li&gt;
&lt;li&gt;On the back-end, you'll use NodeJS/Express for building web server and APIs. You'll also use Socket.io for implementing a signalling-server as is required in WebRTC based applications — of which you'll learn more about later.&lt;/li&gt;
&lt;li&gt;You'll learn to use &lt;strong&gt;PostgreSQL&lt;/strong&gt; as your database. It's fast, extremely extensible to a wide array of needs (including hyperlocal business applications), has support for both SQL and NO-SQL data models, and is scalable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It will be helpful to have at least a theoretical understanding of how relational database systems work.&lt;/li&gt;
&lt;li&gt;Some knowledge of JavaScript and NodeJS is also useful.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Let's get started!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll learn how to construct an efficient relational database model for organizing data and effective querying. You'll then build an API service that allows a client to retrieve information, which can then be displayed on a user interface.&lt;/p&gt;

&lt;p&gt;Go to &lt;a href="https://github.com/booleanhunter-tech-blog/clubhive-boilerplate" rel="noopener noreferrer"&gt;this link&lt;/a&gt;, and follow the instructions to download the repository and install all the necessary project dependencies. It contains some boilerplate code and helper methods for you to use and build the rest of the project as you go about the tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the Entity-Relationship Model
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;&lt;em&gt;entity&lt;/em&gt;&lt;/strong&gt; can be thought of as a &lt;em&gt;category of data&lt;/em&gt; or information in a system, which is of particular interest to us. When you design an entity-relationship model for a system, you identify entities and their relationships with each other, which can be stored in a database.&lt;/p&gt;

&lt;p&gt;Broadly speaking, your app has the following entities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Users&lt;/strong&gt;: Users can join a room and participate in an ongoing conversation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clubs&lt;/strong&gt;: A club will usually be centered around a particular topic, and can consist of multiple live sessions or "&lt;em&gt;rooms&lt;/em&gt;".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rooms&lt;/strong&gt;: A room is a live discussion where participants can tune in and listen, or speak too. A club can have multiple rooms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Topics&lt;/strong&gt;: A club can belong to a certain category, like &lt;strong&gt;Technology&lt;/strong&gt; or &lt;strong&gt;Geopolitics&lt;/strong&gt;. Any rooms created within a club will have conversations related to this topic. As such, there can be multiple clubs around a certain theme or topic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We could imagine more entities in the system but these will suffice for now. If you built data models for each of the above entities, what would they look like? Take a moment to think!&lt;/p&gt;

&lt;p&gt;Here's how our Entity Relationship Diagram could look like:&lt;/p&gt;

&lt;h3&gt;
  
  
  Entity Relationship Diagram
&lt;/h3&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%2Frponnu675kxkip1sdim6.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%2Frponnu675kxkip1sdim6.png" alt="A simple Entity relationship diagram of Clubhouse" width="786" height="896"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What we have here is an &lt;strong&gt;Entity-Relationship Diagram,&lt;/strong&gt; or &lt;strong&gt;ERD&lt;/strong&gt; for short. As the name suggests, it helps us visualize the various entities in a system and their relationships with each other. The above diagram is obviously a very simplistic version of how various entities within the actual Clubhouse app might look like and relate with each other. However, it still serves as a useful starting point!&lt;/p&gt;

&lt;p&gt;Let's take a closer look at some of these relationships:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt; club has exactly &lt;strong&gt;one&lt;/strong&gt; founder. This is a &lt;strong&gt;one-to-one&lt;/strong&gt; relationship.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt; club has many &lt;strong&gt;rooms&lt;/strong&gt;. That's a &lt;strong&gt;one-to-many&lt;/strong&gt; relationship. &lt;strong&gt;A room however, may or may not be associated with a club.&lt;/strong&gt;
&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%2Fp71fwapn8v3stiigu6is.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%2Fp71fwapn8v3stiigu6is.png" alt="Entity relationship diagram of Clubhouse showing the relationships between Users, Clubs and Rooms" width="762" height="972"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly, you can identify some more relationships:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt; club can have &lt;strong&gt;many&lt;/strong&gt; followers (users)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt; user can follow &lt;strong&gt;many&lt;/strong&gt; clubs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So there exists a &lt;strong&gt;many-to-many&lt;/strong&gt; relationship between a user and a club! In &lt;em&gt;ERD&lt;/em&gt; and relational-database terminology, this relationship is referred to as an &lt;strong&gt;associative entity&lt;/strong&gt;, and is implemented by introducing an &lt;strong&gt;associative table&lt;/strong&gt; or, &lt;strong&gt;cross-reference&lt;/strong&gt; table. So in-order to denote this relationship, we'll have a third table which you can name &lt;em&gt;followers&lt;/em&gt;, indicating the relationship:&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%2Fwkhhvw3i6ojzejara5wx.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%2Fwkhhvw3i6ojzejara5wx.png" alt="Entity relationship diagram of Clubhouse showing an association between Users and Clubs" width="766" height="1091"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly, we have a relationship between a user and a room:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt; room can have &lt;strong&gt;many&lt;/strong&gt; participants (users)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt; user could be a participant at &lt;strong&gt;many&lt;/strong&gt; rooms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So there exists a &lt;strong&gt;many-to-many&lt;/strong&gt; relationship between users and rooms. Just like before, you'll use a cross-reference table &lt;strong&gt;participants&lt;/strong&gt; in-order to map out this relationship:&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%2Fl9saaswb65pzy6k74fru.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%2Fl9saaswb65pzy6k74fru.png" alt="Entity relationship diagram of Clubhouse showing a participation association between Users and Rooms" width="779" height="1051"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Designing the Schema in PostgreSQL
&lt;/h3&gt;

&lt;p&gt;PostgreSQL is amongst the most popular relational databases used all over the world, being in the top 2 widely used databases for 2 years in a row in &lt;a href="https://insights.stackoverflow.com/survey/2019#technology" rel="noopener noreferrer"&gt;2019&lt;/a&gt; and &lt;a href="https://insights.stackoverflow.com/survey/2020#most-popular-technologies" rel="noopener noreferrer"&gt;2020&lt;/a&gt;. It has a very high compliance with SQL standards, supports a wide range of native data types (including custom types as well as JSON!), and is extremely &lt;a href="https://www.digitalocean.com/docs/databases/postgresql/resources/supported-extensions/" rel="noopener noreferrer"&gt;extensible&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's begin creating the above tables using the &lt;code&gt;psql&lt;/code&gt; prompt. Once you've fired up the &lt;code&gt;psql&lt;/code&gt; terminal, run the following command to create a new database:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CREATE DATABASE clubhouse;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Next, switch to this database so that the subsequent commands apply to this database by typing &lt;code&gt;\c clubhouse;&lt;/code&gt;. Here are some commonly used queries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;\l&lt;/code&gt;: List available databases.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;\dt&lt;/code&gt;: List existing tables.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SELECT * FROM tablename&lt;/code&gt;: Will print all rows and columns in a table.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DROP TABLE tablename&lt;/code&gt;: Will delete the table.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DROP DATABASE databasename&lt;/code&gt;: Deletes the database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, you'll start by adding the following tables, one by one:&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;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;topics&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;BIGINT&lt;/span&gt; &lt;span class="k"&gt;GENERATED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;IDENTITY&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;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="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;description&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;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;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;BIGINT&lt;/span&gt; &lt;span class="k"&gt;GENERATED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;IDENTITY&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;username&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="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&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;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="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&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;bio&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;350&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;join_date&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;
&lt;span class="p"&gt;);&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;clubs&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;BIGINT&lt;/span&gt; &lt;span class="k"&gt;GENERATED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;IDENTITY&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;founder_id&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;references&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="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="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;topic_id&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&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="nb"&gt;date&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&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;topic_id&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;topics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&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;rooms&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;BIGINT&lt;/span&gt; &lt;span class="k"&gt;GENERATED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;IDENTITY&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;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="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;club_id&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt; &lt;span class="k"&gt;references&lt;/span&gt; &lt;span class="n"&gt;clubs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;
&lt;span class="p"&gt;);&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;followers&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;club_id&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;clubs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&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;user_id&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&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="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="k"&gt;UNIQUE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;club_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&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;club_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;),&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;club_id&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;clubs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;,&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;user_id&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;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;
&lt;span class="p"&gt;);&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;participants&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;room_id&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&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;user_id&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&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="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="k"&gt;role&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;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'AUDIENCE'&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HOST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'MODERATOR'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'SPEAKER'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'AUDIENCE'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;join_date&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;peer_id&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="k"&gt;UNIQUE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;room_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&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;room_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;),&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;room_id&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;rooms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;,&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;user_id&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;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&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;A participant could have one of the following roles:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Host&lt;/strong&gt; — This would be those users who created the room and are in the main speaker panel. They also have the highest privileges within that room, including moderator privileges.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;moderator&lt;/strong&gt; is also present in the speaker panel and can invite other speakers to the room.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;speaker&lt;/strong&gt; is also part of the speaker panel, but cannot add or invite other users.&lt;/li&gt;
&lt;li&gt;Finally, the other participants will be part of the &lt;strong&gt;audience&lt;/strong&gt;. They can only listen, but not speak with other users.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you've created the above tables, you'll also add some sample data. Run the commands from the &lt;em&gt;models/db-schema.txt&lt;/em&gt; file in your repository to add some sample data.&lt;/p&gt;

&lt;p&gt;Once you're done, you can run &lt;code&gt;SELECT * FROM users;&lt;/code&gt; for the &lt;code&gt;users&lt;/code&gt; table and the rest of the tables too in-order to verify if your data has been imported successfully. I highly recommend you to take a few moments to run the above query for the rest of the tables and study the data that's stored in them.&lt;/p&gt;
&lt;h2&gt;
  
  
  Querying for information
&lt;/h2&gt;

&lt;p&gt;We can imagine writing the following queries for our application:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A query to retrieve a list of all available rooms and their information. It would be useful to display this for a user who haven't logged in, so that they may get a glimpse of the kind of conversations that occur with the app!&lt;/li&gt;
&lt;li&gt;Similarly, a user that has logged in would be interested to browse through the rooms for those clubs that they follow.&lt;/li&gt;
&lt;li&gt;And finally when a user joins a room, we'll need a query that retrieves the club-name, room-name and the information about all the participants in the room which we can display in the UI.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Query #1: List all rooms
&lt;/h3&gt;

&lt;p&gt;This basically includes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;All the rooms that are being hosted within a club; AND&lt;/li&gt;
&lt;li&gt;All the rooms that aren't within any specific club&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Here's a screenshot of the home screen when you log into clubhouse:&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%2Fz3x7801et20wrmlqys86.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%2Fz3x7801et20wrmlqys86.png" alt="screenshot of the home screen when you log into clubhouse" width="544" height="968"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The screen contains a scrollable list of rooms, displayed within a card. If you notice closely, each room-card has the following information:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If the room is being hosted within a club, the &lt;strong&gt;name of the club&lt;/strong&gt; appears on the top (with a green icon next to it).&lt;/li&gt;
&lt;li&gt;The names of some of the participants within the room. On Clubhouse, these are usually the names of the hosts, speakers or moderators within the room.&lt;/li&gt;
&lt;li&gt;On the bottom, you also see the overall number of room participants, and also the number of people in the speaker panel.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Basically, our query needs to give us data in the following format:&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%2Fsydjiqxe45d9y76nsdj4.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%2Fsydjiqxe45d9y76nsdj4.png" alt="JSON structure of the rooms information" width="800" height="787"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's break it down:&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 1: Extract the room ID, room name and the club name that the room is a part of
&lt;/h4&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;rooms&lt;/span&gt;&lt;span class="p"&gt;.&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;rooms&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;clubs&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;AS&lt;/span&gt; &lt;span class="n"&gt;club_name&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;clubs&lt;/span&gt;
&lt;span class="k"&gt;RIGHT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;club_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clubs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;

&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Running this query in your psql prompt will give you the following result:&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%2Fczpdqpno7p3wrfzvgdxl.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%2Fczpdqpno7p3wrfzvgdxl.png" alt="Screenshot of the Postgrsql Query result" width="800" height="658"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does this work?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We mention the names of the columns that we're interested in in the beginning. Since our room table and our clubs table both have a column called name, we can rename either of these columns using the &lt;code&gt;AS&lt;/code&gt;clause to assign an alias.&lt;/li&gt;
&lt;li&gt;In the FROM clause, you mention the table or tables from which you can extract the info of one or several of the columns that we mentioned in the beginning.&lt;/li&gt;
&lt;li&gt;And finally, we do a RIGHT JOIN on the rooms table. A JOIN creates an link between two tables based on a given condition. This condition is given within the &lt;code&gt;ON&lt;/code&gt; clause.&lt;/li&gt;
&lt;li&gt;The ORDER BY clause allows you to determine in what order should the query results be returned in. In social networking apps, the latest (or most recent) information is seen in the top, we'll retrieve the rooms in descending order of their creation date.&lt;/li&gt;
&lt;li&gt;The LIMIT clause places an upper limit on how many rows should be returned.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Types of JOINS in a relational database management system:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;An &lt;code&gt;INNER JOIN&lt;/code&gt; returns &lt;strong&gt;only&lt;/strong&gt; the common elements between both the tables — in other words, the intersection where they match on the joined column given by the &lt;code&gt;ON&lt;/code&gt; clause.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;LEFT JOIN&lt;/code&gt; joins the rows from the first (LEFT) table to the second (RIGHT) table. But unlike the INNER JOIN, it will also include the rows from the LEFT table even if they don't match the condition.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;RIGHT JOIN&lt;/code&gt; is similar to the LEFT JOIN, except that it will include rows from the second (RIGHT) table that don't match the condition.&lt;/li&gt;
&lt;/ol&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%2F00fqrup6zxi6m74zdxfw.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%2F00fqrup6zxi6m74zdxfw.png" alt="Figure showing the 3 most common types of Joins in relational database systems" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h5&gt;
  
  
  Why did we apply a RIGHT JOIN?
&lt;/h5&gt;

&lt;p&gt;By selecting the &lt;strong&gt;clubs&lt;/strong&gt; table and applying a RIGHT JOIN on the &lt;strong&gt;rooms&lt;/strong&gt; table, we create an intersection between these two tables. This way, each row within our query result will contain room information and its associated club name too. But we also want to include those rows (rooms) that aren't associated with any club, and hence we specifically apply a RIGHT JOIN.&lt;/p&gt;
&lt;h4&gt;
  
  
  Step #2: Retrive the information of the panel participants for each room
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;participants&lt;/code&gt; table contains the information about the participants within a room. It has the following foreign keys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;room_id&lt;/code&gt; refers to the &lt;em&gt;rooms&lt;/em&gt; table&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;user_id&lt;/code&gt; refers to the &lt;em&gt;users&lt;/em&gt; table&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So we'll need to add a &lt;strong&gt;subquery&lt;/strong&gt; that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Selects the column names that we're interested in: the participant's user ID, their name and their type.&lt;/li&gt;
&lt;li&gt;Applies an INNER JOIN on the &lt;em&gt;users&lt;/em&gt; table, so that each participant row in the &lt;em&gt;subquery&lt;/em&gt; is associated with its respective participant name from the &lt;em&gt;users&lt;/em&gt; table.&lt;/li&gt;
&lt;li&gt;Apply a &lt;code&gt;WHERE&lt;/code&gt; clause to match the participant rows with their respective rooms&lt;/li&gt;
&lt;li&gt;And a filter to return only those participants that are either a host, moderator or speaker.
&lt;/li&gt;
&lt;/ol&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;rooms&lt;/span&gt;&lt;span class="p"&gt;.&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;rooms&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;clubs&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;AS&lt;/span&gt; &lt;span class="n"&gt;club_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;json_agg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&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;name&lt;/span&gt;
        &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;
        &lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
            &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&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="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
            &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HOST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'MODERATOR'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'SPEAKER'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;  
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;participant&lt;/span&gt;  
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;panel&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;clubs&lt;/span&gt;

&lt;span class="k"&gt;RIGHT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;club_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clubs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;

&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;

&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;100&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;json_agg&lt;/code&gt; function is an &lt;a href="https://www.postgresql.org/docs/9.5/functions-aggregate.html" rel="noopener noreferrer"&gt;aggregate&lt;/a&gt; function provided by POSTGRESQL that groups the results of the inner subquery into a JSON array.&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%2Fd1a08uzzppvr04h7zwnr.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%2Fd1a08uzzppvr04h7zwnr.png" alt="Screenshot of the Postgrsql Query result" width="800" height="214"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Step #3: Retrive the count of the participants in the panel, and the total number of participants in the room
&lt;/h4&gt;

&lt;p&gt;For this, we'll apply a very similar query to the previous subquery, except this time we don't need to apply an &lt;code&gt;INNER JOIN&lt;/code&gt; on the users table.&lt;/p&gt;

&lt;p&gt;For fetching the number of participants on the panel list, we just need to select those rows that match the participant row to its associated room id, and apply a filter on the participant-types. Then all you need to do is return a COUNT of those rows.&lt;/p&gt;

&lt;p&gt;So here's how the final query looks like:&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;rooms&lt;/span&gt;&lt;span class="p"&gt;.&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;rooms&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;clubs&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;AS&lt;/span&gt; &lt;span class="n"&gt;club_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;json_agg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&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;name&lt;/span&gt;
        &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;

        &lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
            &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&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="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
            &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HOST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'MODERATOR'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'SPEAKER'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;participant&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;panel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;FILTER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
            &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'AUDIENCE'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;panel_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;FILTER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;participant_countFROM&lt;/span&gt; &lt;span class="n"&gt;clubs&lt;/span&gt;

&lt;span class="k"&gt;RIGHT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;club_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clubs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;

&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&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;rooms&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;clubs&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;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;100&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%2Fbp1s0ad5z2l7zubfs39m.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%2Fbp1s0ad5z2l7zubfs39m.png" alt="Screenshot of the Postgrsql Query result" width="800" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whew, that was a lot! If you've followed the tutorial this far, I commend you!&lt;/p&gt;

&lt;p&gt;Let's move on to the next query —&lt;/p&gt;
&lt;h3&gt;
  
  
  Query #2: List rooms for those clubs that the user follows
&lt;/h3&gt;

&lt;p&gt;Fetching all room information is great. But wouldn't it be better if we tailored the room discovery feature for each user just a little bit?&lt;/p&gt;

&lt;p&gt;The following query will return the room list information for the user &lt;em&gt;Elon&lt;/em&gt;, whose &lt;em&gt;user_id&lt;/em&gt; is &lt;code&gt;5&lt;/code&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="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&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;rooms&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;clubs&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;AS&lt;/span&gt; &lt;span class="n"&gt;club_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;json_agg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&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;name&lt;/span&gt;
            &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;

            &lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
                &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&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="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
                &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'AUDIENCE'&lt;/span&gt;
            &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;participant&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;panel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;FILTER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
            &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'AUDIENCE'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;panel_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;FILTER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;participant_count&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;clubs&lt;/span&gt;

&lt;span class="k"&gt;RIGHT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;club_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clubs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;

&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;

&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;followers&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;followers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;club_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clubs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;

&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;followers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;club_id&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;

&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&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;rooms&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;clubs&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;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;

&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;100&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 query is almost exactly the same as the previous one, except for a few key differences:&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="p"&gt;...&lt;/span&gt;  
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;followers&lt;/span&gt;  
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;followers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;club_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clubs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;

&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;followers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;  
    &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;club_id&lt;/span&gt; &lt;span class="k"&gt;IS&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;p&gt;We do a LEFT JOIN on the followers table, and apply a WHERE clause so that we get those rows where &lt;code&gt;elon&lt;/code&gt; is a follower. But because of this third join, it also has the effect of removing the rooms that don't belong to any club, so we apply one more condition using the &lt;code&gt;OR&lt;/code&gt; clause.&lt;/p&gt;

&lt;p&gt;And here are the results! —&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%2Frrkqabd8dl94kyaxn3bl.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%2Frrkqabd8dl94kyaxn3bl.png" alt="Screenshot of the Postgrsql Query result" width="800" height="122"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you may observe, the previous query returned 12 rows, this query returned only 8 rows — which makes sense, given that this user isn't following all the available clubs in the app.&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%2Fe0jyi9t3rk24mtck7qme.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%2Fe0jyi9t3rk24mtck7qme.png" alt="Mapping the UI with the query" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Query #3: Retrieve information for a single room
&lt;/h3&gt;

&lt;p&gt;When you tap or click on a room in clubhouse, the app opens up the room and you're officially a participant. You're able to see the users in the speaker list, and also other participants who aren't on the panel, but are simply listening.&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%2Fz8ph8e1r17j0wi0fb2bk.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%2Fz8ph8e1r17j0wi0fb2bk.png" alt="Screenshot of a room within Clubhouse" width="544" height="968"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So next, you'll write a query that for a given room ID retrieves the following room information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name of the club&lt;/li&gt;
&lt;li&gt;Name of the room&lt;/li&gt;
&lt;li&gt;The names of speakers on the panel&lt;/li&gt;
&lt;li&gt;The names of the rest of the participants in the audience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's how the resulting query response might look like:&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%2Fpeqiwdrd1nhx18pw2y5t.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%2Fpeqiwdrd1nhx18pw2y5t.png" alt="JSON structure of room metadata" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's write a query that fetches the above details for the room &lt;code&gt;id&lt;/code&gt; 3:&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;rooms&lt;/span&gt;&lt;span class="p"&gt;.&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;rooms&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;clubs&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;AS&lt;/span&gt; &lt;span class="n"&gt;club_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;json_agg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;SELECT&lt;/span&gt;
            &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;peer_id&lt;/span&gt;&lt;span class="p"&gt;,&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;name&lt;/span&gt;
        &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;,&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;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&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="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
            &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HOST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'MODERATOR'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'SPEAKER'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;participant&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;panel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;json_agg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;peer_id&lt;/span&gt;&lt;span class="p"&gt;,&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;name&lt;/span&gt;
        &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;,&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;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&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="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
            &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'AUDIENCE'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;participant&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;audience&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;clubs&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;club_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clubs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Since the first (left) table is rooms and the second (right) table is clubs, we apply a LEFT JOIN. This makes sure that the row from the rooms table isn't discarded even if it isn't associated with any club.&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%2Fv9tjo3fe1dwo47n0cng3.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%2Fv9tjo3fe1dwo47n0cng3.png" alt="PSQL query and response" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Building a web service
&lt;/h2&gt;

&lt;p&gt;Perfect! Now let's try doing the same, except this time you'll create APIs that uses the above queries to return the information we seek.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A brief explanation about some of the packages that you'll use:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://expressjs.com/" rel="noopener noreferrer"&gt;Express&lt;/a&gt; is a web framework for NodeJS. It has lots of APIs, utilities and middlewares in its ecosystem to help you build your application.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/pg" rel="noopener noreferrer"&gt;pg&lt;/a&gt; is a NodeJS middleware for PostgreSQL. It helps your Node app talk to your database. You'll use this to run the same commands that you ran using the &lt;code&gt;psql&lt;/code&gt; terminal, except this time it will be within your web server.&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://handlebarsjs.com/" rel="noopener noreferrer"&gt;handlebars&lt;/a&gt; is a templating engine. You can use it to do parsing and apply conditional logic within html templates on the server before serving them to the client.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Feature #1: List and display rooms
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Backend
&lt;/h4&gt;

&lt;p&gt;Let's begin by first creating functions for each one of our previous queries.&lt;/p&gt;

&lt;p&gt;Inside &lt;em&gt;models/index.js&lt;/em&gt;, you'll add code for the following functions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;getRooms(limit)&lt;/code&gt;: Returns all available rooms&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;getRoomsForUser(limit)&lt;/code&gt;: Returns rooms for those clubs that the user follows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;getRoomDetails(roomId)&lt;/code&gt;: Returns information for the given room id&lt;/li&gt;
&lt;/ol&gt;


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



&lt;p&gt;Next, write a function &lt;code&gt;getUserByUsername(username)&lt;/code&gt; that fetches information of a user using their username. Add the following in &lt;em&gt;models/users.js&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUserByUsername&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
            SELECT * from users
            WHERE username = $1
        `&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;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&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;db&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="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;values&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;res&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&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;h5&gt;
  
  
  APIs
&lt;/h5&gt;

&lt;p&gt;Next, you'll write APIs that utilize the above functions and expose them in a web service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/rooms&lt;/code&gt;: A GET request to this route will return all available rooms. If you pass a query param &lt;code&gt;?userId=6&lt;/code&gt;, then the API should return only those rooms of those clubs that the user is a member of.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/rooms/:roomId&lt;/code&gt;: A GET request to this route will return the participant information for a particular room.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add the following code inside &lt;em&gt;routes/rooms.js:&lt;/em&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="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/:roomId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;room&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRoomDetails&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;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roomId&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;room&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;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;room&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;room&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="na"&gt;club_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;club_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;audience&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audience&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audience&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
                &lt;span class="na"&gt;panel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;panel&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;panel&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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Room does not exist&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;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="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="k"&gt;async&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;rooms&lt;/span&gt; &lt;span class="o"&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;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;rooms&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRoomsForUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;rooms&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRooms&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;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="nx"&gt;rooms&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;Finally, run &lt;code&gt;npm start&lt;/code&gt; in your project root, and open the following URLs on your browser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://localhost:3000/rooms" rel="noopener noreferrer"&gt;http://localhost:3000/rooms&lt;/a&gt;: This will return a list of all available rooms.&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://localhost:3000/rooms?userId=1" rel="noopener noreferrer"&gt;http://localhost:3000/rooms?userId=1&lt;/a&gt;: Returns all rooms for a specific user.&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://localhost:3000/rooms/4" rel="noopener noreferrer"&gt;http://localhost:3000/rooms/4&lt;/a&gt;: Returns information for a single room.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Frontend
&lt;/h4&gt;

&lt;p&gt;Going to &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; displays an empty page. First, we'll add a fake login, so when you type &lt;a href="http://localhost:3000/username=rick" rel="noopener noreferrer"&gt;http://localhost:3000/username=rick&lt;/a&gt;, it displays a message greeting the user Rick Sanchez.&lt;/p&gt;

&lt;p&gt;On your &lt;em&gt;routes/index.js&lt;/em&gt;, add 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="cm"&gt;/* GET home page. */&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="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="k"&gt;async&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&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;userInfo&lt;/span&gt; &lt;span class="o"&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;username&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;user&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUserByUsername&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;userInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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&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="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;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userInfo&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 above code, we check for the request query params' value for username. Then, we use the username to extract the user information, and interpolate these values for the HTML document inside &lt;em&gt;views/index.hbs&lt;/em&gt;. The interpolation is done by &lt;em&gt;handlebars&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Now when you load the &lt;a href="http://localhost:3000/username=rick" rel="noopener noreferrer"&gt;http://localhost:3000/username=rick&lt;/a&gt;, it will display the message "&lt;em&gt;Howdy, Rick Sanchez&lt;/em&gt;" on the header.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next, let's use our APIs to fetch a list of rooms and display them on the UI&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, extract the user details in our HTML file and save them in a variable. Add the following code inside &lt;em&gt;public/modules/index.js&lt;/em&gt;:
&lt;/li&gt;
&lt;/ul&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;updateUserInfo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-userId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Add the following function inside &lt;em&gt;models/api.js&lt;/em&gt; that makes a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API" rel="noopener noreferrer"&gt;fetch&lt;/a&gt; request to our endpoint to fetch the list of rooms, and stores them in a &lt;code&gt;ROOMS&lt;/code&gt; variable:
&lt;/li&gt;
&lt;/ul&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchRooms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`/rooms?userId=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/rooms&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="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="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="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;ok&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;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="nf"&gt;updateRooms&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;rooms&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusText&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;Now, you'll use this function to fetch the list of rooms, and utilize a helper method to display them in the UI. Add the following code in &lt;em&gt;modules/index.js&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;displayRooms&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&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;USER_INFO&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;result&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;fetchRoomsForUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;USER_INFO&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchRooms&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;renderRooms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ROOMS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;And we're done!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You should see the following page when you head over to &lt;a href="http://localhost:3000/?username-rick" rel="noopener noreferrer"&gt;http://localhost:3000/?username-rick&lt;/a&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%2Fagnsjbztk00iyxw6soxy.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%2Fagnsjbztk00iyxw6soxy.png" alt="Screenshot of Clubhive hallway" width="800" height="586"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;In the next tutorial, you'll learn how to implement the next feature — joining a room and interacting with other participants via audio!&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/egeninc" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&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%2Forganization%2Fprofile_image%2F4696%2Ff904b21d-9fd4-4740-85aa-f48e9ea1992e.png" alt="Egen" width="800" height="450"&gt;
      &lt;div class="ltag__link__user__pic"&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%2Fuser%2Fprofile_image%2F108200%2F3f49e017-fd5f-4531-87fe-e4bb39f82949.jpg" alt="" width="800" height="1200"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/egeninc/how-to-build-your-own-social-audio-chat-application-part-2-4jjo" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;How to build your own Social Audio Chat Application - Part 2&lt;/h2&gt;
      &lt;h3&gt;Ashwin Hariharan for Egen ・ Feb 23 '22&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#tutorial&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;p&gt;If you liked this tutorial or have any questions, do leave a like or a comment!&lt;/p&gt;




&lt;p&gt;Special thanks to &lt;a href="https://prashanthmolakala.github.io/" rel="noopener noreferrer"&gt;Prashanth Molakala&lt;/a&gt; for helping with optimizing the postgresql queries 🙏&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>webdev</category>
      <category>node</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Fashion E-Commerce: Using Computer Vision to Find Clothing that Fits Like a Glove</title>
      <dc:creator>Ashwin Hariharan</dc:creator>
      <pubDate>Tue, 15 Feb 2022 10:07:05 +0000</pubDate>
      <link>https://dev.to/egeninc/fashion-e-commerce-using-computer-vision-to-find-clothing-that-fits-like-a-glove-2o3i</link>
      <guid>https://dev.to/egeninc/fashion-e-commerce-using-computer-vision-to-find-clothing-that-fits-like-a-glove-2o3i</guid>
      <description>&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Problems in shopping for clothing online&lt;/li&gt;
&lt;li&gt;How does SmartTry use computer vision to make cloth shopping easier&lt;/li&gt;
&lt;li&gt;Tutorial&lt;/li&gt;
&lt;li&gt;Jupyter-Notebook slides&lt;/li&gt;
&lt;li&gt;Architecture and implementation&lt;/li&gt;
&lt;li&gt;Potential revenue models&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;It was October 2019, and my cousin's wedding was taking place in about a week. Like everyone else, I was faced with the dilemma — what to wear? The struggle was real.&lt;/p&gt;

&lt;p&gt;So there I was, in the mall with my brother picking out shirts, because everyone knows you can't go wrong with a great shirt right? I picked a few and was searching for a place to try them on before deciding which ones to buy. Finally found somewhere just right.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It was a &lt;strong&gt;fitting&lt;/strong&gt; room.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I tried them on, and after checking myself out (and getting my brother's nod of approval) I bought the ones that fit just right. The following week, I wore one of them to the wedding. I looked great. Just call me &lt;a href="https://www.urbandictionary.com/define.php?term=GQ" rel="noopener noreferrer"&gt;GQ&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Like many guys, I don't particularly like shopping, but I tolerate the fitting room because it gives me the opportunity to see how everything looks before going out in public. Getting the right clothes the first time means not having to head back to the store for a return and round 2. I'm efficient like that.&lt;/p&gt;

&lt;p&gt;It goes by different names — changing room, dressing room, trial room. You know, those small cubicles with the big mirrors and lighting that makes you look like an extra in &lt;em&gt;The Walking Dead&lt;/em&gt;? The place where before you enter, the staff stops you and barks "Sorry, you can only bring in 4 pieces at a time", and you're like — "Can I please bring in all 7? I won't take long!" — just so that you can play dress-up for the next 45 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%2Flqyhzewapcgt6u4ft3jg.jpg" 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%2Flqyhzewapcgt6u4ft3jg.jpg" alt="Meme" width="335" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Yeah, they're kinda nice aren't they?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But then 2020 happened — a year that most people will remember for a very specific reason. With social distancing protocols and strict sanitation rules, shopping for clothes just became a lot harder.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Problem of Size
&lt;/h2&gt;

&lt;p&gt;It's not like 2020 forced us to spin our own cloth. It was simple enough to order clothes online, but it created a whole new set of issues.&lt;/p&gt;

&lt;p&gt;Most of us have purchased something online. Electronics, household appliances, books or kitchenware. But amongst all, shopping for apparels is often the trickiest. It's pretty straightforward to purchase a smartphone, a laptop or some other gadget — you only need to worry if the specs and the design meet your needs, and whether it's got positive reviews. Ditto with books, handbags, or even jewellery.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But clothes? Reviews alone just won't cut it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You probably hate going for clothes shopping just as much as I do. It can be terribly tiresome. But you gotta admit that it's at-least somewhat un-ambiguous. In a clothing store, picking something you like is often the hardest part. But once you do find something, things get simple. Grab your size, try it on, check yourself out.&lt;/p&gt;

&lt;p&gt;With online shopping however, everything gets flipped! earching for just the right thing is easy — thanks to the ever-improving search features from all the mainstream e-commerce platforms. &lt;strong&gt;But getting the right fit?&lt;/strong&gt; It's not as easy as it looks.&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%2F2n6z5rw44pnlltdi43os.jpeg" 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%2F2n6z5rw44pnlltdi43os.jpeg" alt="Funny picture showing what a clothing looks like on a mannequin vs what it looks like when you wear it" width="800" height="755"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's no wonder that the average return rates for online apparel shopping is a whopping 32%, and it &lt;em&gt;burns a hole in the pocket&lt;/em&gt; for several e-commerce retailers. Here are some &lt;em&gt;wrinkle-inducing&lt;/em&gt; stats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Almost &lt;strong&gt;1&lt;/strong&gt; out every &lt;strong&gt;3&lt;/strong&gt; apparels purchased online get returned.&lt;/li&gt;
&lt;li&gt;In 2019, about $260 Billion worth of retail goods were returned, out of which &lt;strong&gt;$30 Billion&lt;/strong&gt; were from apparels.&lt;/li&gt;
&lt;li&gt;The cost of returns is estimated to be nearly $1.5 Billion. The cost of each return is between $5-$15.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What are the top 3 reasons for people returning apparels bought online?
&lt;/h3&gt;

&lt;p&gt;Here are some stats that'll create &lt;em&gt;creases&lt;/em&gt;... on your forehead, that is! -&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Size misfits&lt;/strong&gt; is the biggest factor, making &lt;strong&gt;43%&lt;/strong&gt; of total online shoppers citing this as the reason.&lt;/li&gt;
&lt;li&gt;Quality and color mismatch makes up for &lt;strong&gt;29%&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;22%&lt;/strong&gt; of shoppers feel the need to simulate a trial room at home.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Despite these stats, online shopping isn't going away. More and more users are shopping online post-COVID:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;27% of customers switched from offline to online due to COVID-19 in USA.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;12% of customers would prefer to continue purchasing online even after the COVID.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So needless to say, online shopping is here to stay. And that includes apparel shopping as well, which brings with it with the &lt;strong&gt;size-mismatch problem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The size-mismatch problem contributes to $1 Billion returns annually and affects inventory planning, leading to heavy losses for e-retailers.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Size-mismatch is not a mere wardrobe malfunction, it is a huge-sized problem!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At &lt;a href="https://egen.solutions/" rel="noopener noreferrer"&gt;Egen&lt;/a&gt;, we decided to iron this one out.&lt;/p&gt;

&lt;h2&gt;
  
  
  SmartTry
&lt;/h2&gt;

&lt;p&gt;Before the e-commerce era began, how did someone know if a certain clothing fit them?&lt;/p&gt;

&lt;p&gt;The solution was simple. If you were buying a new shirt, you'd simply compare it with another shirt that you already possess — one that was already the best fit for you. The best part is, you wouldn't even need to try the shirt on in a fitting room, and it'd still be a great fit for you most of the time!&lt;/p&gt;

&lt;p&gt;That's what SmartTry does — it enables users to compare their existing best fit clothes to new ones they want to purchase online.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Simplicity, carried to an an extreme, becomes elegance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  How does SmartTry work?
&lt;/h3&gt;

&lt;p&gt;Say you're trying to buy a T-shirt online from Amazon, or from mango.com like the below screenshot -&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%2Fuaqozu0o457f6x52bn10.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%2Fuaqozu0o457f6x52bn10.png" alt="Compare with SmartTry plugin" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You find the ultimate white T-shirt for the ultimate in effortless cool in your next Zoom meeting. But you've never bought a T-shirt from Mango before, so how will you know how it fits. (We'll spare you the rant on inconsistent sizing. You know it. We all know it.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What do you do? You don't wanna go through the whole ordeal of having to return it if it's the wrong fit!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Precisely the pain point for you and many other people. And here's where SmartTry's user experience is guaranteed to &lt;em&gt;knock your socks off&lt;/em&gt;!&lt;/p&gt;

&lt;h4&gt;
  
  
  Features
&lt;/h4&gt;

&lt;p&gt;Clicking on the green SmartTry button opens the SmartTry plugin in a popup on your screen:&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%2Fqohcu7zngi7gvhc9lt1w.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%2Fqohcu7zngi7gvhc9lt1w.png" alt="Screenshot of the SmartTry plugin" width="800" height="825"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The SmartTry plugin shows you the T-shirt or the outfit that you want to buy, along with your previous best fits for you to compare.&lt;/li&gt;
&lt;li&gt;Clicking on the &lt;em&gt;Add a new apparel to Compare&lt;/em&gt; icon lets you upload a picture of an outfit that you currently own, into the SmartTry platform . You'll use this to upload a photo of the T-Shirt that is your current best-fit.&lt;/li&gt;
&lt;li&gt;On uploading an apparel, it gets added to the &lt;em&gt;Recommended Items&lt;/em&gt; section and to your &lt;em&gt;Collection&lt;/em&gt;, with a button underneath that says &lt;em&gt;Compare&lt;/em&gt;. Clicking on the &lt;em&gt;Collection&lt;/em&gt; tab on the left will also show you all your previous uploads neatly organized within categories:&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%2F34280onapopnca387n1j.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%2F34280onapopnca387n1j.png" alt="Uploaded collections in SmartTry" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, when you click on &lt;em&gt;Compare&lt;/em&gt;, SmartTry runs its magic in the background and shows you how good of a fit your selection is!&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%2F61xvsaw8ejrhqfw0zs6c.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%2F61xvsaw8ejrhqfw0zs6c.png" alt="Screenshot of SmartTry showing options to compare clothing measurements with your best fit" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The blue T-shirt border represents your current best-fit that you used for the comparison, while the black border is the T-shirt that you want to buy.&lt;/li&gt;
&lt;li&gt;On the right, you'll be able to see precise measurements for the T-shirt you selected and your current best-fit. This helps you easily compare the differences between the two. (Let's face it. Every site has a size chart. No one is whipping out a measuring tape before clicking add to cart. Problem solved.)&lt;/li&gt;
&lt;li&gt;In this example, the &lt;em&gt;Small&lt;/em&gt; size seems a little too small — a &lt;em&gt;67% match&lt;/em&gt;. But not to worry! Since you like the T-shirt so much, you can switch between different sizes:&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%2F8pby2gx01u8akcgibpjo.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%2F8pby2gx01u8akcgibpjo.png" alt="Screenshot of the SmartTry app showing a Medium size fit" width="800" height="451"&gt;&lt;/a&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%2Fcubvp7kwv77gskxoinno.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%2Fcubvp7kwv77gskxoinno.png" alt="Screenshot of the SmartTry app showing a Large size fit" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Medium size is a 94% fit.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can now buy the outfit with the right size, knowing fully well that it'll fit you to a T!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Life ain't perfect, but your outfit damn well can be!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Unmasking the Magic
&lt;/h2&gt;

&lt;p&gt;If you're a programmer, I'm sure you're wondering by now — &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How does SmartTry really work behind the scenes, and how do you build it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well &lt;em&gt;roll up your sleeves&lt;/em&gt;, because we put together a tutorial for you to follow along!&lt;/p&gt;

&lt;h3&gt;
  
  
  What you will learn
&lt;/h3&gt;

&lt;p&gt;Whatever the mode of user-interaction may be, the power of SmartTry comes from &lt;strong&gt;Computer Vision&lt;/strong&gt; techniques, that allows it to compare two different outfits and analyze the difference in their measurements. Over the next several minutes, I'll walk you through how to build this algorithm.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You'll use &lt;a href="https://opencv.org/" rel="noopener noreferrer"&gt;OpenCV&lt;/a&gt;, one of the most popular and widely used computer vision and machine learning libraries.&lt;/li&gt;
&lt;li&gt;For writing code, you'll use a &lt;a href="https://jupyter.org/" rel="noopener noreferrer"&gt;Jupyter-Notebook&lt;/a&gt;, an open-source web application that allows you to create and share documents that contain live code, equations, visualizations and narrative text.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A word to the wise
&lt;/h3&gt;

&lt;p&gt;Some knowledge of Python is essential in-order to understand this tutorial. If you haven't worked on Python before, I highly recommend this free course on &lt;a href="https://in.udacity.com/course/introduction-to-python--ud1110" rel="noopener noreferrer"&gt;Introduction to Python&lt;/a&gt;, before proceeding with the rest of this article.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Python?&lt;/strong&gt; Because it is fairly easy to learn, and the Python ecosystem has a lot of tools and libraries for computer-vision, machine learning, and more. Once you've built the algorithm, Python also has several web-frameworks that allow you to build a service around this feature.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tutorial
&lt;/h3&gt;

&lt;p&gt;Go to this &lt;a href="https://github.com/booleanhunter-tech-blog/smarttry" rel="noopener noreferrer"&gt;github link&lt;/a&gt;, and follow the instructions to download and install the the project dependencies. Then, run jupyter-lab in your project root and create a new Python3.x notebook file. Next,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Head over to the &lt;a href="https://booleanhunter-tech-blog.github.io/smarttry/" rel="noopener noreferrer"&gt;Jupyter tutorial&lt;/a&gt; and follow the steps outlined, to build your very own program that calculates accurate measurements for a given T-Shirt!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Alternatively&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Head over to the below &lt;em&gt;Binder&lt;/em&gt; link and execute the code right within your browser without the hassles of installation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mybinder.org/v2/gh/booleanhunter-tech-blog/smarttry/master" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmybinder.org%2Fbadge_logo.svg" alt="Binder" width="109" height="20"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;smarttry.ipynb&lt;/em&gt; notebook has the complete source code with instructions for you to read. You could choose to run this directly, or write code on your own in a new notebook file as you follow the tutorial.&lt;/p&gt;

&lt;h4&gt;
  
  
  Slides
&lt;/h4&gt;

&lt;p&gt;If you prefer taking just a quick peek into the logic, here's a short slide-deck:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://www.slideshare.net/slideshow/embed_code/key/wfxcmA215tJ8E8" alt="wfxcmA215tJ8E8 on slideshare.net" width="100%" height="487"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;So go ahead, put on your thinking cap and have fun!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first version of SmartTry that we developed at Egen achieved an accuracy of &lt;strong&gt;91.7%&lt;/strong&gt; for clothing measurements! Hopefully, you'll be able to use this as a performance benchmark when following the tutorial and writing the program. I'm curious to know what your accuracy score looks like!&lt;/p&gt;

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

&lt;p&gt;Once you've developed your program, you can integrate this into a web-service and expose it via an API. Here's the architecture implementation that we designed at Egen:&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%2Fkohnyxyp7i3tdyltn38y.jpeg" 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%2Fkohnyxyp7i3tdyltn38y.jpeg" alt="SmartTry architecture" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It doesn't stop here! The future scope for such a feature is immense:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On websites, SmartTry can be easily integrated into any existing e-commerce platform — as a popup, or an installable plugin, or a platform integration, or even as a browser extension.&lt;/li&gt;
&lt;li&gt;Integrate SmartTry into mobile applications, which will allow you to upload photos of your outfit more easily.&lt;/li&gt;
&lt;li&gt;You could maintain a common SmartTry profile that'll exist across multiple e-commerce websites.&lt;/li&gt;
&lt;li&gt;A history page to quickly review past comparisons.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and more!&lt;/p&gt;

&lt;h3&gt;
  
  
  Potential Revenue Models
&lt;/h3&gt;

&lt;p&gt;Being an intermediary between different parties that interact with a product is a model that a lot of businesses are exploring in recent times. SmartTry being designed as an easy-to-add plugin with a high accuracy makes it a really good contender for adoption in new businesses and existing e-commerce platforms. Here are some revenue models that can be quite viable (although they're by no means an exhaustive list):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At-cost SaaS model for online shopping. The pricing could be based on the number of API calls made to the SmartTry platform, for example — $0.001 per 1 API call.&lt;/li&gt;
&lt;li&gt;Size and shopping data of users and their different demographics&lt;/li&gt;
&lt;li&gt;Sending traffic from our platform to "preferred" e-commerce websites&lt;/li&gt;
&lt;li&gt;In-store enterprise yearly licensing model&lt;/li&gt;
&lt;li&gt;A freemium model with basic features accessible for free, and upgrades or more advanced features available at a monthly subscription.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;What is right is often forgotten by what is convenient. And evidently so, it's true in the world of fashion e-commerce — with the potential of nearly &lt;strong&gt;$1 Billion&lt;/strong&gt; USD in cost savings and returns, if done the right way! Reduced apparel returns would also translate into reduced carbon emissions, logistics and waste — thus helping the planet be clothed in green!&lt;/p&gt;

&lt;h3&gt;
  
  
  Credits
&lt;/h3&gt;

&lt;p&gt;All credits for developing the idea and concept goes to the SmartTry team at &lt;a href="http://egen.solutions/" rel="noopener noreferrer"&gt;Egen&lt;/a&gt;, consisting of &lt;em&gt;Arif&lt;/em&gt;, &lt;em&gt;Gagan&lt;/em&gt;, &lt;em&gt;Anmol&lt;/em&gt; and &lt;em&gt;Aatman&lt;/em&gt;. 🙌🏼&lt;/p&gt;

</description>
      <category>programming</category>
      <category>tutorial</category>
      <category>python</category>
      <category>ux</category>
    </item>
    <item>
      <title>Build a community-driven delivery app using Django, PostgreSQL &amp; JavaScript - Part 2</title>
      <dc:creator>Ashwin Hariharan</dc:creator>
      <pubDate>Tue, 01 Feb 2022 12:03:19 +0000</pubDate>
      <link>https://dev.to/egeninc/build-a-community-driven-delivery-app-using-django-postgresql-javascript-part-2-jod</link>
      <guid>https://dev.to/egeninc/build-a-community-driven-delivery-app-using-django-postgresql-javascript-part-2-jod</guid>
      <description>&lt;p&gt;Welcome to the 2nd part of this series on using technology to foster sustainability in your community! In this tutorial, you'll continue building &lt;strong&gt;Kartpool&lt;/strong&gt; — a community driven delivery platform for the ones who need it the most!&lt;/p&gt;

&lt;p&gt;Be sure to read &lt;a href="https://dev.to/egeninc/thinking-of-building-a-contact-tracing-application-what-to-do-instead-4pd3"&gt;Part 1&lt;/a&gt; of the tutorial series thoroughly and complete the exercises before you proceed with this tutorial!&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Problems in traditional delivery models&lt;/li&gt;
&lt;li&gt;Local Search and Discovery platforms&lt;/li&gt;
&lt;li&gt;
Kartpool app features

&lt;ul&gt;
&lt;li&gt;Advantages&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Engineering the app

&lt;ul&gt;
&lt;li&gt;Django&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Feature #1: Adding a wishlist&lt;/li&gt;
&lt;li&gt;Feature #2: Listing nearby wishlists&lt;/li&gt;
&lt;li&gt;Feature #3: Store navigation and info&lt;/li&gt;
&lt;li&gt;Feature #4: Updating a wishlist&lt;/li&gt;
&lt;li&gt;Next steps&lt;/li&gt;
&lt;li&gt;Source code&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;To recap, here's the list of features:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature #1:&lt;/strong&gt; A location-based store discovery service from where users could buy grocery and other essentials. You already built this in &lt;a href="https://dev.to/egeninc/thinking-of-building-a-contact-tracing-application-what-to-do-instead-4pd3"&gt;Part 1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature #2:&lt;/strong&gt; Users can select a store and add a &lt;strong&gt;wishlist&lt;/strong&gt; of essentials that they intend to buy. This wishlist would be visible to other residents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature #3:&lt;/strong&gt; Any other resident can choose to accept this person's request and become a &lt;strong&gt;wishmaster&lt;/strong&gt;. Then, they can purchase the items from the store on behalf of the requestor and deliver it to them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature #4:&lt;/strong&gt; Users can give &lt;strong&gt;karma points&lt;/strong&gt; to runners via a recognition and appreciation system, for being good samaritans and helpful members of the community.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sounds similar to online grocery delivery platforms right? What's so different about this one, when so many others are already out there?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fair question indeed! Let's look at some problems in the existing delivery business models, and what your platform will help solve:&lt;/p&gt;

&lt;h2&gt;
  
  
  Problems in traditional delivery models
&lt;/h2&gt;

&lt;p&gt;You may already be aware of several retail delivery platforms out there. &lt;a href="https://www.walmart.com/grocery/" rel="noopener noreferrer"&gt;&lt;strong&gt;Walmart&lt;/strong&gt;&lt;/a&gt;, founded in 1962, operates a multinational chain of hypermarkets, grocery stores and discount department stores along with home delivery, and is arguably the largest retailer in the US in terms of revenue.&lt;/p&gt;

&lt;p&gt;In June 2017, &lt;strong&gt;Amazon&lt;/strong&gt; acquired &lt;strong&gt;Whole Foods&lt;/strong&gt; for $13.7 Billion USD and amped up their retail delivery offerings as well. There's also &lt;a href="https://www.instacart.com" rel="noopener noreferrer"&gt;&lt;strong&gt;Instacart&lt;/strong&gt;&lt;/a&gt; — another grocery delivery and pick-up service in Canada and USA. Despite losing Whole Foods as a customer, Instacart holds a whopping &lt;a href="https://progressivegrocer.com/how-instacart-has-outsmarted-amazon" rel="noopener noreferrer"&gt;59%&lt;/a&gt; of the delivery market. And &lt;strong&gt;Kroger,&lt;/strong&gt; another American retail company, is the second largest retailer in the United States, just behind Walmart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;These developments in the retail and delivery sectors have had various &lt;a href="https://www.digitalcommerce360.com/2020/06/12/why-independent-grocers-should-avoid-instacart-and-similar-services/" rel="noopener noreferrer"&gt;impacts on local businesses&lt;/a&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;While these platforms offer convenience, there can be challenges in ensuring a consistently positive experience for customers who shop at local stores.&lt;/li&gt;
&lt;li&gt;All these platforms have also been at the center of a large number of controversies and lawsuits on issues involving low wages, poor working conditions, treatment of suppliers, and waste management. When local businesses are on-boarded onto these larger platforms, any bad press coverages and negative consequences tend to spill over into your store's reputation and reviews as well, for probably no fault of their own.&lt;/li&gt;
&lt;li&gt;Large companies slowly end up transforming into a monopoly — taking over smaller businesses and becoming the sole retailer and distribution chain in the area. Eventually your local businesses become very dependent on these platforms, which is a bad idea.&lt;/li&gt;
&lt;li&gt;There are labor costs, and service and delivery charges associated while utilizing larger monopolized platforms. Due to these, businesses would make lesser profits than they did if they were to sell the items directly. In-order to maintain their current profits or to grow, they would inevitably need to raise the prices of items — once again, bad news for both customers and grocers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It seems like there are a lot of opportunities for innovation in the delivery model. Time for a fresh new approach!&lt;/p&gt;

&lt;h2&gt;
  
  
  Local Search and Discovery Platforms
&lt;/h2&gt;

&lt;p&gt;In the previous part, you learned to build a store discovery service that fetches all nearby stores in your neighborhood and displays them on a map.&lt;/p&gt;

&lt;p&gt;Over the last decade, local search-and-discovery applications have been seeing a steady rise in usage and popularity. In 2009, &lt;strong&gt;Foursquare&lt;/strong&gt; — a platform of nearly 50 million users — launched a platform that let users search for restaurants, nightlife spots, shops and other places in a location. In 2012, Facebook launched &lt;strong&gt;Nearby&lt;/strong&gt;, Foursquare's competitor that pretty much did the same thing. And in 2017, &lt;strong&gt;Google Maps&lt;/strong&gt; announced a similar feature that let users create lists of their favorite places to visit.&lt;/p&gt;

&lt;p&gt;When you look at the user interfaces in several of these platforms, you observe a lot of similarities — especially on the layout on the home page that shows the places of interest:&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%2Fg7xrhgdtrwupsowc4tv3.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%2Fg7xrhgdtrwupsowc4tv3.png" alt="Screenshot of Foursquare's City guide" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Indeed, if you look at &lt;a href="https://foursquare.com/explore?mode=url&amp;amp;ne=41.998029%2C-87.505589&amp;amp;q=Shopping&amp;amp;sw=41.77464%2C-87.902126" rel="noopener noreferrer"&gt;Foursquare's city guide&lt;/a&gt; — the user-interface consists of a small column on the left that displays a list of areas of interest, along with their locations to the right on a wide map. &lt;a href="https://www.google.com/maps/search/shops/@41.9698117,-87.6921986,15z" rel="noopener noreferrer"&gt;Google Maps&lt;/a&gt; also has a similar interface:&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%2Ftdg73i8any55ednj8vf5.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%2Ftdg73i8any55ednj8vf5.png" alt="Screenshot of Google Maps" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here's AirBnb:&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%2F8spmrmy110h14b9b6tzy.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%2F8spmrmy110h14b9b6tzy.png" alt="Screenshot of AirBnb app" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking on one of the items on the left makes the map fly to the associated location and zoom in to the marker icon. Sometimes, it also shows a popup on the marker with some useful information.&lt;/p&gt;

&lt;p&gt;So needless to say, these user-interfaces are in vogue because its convenient to navigate through the list on the left, and look at their associated locations on the right on the map.&lt;/p&gt;

&lt;p&gt;Deriving lessons from both the online grocery delivery models and local search and discovery applications, this platform that you'll build might just be what your community needs!&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%2Fphm5uget2xndawqxtx3b.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%2Fphm5uget2xndawqxtx3b.png" alt="Kartpool app screenshot" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

Kartpool app screenshot - what you'll build!




&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;p&gt;On the right-side you have a map where you'll type in the name of a location, which then displays stores in the area. You already did this in the previous tutorial.&lt;/p&gt;

&lt;p&gt;The left column is a bit different — unlike Foursquare or Google Maps, you won't be displaying stores here, but &lt;strong&gt;wishlists.&lt;/strong&gt; Clicking on one of the wishlist cards will make the map "fly" to the location of the store, where the items can be purchased from. These list of cards are arranged across 3 different tabs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;em&gt;1st tab&lt;/em&gt; displays all &lt;strong&gt;nearby wishlists&lt;/strong&gt; created by users in the neighborhood. From here, you can accept a wishlist and it will be assigned to you to collect from a store nearby.&lt;/li&gt;
&lt;li&gt;Wishlists created by you will be visible on the &lt;em&gt;2nd tab&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;3rd tab&lt;/em&gt; shows wishlists that you accept from the 1st tab. If you mark a wishlist as &lt;em&gt;accepted&lt;/em&gt;, you become a &lt;em&gt;wishmaster&lt;/em&gt; for that user and it gets added to your trips. You can then make a trip to the store to purchase the items, and mark them as &lt;em&gt;fulfilled&lt;/em&gt; once your neighbor receives them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To &lt;strong&gt;create a wishlist&lt;/strong&gt;, you'll select a store icon from the map and add the items that you need by using the input-field on the bottom left.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How are these features useful?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;p&gt;While most of the year 2020 was spent in lockdowns and quarantines, it also revealed many &lt;a href="https://nbc16.com/news/local/teens-organize-to-help-deliver-groceries-for-seniors-in-roseburg" rel="noopener noreferrer"&gt;heart-warming&lt;/a&gt; &lt;a href="https://shoppingangelsglobal.org/" rel="noopener noreferrer"&gt;examples&lt;/a&gt; of how powerful organized efforts and informed choices of individuals within a community can be.&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%2Fm5knnvi50kt9bf5lybhf.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%2Fm5knnvi50kt9bf5lybhf.png" alt="Examples of community spirit during a crisis" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Providing a digital tool that leverages this power can create an immensely positive social and economic impact:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You could foster virtually an endless shopping experience focused exclusively on local stores and businesses.&lt;/li&gt;
&lt;li&gt;User on-boarding becomes simpler.&lt;/li&gt;
&lt;li&gt;Enable massive reduction in delivery/service fees.&lt;/li&gt;
&lt;li&gt;The business model is socially driven and community-driven, which will foster a feeling of togetherness and readiness to help those in need.&lt;/li&gt;
&lt;li&gt;Not having to rely on middlemen and eliminating needless logistics and packaging would translate into drastic reductions in pollution and consumer waste, thus helping the planet stay green.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you're excited. &lt;strong&gt;Let's begin!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Engineering
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Django
&lt;/h3&gt;

&lt;p&gt;A Django project consists of one or more &lt;a href="https://docs.djangoproject.com/en/dev/ref/applications/#projects-and-applications" rel="noopener noreferrer"&gt;&lt;em&gt;applications&lt;/em&gt;&lt;/a&gt;. At the moment, your project root directory contains two applications — &lt;em&gt;stores&lt;/em&gt; and &lt;em&gt;home&lt;/em&gt;. An application encapsulates a set of related features along with its own models, views, serializers and business logic.&lt;/p&gt;

&lt;p&gt;It is useful to group your project logic in this way as it offers a lot of advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It gives you much better organization and structure of your project, and allows you to maintain separation of concerns.&lt;/li&gt;
&lt;li&gt;Flexible development — one developer could chose to work on features related to &lt;em&gt;stores,&lt;/em&gt; while another could chose to work on the &lt;em&gt;wishlists&lt;/em&gt; feature.&lt;/li&gt;
&lt;li&gt;Re-usability — you can easily reuse an app and migrate it to another project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So in your current project, everything that is related to stores is in the &lt;em&gt;stores&lt;/em&gt; directory, and everything related to rendering the home page is in the &lt;em&gt;home&lt;/em&gt; directory. Similarly, you'll create a new Django app for the &lt;em&gt;wishlists&lt;/em&gt; feature. In your terminal type &lt;code&gt;python manage.py startapp wishlists&lt;/code&gt;. This will create a new directory &lt;em&gt;wishlists&lt;/em&gt; with its structure similar to the &lt;em&gt;stores&lt;/em&gt; directory.&lt;/p&gt;

&lt;h4&gt;
  
  
  Wishlists
&lt;/h4&gt;

&lt;h5&gt;
  
  
  Step #1: Create the database model for storing wishlists
&lt;/h5&gt;

&lt;p&gt;Open &lt;em&gt;wishlists/model.py&lt;/em&gt; and add the following code:&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;from&lt;/span&gt; &lt;span class="n"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.contrib.postgres.fields&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ArrayField&lt;/span&gt;

&lt;span class="c1"&gt;# Create your models here.
&lt;/span&gt;
&lt;span class="n"&gt;WISHLIST_STATUSES&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PENDING&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PENDING&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ACCEPTED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ACCEPTED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FULFILLED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FULFILLED&lt;/span&gt;&lt;span class="sh"&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;class&lt;/span&gt; &lt;span class="nc"&gt;Wishlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auto_now_add&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
    &lt;span class="n"&gt;buyer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&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="n"&gt;wishmaster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&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="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ArrayField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&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="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
        &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;WISHLIST_STATUSES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PENDING&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&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;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stores.Store&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="n"&gt;related_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wishlists&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="n"&gt;on_delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SET_NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&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;Each wishlist can have one of three statuses, with the default &lt;code&gt;status&lt;/code&gt; being &lt;code&gt;PENDING&lt;/code&gt;at the time of creation.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;buyer&lt;/code&gt;is that user who creates the wishlist, while the &lt;code&gt;wishmaster&lt;/code&gt;is the user that makes the trip to the store and collects the items on behalf of the buyer.&lt;/li&gt;
&lt;li&gt;Each wishlist also has a foreign key that's associated with a valid store-id from the &lt;code&gt;stores&lt;/code&gt; model that you implemented in the previous tutorial.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you'll run &lt;code&gt;python manage.py makemigrations&lt;/code&gt; followed by &lt;code&gt;python manage.py migrate&lt;/code&gt; . Django's ORM will create the table with the defined schema in the database!&lt;/p&gt;

&lt;h5&gt;
  
  
  Step #2: Add a serializer
&lt;/h5&gt;

&lt;p&gt;In &lt;em&gt;wishlists/serializers.py&lt;/em&gt;, add the following:&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;from&lt;/span&gt; &lt;span class="n"&gt;rest_framework&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;serializers&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Wishlist&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WishlistSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelSerializer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
        &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Wishlist&lt;/span&gt;  
        &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;created_at&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;buyer&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wishmaster&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;store&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;  
        &lt;span class="p"&gt;]&lt;/span&gt;  

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

&lt;/div&gt;



&lt;h5&gt;
  
  
  Step #3: Define the View Class
&lt;/h5&gt;

&lt;p&gt;Add the following in &lt;em&gt;wishlists/views.py&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rest_framework&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;viewsets&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rest_framework.response&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Responsefrom&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Wishlist&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.serializers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WishlistSerializer&lt;/span&gt;

&lt;span class="c1"&gt;# Create your views here.
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WishlistView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewsets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelViewSet&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
    &lt;span class="n"&gt;queryset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Wishlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
    &lt;span class="n"&gt;serializer_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WishlistSerializer&lt;/span&gt;  

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

&lt;/div&gt;



&lt;p&gt;You'll add the controller logic for creating, listing and updating wishlists within this class.&lt;/p&gt;

&lt;h5&gt;
  
  
  Step #4: Define the API service
&lt;/h5&gt;

&lt;p&gt;Add the URL for your wishlists service in &lt;em&gt;kartpool/urls.py&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;wishlists&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;wishlists_viewsrouter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wishlists&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wishlists_views&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WishlistView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;basename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wishlists&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any request made to the endpoint &lt;em&gt;/wishlists/&lt;/em&gt; will execute the relevant controller within your &lt;code&gt;WishlistView&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;Now you're ready to begin developing the wishlist feature for your app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Some helper methods have already been provided for you in the code, so that you may devote most of your time towards writing the core logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;helpers.js&lt;/strong&gt;: Contains methods to render wishlists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;api.js&lt;/strong&gt;: Has functions for making network requests to the &lt;code&gt;/stores/&lt;/code&gt; and &lt;code&gt;/wishlists/&lt;/code&gt; endpoints.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Feature #1: Adding a Wishlist
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Backend
&lt;/h3&gt;

&lt;p&gt;Create a new file &lt;em&gt;services.py&lt;/em&gt; in the &lt;em&gt;wishlists&lt;/em&gt; directory.&lt;/p&gt;

&lt;p&gt;Here, you'll write a function that takes in 3 arguments — a &lt;code&gt;buyer&lt;/code&gt;, an &lt;code&gt;items&lt;/code&gt; array, and a &lt;code&gt;store&lt;/code&gt;. This function will create a new &lt;code&gt;Wishlist&lt;/code&gt;, save it in the table, and return it.&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;from&lt;/span&gt; &lt;span class="n"&gt;django.core.exceptions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ObjectDoesNotExist&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Wishlist&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;stores.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Store&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_wishlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buyer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
    &lt;span class="n"&gt;wishlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Wishlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
        &lt;span class="n"&gt;buyer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;buyer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="n"&gt;store_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;  
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;wishlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wishlist&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, you'll import this function in &lt;em&gt;wishlist/views.py&lt;/em&gt; and add the controller logic in the &lt;code&gt;WishlistView&lt;/code&gt;class.&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
    &lt;span class="n"&gt;buyer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;buyer&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
    &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
    &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;store&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;wishlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_wishlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buyer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
    &lt;span class="n"&gt;wishlist_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WishlistSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wishlist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;many&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wishlist_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When when someone makes a POST request to the &lt;em&gt;/wishlists/&lt;/em&gt; endpoint, it'll run the &lt;code&gt;create&lt;/code&gt; method, extract the values for the &lt;code&gt;buyer&lt;/code&gt;, &lt;code&gt;items&lt;/code&gt; and the &lt;code&gt;store&lt;/code&gt; id, and pass them to &lt;code&gt;create_wishlist&lt;/code&gt; to create a new wishlist in the db.&lt;/p&gt;

&lt;h3&gt;
  
  
  Front-end
&lt;/h3&gt;

&lt;p&gt;In order to add a wishlist on the front-end, you'll need to click on a store marker on the map, and add items on the input-box&lt;code&gt;#wishlist-items&lt;/code&gt; separated by commas. Then when you click on the "&lt;em&gt;Add a wishlist&lt;/em&gt;" button, it will make a POSt request to &lt;em&gt;/wishlists/&lt;/em&gt; with the required data.&lt;/p&gt;

&lt;p&gt;Open &lt;em&gt;wishlists.js&lt;/em&gt; and add the following:&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createWishlist&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;wishlistInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wishlist-items&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;trim&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;USERNAME&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;SELECTED_sTORE_ID&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;wishlistInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="nf"&gt;addWishlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;USERNAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;wishlistInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;STORE&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 function extracts the value from the input-field, converts it into an array, and passes these values to the method &lt;code&gt;addWishlist&lt;/code&gt;, which will make the &lt;em&gt;POST&lt;/em&gt; request to add the wishlist into the database!&lt;/p&gt;

&lt;p&gt;You'll now need to run this function upon clicking the &lt;em&gt;Add a wishlist&lt;/em&gt; button. Let's define the event handler for this in &lt;em&gt;index.js&lt;/em&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;add-wishlist&lt;/span&gt;&lt;span class="dl"&gt;"&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="kd"&gt;function&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="p"&gt;{&lt;/span&gt;  
    &lt;span class="nf"&gt;createWishlist&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;Run &lt;code&gt;python manage.py runserver&lt;/code&gt; and head over to localhost:8000/?username=YOURNAME. Try adding your first wishlist and some sample wishlists for a few other users as well. You should be able to see them in your database.&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%2Fue68jwpmdae7s7bwxl4t.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%2Fue68jwpmdae7s7bwxl4t.png" alt="Screenshot of my psql terminal" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, you'll build the service to fetch nearby wishlists and display them in the UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature #2: Listing nearby wishlists
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Backend
&lt;/h3&gt;

&lt;p&gt;For retriving nearby wishlists, you'll define a function &lt;code&gt;get_wishlists&lt;/code&gt; in &lt;em&gt;wishlists/services.py&lt;/em&gt;, that accepts 3 arguments — a &lt;code&gt;latitude&lt;/code&gt;, a &lt;code&gt;longitude&lt;/code&gt;, and an optional &lt;code&gt;options&lt;/code&gt;dictionary.&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;from&lt;/span&gt; &lt;span class="n"&gt;stores.services&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_nearby_stores_within&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_wishlists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Wishlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
        &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="n"&gt;store__in&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;get_nearby_stores_within&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
            &lt;span class="n"&gt;latitude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
            &lt;span class="n"&gt;longitude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
            &lt;span class="n"&gt;km&lt;/span&gt;&lt;span class="o"&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;limit&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="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;created_at&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;  
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the &lt;code&gt;get_nearby_stores_within&lt;/code&gt; function that you wrote in Part 1, we can use the foreign key &lt;code&gt;store&lt;/code&gt; and retrieve only those wishlists for which their associated stores are near the given pair of coordinates. That way in the UI, you'll never have a wishlist for which its store isn't visible on the map! &lt;em&gt;Makes sense?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;get_wishlists&lt;/code&gt; method, you can retrieve the required data for all 3 tabs for the left column using the &lt;code&gt;options&lt;/code&gt; argument:&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%2Fybmr4wdehzo7j8492c02.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%2Fybmr4wdehzo7j8492c02.png" alt="Screenshot of the Wishlists tab" width="517" height="96"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you wish to return your own requests, you just need to retrieve those wishlists for which you're the &lt;em&gt;buyer&lt;/em&gt;. So you'd pass in &lt;code&gt;{buyer=ashwin}&lt;/code&gt; in the &lt;code&gt;options&lt;/code&gt; argument.&lt;/li&gt;
&lt;li&gt;Likewise, for retrieving your trips, you'll just need to retrieve those wishlists for which you're the &lt;em&gt;wishmaster&lt;/em&gt;, by providing &lt;code&gt;{wishmaster=ashwin}&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, you'll import the above function and add the controller logic in &lt;em&gt;wishlists/views.py&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;latitude&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_params&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;lat&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;longitude&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_params&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;lng&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;buyer&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wishmaster&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_params&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="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;

    &lt;span class="n"&gt;wishlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_wishlists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;wishlist_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WishlistSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wishlist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;many&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wishlist_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Frontend
&lt;/h3&gt;

&lt;p&gt;Inside &lt;em&gt;wishlists.js&lt;/em&gt;, you'll have 3 functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;displayNearbyWishlists&lt;/code&gt;: To show all nearby wishlists in the 1st tab.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;displayMyRequests&lt;/code&gt;: To show wishlists that you created in the 2nd tab.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;displayMyTrips&lt;/code&gt;: To show the wishlists that you accepted in the 3rd Tab.
&lt;/li&gt;
&lt;/ul&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;displayNearbyWishlists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nearbyWishlists&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;fetchNearbyWishlists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;renderWishlists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nearby-wishlists&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nearbyWishlists&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;fetchNearbyWishlists&lt;/code&gt; makes an HTTP &lt;em&gt;GET&lt;/em&gt; request with the given pair of coordinates to the endpoint &lt;code&gt;/wishlists/&lt;/code&gt;. Once the wishlists are fetched, you'll render it inside the tab section with the id &lt;code&gt;nearby-wishlists&lt;/code&gt;, using the helper method &lt;code&gt;renderWishlists&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Likewise, add the other two functions as well:&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;displayMyRequests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myWishlists&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;fetchNearbyWishlists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;buyer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;USERNAME&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;  
        &lt;span class="nf"&gt;renderWishlists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-wishlists&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myWishlists&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
    &lt;span class="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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;displayMyTrips&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myTrips&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;fetchNearbyWishlists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;wishmaster&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;USERNAME&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;  
        &lt;span class="nf"&gt;renderWishlists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-trips&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myTrips&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
    &lt;span class="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;Refresh the page and try it out!&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature #3: Store Navigation and Info
&lt;/h2&gt;

&lt;p&gt;Displaying wishlists is great, but how do you know which store to collect it from?&lt;/p&gt;

&lt;p&gt;That's where the foreign key &lt;code&gt;store&lt;/code&gt; on our &lt;code&gt;Store&lt;/code&gt; model comes in handy, which is present in the JSON response when you make the request to fetch wishlists:&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%2Fpilw1krxfgygr6rwvsx2.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%2Fpilw1krxfgygr6rwvsx2.png" alt="Screenshot of Google Chrome network tab showing the request response JSON" width="800" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the DOM, each wishlist card has a data-attribute with the value of the associated store-id:&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%2F02rupvnrdfbltd0bg9rg.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%2F02rupvnrdfbltd0bg9rg.png" alt="Screenshot of Google Chrome Elements Inspector showing the attributes on the DOM element for a wishlist" width="800" height="581"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inside &lt;em&gt;stores.js&lt;/em&gt;, add a function &lt;code&gt;setStoreNavigation&lt;/code&gt; that takes in 2 arguments — &lt;code&gt;map&lt;/code&gt; and &lt;code&gt;storesGeoJson&lt;/code&gt; . The function will loop over all the wishlist elements and add a &lt;em&gt;click&lt;/em&gt; event listener on all of them. Upon a click,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetch the wishlist's associated store-id from the &lt;code&gt;data-store-id&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;Then using the store-id, find the relevant store's GeoJSON information (that also contains the latitude and longitude information) from &lt;code&gt;storesGeoJson&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Using the store's coordinates, you can now programmatically make &lt;em&gt;Mapbox&lt;/em&gt; zoom into the store's location.
&lt;/li&gt;
&lt;/ul&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setStoreNavigation&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;storesGeoJson&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;wishlistElements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementsByClassName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wishlist&lt;/span&gt;&lt;span class="dl"&gt;'&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;wishlistElements&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="nx"&gt;wishlistElements&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;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;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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;storeId&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="nx"&gt;currentTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-store-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;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;point&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;storesGeoJson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;features&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;storeId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;point&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&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="nf"&gt;flyToStore&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;point&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
                    &lt;span class="nf"&gt;displayStoreDetails&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;point&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
                    &lt;span class="nf"&gt;updateSelectedStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;storeId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
                    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
                &lt;span class="p"&gt;}&lt;/span&gt;  
            &lt;span class="p"&gt;}&lt;/span&gt;  
        &lt;span class="p"&gt;}&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, add the function &lt;code&gt;flyToStore&lt;/code&gt; that zooms the map into a given pair of coordinates within &lt;em&gt;map.js&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;flyToStore&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;point&lt;/span&gt;&lt;span class="p"&gt;)&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="nf"&gt;flyTo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;  
        &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;point&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="na"&gt;zoom&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Refresh the page, type in a location in which you created the wishlists in the previous step. Once the wishlists show up, click on one of them and watch the map zoom in to the store marker!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But we're not nearly done yet.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessibility
&lt;/h3&gt;

&lt;p&gt;In the previous tutorial, you added a &lt;code&gt;title&lt;/code&gt; attribute to each marker that shows you the store information when you hover your cursor over a store icon. Though it gets the job done, it isn't nearly good in terms of &lt;strong&gt;accessibility&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Along with flying to the store location, what would really be good is to also show a popup on the marker. Lucky for you, &lt;em&gt;Mapbox&lt;/em&gt; has a neat little API that does the job!&lt;/p&gt;

&lt;p&gt;Add the following function within &lt;em&gt;map.js&lt;/em&gt;, and call it inside &lt;code&gt;setStoreNavigation&lt;/code&gt;, right after you fly to the store:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;displayStoreDetails&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;point&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;popUps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementsByClassName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mapboxgl-popup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
    &lt;span class="cm"&gt;/** Check if there is already a popup on the map and if so, remove it */&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;popUps&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;popUps&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="nf"&gt;remove&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;popup&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;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Popup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;closeOnClick&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;setLngLat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;point&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`  
            &amp;lt;details&amp;gt;  
                &amp;lt;summary&amp;gt;&amp;lt;h2&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;point&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&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="s2"&gt;&amp;lt;/h2&amp;gt;&amp;lt;/summary&amp;gt;  
                &amp;lt;dl&amp;gt;  
                    &amp;lt;dt&amp;gt;Distance&amp;lt;/dt&amp;gt;  
                    &amp;lt;dd&amp;gt;Approximately &amp;lt;strong&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;point&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;distance&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="s2"&gt; km&amp;lt;/strong&amp;gt; away&amp;lt;/dd&amp;gt;

                    &amp;lt;dt&amp;gt;Address&amp;lt;/dt&amp;gt;  
                    &amp;lt;dd&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;point&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;N/A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/dd&amp;gt;

                    &amp;lt;dt&amp;gt;Phone&amp;lt;/dt&amp;gt;  
                    &amp;lt;dd&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;point&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;phone&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;N/A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/dd&amp;gt;

                    &amp;lt;dt&amp;gt;Rating&amp;lt;/dt&amp;gt;  
                    &amp;lt;dd&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;point&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rating&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;N/A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/dd&amp;gt;  
                &amp;lt;/dl&amp;gt;  
            &amp;lt;/details&amp;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;addTo&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;popup&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;Moving on to our final set of feature in this tutorial:&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature #4: Updating a wishlist
&lt;/h2&gt;

&lt;p&gt;So far, you've managed to build stuff that adds quite some oomph factor on the UI. But your app isn't usable yet.&lt;/p&gt;

&lt;p&gt;The real fun begins when a user can pick up one of the wishlists created by someone in the neighborhood. This is where the true value of the application lies — the community aspect that makes it possible for neighbors to help each other and be good samaritans during times of need!&lt;/p&gt;

&lt;p&gt;When a wishlist item is first created on the platform, it isn't assigned to any &lt;em&gt;wishmaster&lt;/em&gt; yet, and the default &lt;code&gt;status&lt;/code&gt; is set to &lt;code&gt;PENDING&lt;/code&gt;. So this is how the card looks like on the UI:&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%2Fhn4zopuiw3bb7x2ejg03.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%2Fhn4zopuiw3bb7x2ejg03.png" alt="Screenshot of a wishlist item on the Kartpool app in the pending status" width="608" height="204"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In-order to accept a wishlist:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on the little grey icon on the right-side of the card. This icon has a class value &lt;code&gt;accept&lt;/code&gt;in the DOM.&lt;/li&gt;
&lt;li&gt;Upon clicking the icon, the app will make a &lt;code&gt;PATCH&lt;/code&gt; request to the &lt;em&gt;/wishlists/&lt;/em&gt; endpoint.&lt;/li&gt;
&lt;li&gt;On the backend, update the wishlist item's &lt;code&gt;status&lt;/code&gt; to &lt;code&gt;ACCEPTED&lt;/code&gt;, and also update the &lt;code&gt;wishmaster&lt;/code&gt; field to the current user.&lt;/li&gt;
&lt;li&gt;Finally in the UI, &lt;em&gt;accepted&lt;/em&gt; wishlists will be indicated by a little green shopper icon with an &lt;code&gt;accepted&lt;/code&gt;class, like this:&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%2F1owfhv866abzv0nzpzs3.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%2F1owfhv866abzv0nzpzs3.png" alt="Screenshot of a wishlist item on the Kartpool app in the accepted status" width="691" height="202"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the items have been picked up by the &lt;em&gt;wishmaster&lt;/em&gt; and handed over to the &lt;em&gt;buyer&lt;/em&gt;, they can then click on the green icon and mark it as &lt;code&gt;FULFILLED&lt;/code&gt; with a similar &lt;code&gt;PATCH&lt;/code&gt; request, after which it'll 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%2Fpv1eqbeqod8kmb1x300a.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%2Fpv1eqbeqod8kmb1x300a.png" alt="Screenshot of a wishlist item on the Kartpool app in the fulfilled status" width="645" height="201"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend
&lt;/h3&gt;

&lt;p&gt;Create a function &lt;code&gt;update_wishlist&lt;/code&gt; inside &lt;em&gt;wishlists/services.py&lt;/em&gt;. This function will require 3 arguments — the primary key of the wishlist &lt;code&gt;pk&lt;/code&gt;, the &lt;code&gt;wishmaster&lt;/code&gt;, and &lt;code&gt;status&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_wishlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wishmaster&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&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="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ACCEPTED&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;wishlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Wishlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&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="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;wishlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wishmaster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wishmaster&lt;/span&gt;  
        &lt;span class="n"&gt;wishlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;

        &lt;span class="n"&gt;wishlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;update_fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wishmaster&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wishlist&lt;/span&gt;  
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ObjectDoesNotExist&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Wishlist does not exist&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll call this method upon receiving a &lt;code&gt;PATCH&lt;/code&gt; request to the /wishlists/ endpoint. For &lt;code&gt;PATCH&lt;/code&gt; requests, the controller logic needs to be written within the &lt;code&gt;partial_update&lt;/code&gt; inside your Class View.&lt;/p&gt;

&lt;p&gt;Import the above method inside &lt;em&gt;wishlists/views.py&lt;/em&gt; and add the following code inside the &lt;code&gt;WishlistView&lt;/code&gt; class:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;partial_update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
    &lt;span class="n"&gt;wishlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;update_wishlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
        &lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="n"&gt;wishmaster&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wishmaster&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  
        &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;wishlist_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WishlistSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wishlist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;many&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wishlist_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all you need for the backend!&lt;/p&gt;

&lt;h3&gt;
  
  
  Frontend
&lt;/h3&gt;

&lt;p&gt;First you'll register an event listener for any click events that occur on the &lt;em&gt;wishlists&lt;/em&gt; container elements. Add the following code inside &lt;em&gt;index.js&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wishlists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementsByClassName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wishlists&lt;/span&gt;&lt;span class="dl"&gt;'&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;wishlists&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="nx"&gt;wishlists&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="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updateWishlistStatus&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 is how the markup of the card looks like:&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%2Fycmknytqgsob03p5h580.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%2Fycmknytqgsob03p5h580.png" alt="Screenshot of Google Chrome Elements Inspector that shows the markup structure of a wishlist card in the DOM" width="533" height="721"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;em&gt;wishlists.js&lt;/em&gt;, you'll define a function &lt;code&gt;updateWishlistStatus&lt;/code&gt; that runs whenever a click occurs anywhere within the three wishlist container elements. Within this function:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First check whether the click occurred on any of the icons on the right-side of the card. If it did, then&lt;/li&gt;
&lt;li&gt;Grab the wishlist's primary key (id) from the &lt;code&gt;data-id&lt;/code&gt; field.&lt;/li&gt;
&lt;li&gt;Determine the right &lt;code&gt;status&lt;/code&gt; value to be set, using the class-name of the icon.&lt;/li&gt;
&lt;li&gt;Finally call the &lt;code&gt;updateWishlist&lt;/code&gt; function from &lt;em&gt;api.js&lt;/em&gt; to make the &lt;code&gt;PATCH&lt;/code&gt; request and update the wishlist!
&lt;/li&gt;
&lt;/ol&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateWishlistStatus&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="p"&gt;{&lt;/span&gt;  
    &lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;accept&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="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  
            &lt;span class="nf"&gt;updateWishlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
                &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  
                &lt;span class="p"&gt;{&lt;/span&gt;  
                    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ACCEPTED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
                    &lt;span class="na"&gt;wishmaster&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;USERNAME&lt;/span&gt;  
                &lt;span class="p"&gt;}&lt;/span&gt;  
            &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
                &lt;span class="nf"&gt;updateWishlistNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ACCEPTED&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;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;accepted&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="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  
            &lt;span class="nf"&gt;updateWishlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
                &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  
                &lt;span class="p"&gt;{&lt;/span&gt;  
                    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FULFILLED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
                    &lt;span class="na"&gt;wishmaster&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;USERNAME&lt;/span&gt;  
                &lt;span class="p"&gt;}&lt;/span&gt;  
            &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
                &lt;span class="nf"&gt;updateWishlistNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FULFILLED&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;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;And you're done. Refresh the page, play around with your app and watch it in action!&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Congrats on successfully building a Minimum Viable Product. As an exercise, I leave it to you implement the &lt;em&gt;karma points&lt;/em&gt; feature. Don't hesitate to leave a comment if you need any help!&lt;/p&gt;

&lt;p&gt;Once you finish developing all the essential features, it's time for you to speak to your neighbors, demonstrate to them the usefulness of this service, and get some real active users on the platform. Alone, you can do little — but together, you can do so much more!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Technology is best when it brings people together — &lt;a href="https://ma.tt/" rel="noopener noreferrer"&gt;Matt Mullenweg&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Speaking to members within your community will help you receive valuable feedback for your platform. Here are some nice-to-have features that'll make your app even more powerful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add the ability for users to sign-up and create accounts in the platform.&lt;/li&gt;
&lt;li&gt;A community hall-of-fame page that displays &lt;em&gt;Samaritans of the month.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Show a store's inventory so that users may know beforehand whether they can buy a certain item from a store. You will need to connect with your local businesses for this one!&lt;/li&gt;
&lt;li&gt;Continuously refreshing the page after adding or updating a wishlist is annoying. Why don't you try adding &lt;a href="https://channels.readthedocs.io/en/stable/" rel="noopener noreferrer"&gt;web-sockets&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;Implement payment integration so that users can make payments directly to the store from within the app.&lt;/li&gt;
&lt;li&gt;Build a Progressive Web App or a native mobile application UI.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Crisis are part of life. Everybody has to face them, and it doesn't make any difference what the crisis is. Most hardships are opportunities to either advance or stay where you are.&lt;/p&gt;

&lt;p&gt;That being said —&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You shouldn't necessarily have to wait until you're in a crisis to come up with a crisis plan.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The utility of Kartpool will extend beyond emergencies. With big players and large retail chains already eating up local businesses and killing most competition, platforms like these will give the small guys a fighting chance. Your local economy and community will thrive, adapt and grow together in the ever-changing e-commerce landscape and become sustainable!&lt;/p&gt;

&lt;p&gt;I leave you with this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Thou shalt treat thy neighbor as thyself&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Source code
&lt;/h3&gt;

&lt;p&gt;Here's the &lt;a href="https://github.com/booleanhunter-tech-blog/kartpool" rel="noopener noreferrer"&gt;github repository&lt;/a&gt; for reference. In case you have any questions regarding the tutorial, please leave a comment below!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>django</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Build a community-driven delivery app using Django, PostgreSQL &amp; JavaScript</title>
      <dc:creator>Ashwin Hariharan</dc:creator>
      <pubDate>Mon, 31 Jan 2022 16:25:08 +0000</pubDate>
      <link>https://dev.to/egeninc/thinking-of-building-a-contact-tracing-application-what-to-do-instead-4pd3</link>
      <guid>https://dev.to/egeninc/thinking-of-building-a-contact-tracing-application-what-to-do-instead-4pd3</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post was originally published an year ago. I hope the dev.to community finds it useful!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Some COVID-19 stats&lt;/li&gt;
&lt;li&gt;Kartpool app features&lt;/li&gt;
&lt;li&gt;
What you will build

&lt;ul&gt;
&lt;li&gt;Tech stack you will learn and use&lt;/li&gt;
&lt;li&gt;Prerequisites&lt;/li&gt;
&lt;li&gt;Project boilerplate&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
PostgreSQL

&lt;ul&gt;
&lt;li&gt;Designing the schema&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Django&lt;/li&gt;
&lt;li&gt;Open Street Maps&lt;/li&gt;
&lt;li&gt;
Building a location-based store discovery service

&lt;ul&gt;
&lt;li&gt;Backend&lt;/li&gt;
&lt;li&gt;Frontend&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Next steps&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;2012, Terminator, World War Z, Avengers — Endgame. Over the years, movies featuring apocalyptic events have been a favorite amongst movie-goers. Many of them featured the Earth nearly coming to an end in a myriad of ways — from killer zombies to aliens, or something about the planet's core getting unstable. They all usually end up with a hero (or a team of heroes) saving the world from the brink of destruction. And everything's back to normal.&lt;/p&gt;

&lt;p&gt;Except the year 2020 brought us the Coronavirus — something that none of the movies prepared us for. Although calling COVID-19 an apocalypse might be a bit of a stretch, it's not too far from the next worst thing — a &lt;strong&gt;Pandemic&lt;/strong&gt;. The thing about post-apocalyptic movies, you see, is that there's always a &lt;a href="https://en.wikipedia.org/wiki/Deus_ex_machina" rel="noopener noreferrer"&gt;Deus ex machina&lt;/a&gt; that brings everyone back from the edge of the abyss. I mean, how many of us have at least fantasized about this, for example?&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Unfortunately in the real world, we don't have one.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So while our scientists toil hard to produce vaccines for COVID-19, countries around the world have been making attempts to address the problems that have arisen due to the pandemic. Technology solutions, in particular, have an immense potential in raising awareness and slowing down the spread of disease. Unfortunately, besides contact-tracing applications and daunting dashboards that display disease stats, most efforts have only been marginally effective.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9wnswb9b390wv7zgy69r.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9wnswb9b390wv7zgy69r.jpg" alt="Commitstrip comic"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's look at some stats
&lt;/h2&gt;

&lt;p&gt;You probably are keeping an eye on the numbers related to COVID-19 case reports already. But there are several other kinds of stats that matter too, in the long run!&lt;/p&gt;

&lt;p&gt;Over the course of the year, numerous studies have shed light on how COVID-19 has affected people and shaped up some significant behavioral patterns. Here are some highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Most people are uncomfortable with visiting public spaces and are apprehensive in stepping out, doing so only for &lt;a href="https://www.mckinsey.com/business-functions/marketing-and-sales/our-insights/a-global-view-of-how-consumer-behavior-is-changing-amid-covid-19#:~:text=3.-,Shock%20to%20loyalty,different%20retailer%20during%20the%20crisis." rel="noopener noreferrer"&gt;purchasing essentials&lt;/a&gt;, like Groceries, household supplies, and medicines.&lt;/li&gt;
&lt;li&gt;About 40% of consumers prefer to have their products locally sourced. However, the projections for online grocery sales also indicate an increase by almost the same amount. More people are switching to e-commerce platforms for purchasing groceries and medicine, citing hygiene and fear of exposure to the virus as the top concerns.&lt;/li&gt;
&lt;li&gt;Online shopping has created a massive dislocation in local businesses. A survey in the US revealed that out of a sample size of 5800 businesses, 43% had temporarily closed! The biggest reason being that there was no reliable shipping, and most didn't have enough capital to set up a digital infrastructure for their consumers. At the same time, businesses that do manage to provide a reliable delivery option are expected to see nearly 16% increase in revenue.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Now that you know this, how do you make sense of it all?
&lt;/h3&gt;

&lt;p&gt;To put it succinctly, the need of the hour is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Effective means to shop for essentials online and optimize distribution.&lt;/li&gt;
&lt;li&gt;Improve offline shopping experience in a manner that poses the least risk of exposure.&lt;/li&gt;
&lt;li&gt;Serve the neighborhood and local businesses.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Enter Kartpool
&lt;/h2&gt;

&lt;p&gt;We at &lt;a href="http://egen.solutions/" rel="noopener noreferrer"&gt;Egen&lt;/a&gt; decided to build Kartpool — a &lt;strong&gt;community-driven&lt;/strong&gt; delivery app and platform for connecting residents with local businesses.&lt;/p&gt;

&lt;p&gt;After some good old fashioned design thinking, these were the following key features that we agreed upon:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature #1:&lt;/strong&gt; Residents will be able to look at stores in their vicinity and look at the inventory. They can then add a &lt;strong&gt;wishlist&lt;/strong&gt; of essentials that they intend to buy and place a &lt;em&gt;request&lt;/em&gt;, which would be visible to other residents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature #2:&lt;/strong&gt; Any other resident can choose to accept this person's request and become a &lt;strong&gt;wishmaster&lt;/strong&gt;. Then, they can purchase the items from the store on behalf of the requestor and deliver it to them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature #3:&lt;/strong&gt; Users can give &lt;strong&gt;karma points&lt;/strong&gt; to runners via a recognition and appreciation system, for being good samaritans and helpful members of the community.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Community-first?
&lt;/h3&gt;

&lt;p&gt;Government imposed lockdowns will only take us so far. The focus must change to community participation — a bottom-up approach, unlike the top-down directives initiated by the world's governments so far. Talking about the biggest lessons from the Ebola epidemic of 2014, the &lt;a href="https://www.who.int/csr/disease/ebola/one-year-report/response-in-2015/en/" rel="noopener noreferrer"&gt;WHO&lt;/a&gt; said that "&lt;strong&gt;community engagement is the one factor that underlies the success of all other control measures.&lt;/strong&gt;"&lt;/p&gt;

&lt;p&gt;This means, providing your community with access to digital tools that assist in getting timely access to essentials, while at the same time observing containment protocols, and also helping local businesses and livelihoods.&lt;/p&gt;

&lt;p&gt;So, if you're a developer and feel the need to scratch that programming itch, keep reading! I'll walk you through how to build this application that will be useful to your community and neighborhood during this crisis!&lt;/p&gt;

&lt;h2&gt;
  
  
  What you will build
&lt;/h2&gt;

&lt;p&gt;In this tutorial, you'll learn to build a minimum viable solution that you'll be able to present within your community. At the end of this tutorial, you will have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A client-facing app that runs in a browser, in which users will be able to view nearby stores and wishlists. They can accept to pickup wishlists from other users and also make wishlists of their own.&lt;/li&gt;
&lt;li&gt;A backend server to handle incoming requests and to interact with the database.&lt;/li&gt;
&lt;li&gt;A database to store and retrieve information on nearby stores, wishlists, and users in the neighborhood.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since this is a programming tutorial, your focus will not be much on web design, (though you're welcome to do so if you wish! ). It'll primarily be about building this service that has an immediate and long-lasting practical and utilitarian benefit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technologies that you will learn and use
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You'll learn to use &lt;strong&gt;PostgreSQL&lt;/strong&gt; as your database. It's fast, extremely extensible to a wide array of needs (including hyperlocal business applications), has support for both SQL and NO-SQL data models, and is scalable.&lt;/li&gt;
&lt;li&gt;You'll use &lt;strong&gt;Python&lt;/strong&gt; with &lt;strong&gt;Django&lt;/strong&gt; for the back-end. It's a very robust and well-maintained framework, specially designed to accelerate building web services. It's also compatible with major databases like PostgreSQL.&lt;/li&gt;
&lt;li&gt;On the front-end, you'll be using HTML, CSS and &lt;strong&gt;JavaScript&lt;/strong&gt;, and also a few other libraries like &lt;strong&gt;Mapbox&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It will be helpful to have at least a theoretical understanding of how relational database systems work.&lt;/li&gt;
&lt;li&gt;Some knowledge of JavaScript and Python will also help.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Contents
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Part 1 (what you're reading right now)
&lt;/h4&gt;

&lt;p&gt;You'll begin with the first feature of this application. For this feature to work, you'll build an API that fetches all stores from a nearby location along with their inventory information, and display it to the user. Here's what we'll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PostgreSQL schema design and modeling&lt;/li&gt;
&lt;li&gt;Using PostgreSQL geospatial querying capabilities&lt;/li&gt;
&lt;li&gt;Using Django and Mapbox to build a Location-based store discovery service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In&lt;/p&gt;

&lt;h4&gt;
  
  
  Part 2
&lt;/h4&gt;

&lt;p&gt;we'll extend this service and add the ability for users to create wishlists and accept wishlists from other users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's get started!
&lt;/h3&gt;

&lt;p&gt;Go to &lt;a href="https://github.com/booleanhunter-tech-blog/kartpool-wip" rel="noopener noreferrer"&gt;this link&lt;/a&gt;, and follow the instructions to install all the necessary project dependencies. Some of the libraries, python modules and methods have already been imported in the code for you to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  PostgreSQL
&lt;/h2&gt;

&lt;p&gt;PostgreSQL is amongst the most popular relational databases used all over the world, being in the top 2 widely used databases for 2 years in a row in &lt;a href="https://insights.stackoverflow.com/survey/2019#technology" rel="noopener noreferrer"&gt;2019&lt;/a&gt; and &lt;a href="https://insights.stackoverflow.com/survey/2020#most-popular-technologies" rel="noopener noreferrer"&gt;2020&lt;/a&gt;. It has a very high compliance with SQL standards, supports a wide range of native data types (including custom types as well as JSON!), and is extremely &lt;a href="https://www.digitalocean.com/docs/databases/postgresql/resources/supported-extensions/" rel="noopener noreferrer"&gt;extensible&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You'll be using PostgreSQL along with &lt;a href="https://postgis.net/" rel="noopener noreferrer"&gt;&lt;strong&gt;PostGIS&lt;/strong&gt;&lt;/a&gt; - an extension that allows you to store geographic and spatial data in PostgreSQL. It makes your DBMS location aware. and comes with some really &lt;a href="https://postgis.net/features/" rel="noopener noreferrer"&gt;cool features&lt;/a&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  Designing the schema
&lt;/h3&gt;

&lt;p&gt;Broadly speaking, your app has 3 kinds of entities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Store&lt;/strong&gt;: Any shop that sells items to people in the nearby vicinity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User&lt;/strong&gt;: A user in the system can be someone who issues a request for items to purchase from a store, or they can also be a runner who can collect items on behalf of another user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wishlist&lt;/strong&gt;: A user can create a wishlist of items that they need, which can be bought from a nearby store. This wishlist will be visible to other users in the neighrborhood, and one of them can volunteer to take a &lt;em&gt;trip&lt;/em&gt; to the store and buy those items.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We could imagine more entities in the system but these will suffice for now. If you built data models for each of the above entities, what would they look like? Take a moment to think!&lt;/p&gt;

&lt;p&gt;Next, fire up your terminal and run &lt;code&gt;psql&lt;/code&gt; to enter the PostgreSQL interactive terminal. Once it opens up, you can look at the existing databases in PostgreSQL by typing &lt;code&gt;\l&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You'll need to create a new database for your application. Let's call it kartpool. Run &lt;code&gt;CREATE DATABASE kartpool;&lt;/code&gt; to create a new database.&lt;/p&gt;

&lt;p&gt;For applying queries to this database, you'll need to switch to it by typing &lt;code&gt;\c kartpool&lt;/code&gt;. Here are some commonly used queries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;\l&lt;/code&gt;: List available databases&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;\dt&lt;/code&gt;: List existing tables&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SELECT * FROM stores&lt;/code&gt;: Will print all rows and columns in the &lt;code&gt;stores&lt;/code&gt; table.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DROP TABLE &amp;lt;tablename&amp;gt;&lt;/code&gt;: Will delete the table&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DROP DATABASE kartpool&lt;/code&gt;: Deletes the database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now as you may already know, in a relational database, data is usually stored in rows and columns, and the usual data-types can be text, numeric, unique identifiers, images — which is sufficient for most applications. However, when you're building an application that has a location-based feature, you need for a way to be able to store spatial information as well!&lt;/p&gt;

&lt;p&gt;And here is where PostGIS helps! In addition to the existing data-types and querying that PostgreSQL provides, PostGIS introduces some more &lt;a href="http://postgis.net/workshops/postgis-intro/geometries.html#representing-real-world-objects" rel="noopener noreferrer"&gt;data-types&lt;/a&gt;, and also provides you with querying capabilities, especially for geographical information.&lt;/p&gt;

&lt;p&gt;You'll be working with the &lt;a href="http://postgis.net/workshops/postgis-intro/geometries.html#points" rel="noopener noreferrer"&gt;Point&lt;/a&gt; datatype, because that will allow you to store location information of a store using its geographic coordinates. Then, if you have the user's location, you'll be able to query for stores within their proximity!&lt;/p&gt;

&lt;p&gt;For the purposes of this tutorial, you won't dive into the SQL commands — as it can become quite hard to get a hold of the syntax!&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F123sj3drylaaifr7y9zm.png" class="article-body-image-wrapper"&gt;&lt;img alt="A screenshot showing how complex an SQL command can get" 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%2F123sj3drylaaifr7y9zm.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;a href="https://www.reddit.com/r/programminghorror/comments/bda6o1/the_more_i_look_at_it_the_less_i_understand/" rel="noopener noreferrer"&gt;
The more I look at it, the less I understand!
&lt;/a&gt;




&lt;p&gt;So instead, you'll learn to use Django's Object Relational-mapping (ORM) layer to interact with the database. That way, if you switch the database from PostgreSQL to another one that's supported by Django, your python code still remains the same!&lt;/p&gt;

&lt;h2&gt;
  
  
  Django
&lt;/h2&gt;

&lt;p&gt;Django has been consistently rated among the most popular Python frameworks for multiple years in a row. It supports the Model-View-Template pattern, which allows you to maintain a robust architecture in your code, as the application grows with time. It also has "batteries" included — meaning that it comes with an admin panel and an extensive set of plugins for building just about anything ranging from authentication services to payment systems.&lt;/p&gt;

&lt;p&gt;Django also comes along with an ORM system by default, which you'll be using to interact and query your PostgreSQL database.&lt;/p&gt;

&lt;p&gt;Now, each Django project consists of one or more &lt;em&gt;applications&lt;/em&gt;. A Django app allows you to encapsulate a set of related functionality along with its models, views and application logic together. For this project, you'll have the following apps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;stores&lt;/code&gt; : All store-related functionality (such as storing or fetching nearby stores) will go here.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wishlists&lt;/code&gt;: This will contain all the logic required to create/update or retrieve wishlists.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;home&lt;/code&gt;: You'll use this app to serve the app page in the browser along with client side JavaScript files, stylesheets, and for loading external libraries like Mapbox.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You'll first define the model for store-related information in&lt;code&gt;stores/models.py&lt;/code&gt;. Open the file in your favorite editor and write the following:&lt;/p&gt;


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


&lt;p&gt;Here you've basically defined a class for your Store, along with its fields and data-types. The &lt;em&gt;location&lt;/em&gt; field, in particular, is of type &lt;code&gt;Point&lt;/code&gt; — so that allows you to store information using a pair of coordinates.&lt;/p&gt;

&lt;p&gt;Now, you'll need to tell Django to create the store table in PostgreSQL. In your terminal, run the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python manage.py makemigrations  
python manage.py migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will have the effect of creating a table called &lt;code&gt;stores_store&lt;/code&gt;! This is the equivalent of running an SQL query using &lt;code&gt;psql&lt;/code&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="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;stores_store&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;BIGINT&lt;/span&gt; &lt;span class="k"&gt;GENERATED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;IDENTITY&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;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="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;store_type&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;created_at&lt;/span&gt; &lt;span class="n"&gt;TIMESTAMPTZ&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;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;opening_hour&lt;/span&gt; &lt;span class="nb"&gt;TIME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;closing_hour&lt;/span&gt; &lt;span class="nb"&gt;TIME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;city&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;latitude&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;longitude&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="n"&gt;GEOGRAPHY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;address&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="n"&gt;phone&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="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Next, you'll need to populate some data! Wouldn't it be awesome if you could find some real data to populate your database?&lt;/p&gt;

&lt;p&gt;Well lucky for you, we have -&lt;/p&gt;
&lt;h2&gt;
  
  
  Open Street Maps
&lt;/h2&gt;

&lt;p&gt;A collaborative project, that has real-world location information of billions of areas of interest — collected and aggregated by good samaritans all over the world! Plenty of non-profits have built useful services &lt;sup id="fnref1"&gt;1&lt;/sup&gt; on top of OSM, like &lt;a href="https://wheelmap.org/" rel="noopener noreferrer"&gt;Wheelmap&lt;/a&gt; for example:&lt;/p&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%2Fhks1f3rikmfes7vn7lrf.png" alt="Screenshot of Wheelmap, An OpenStreet Map that shows all the places that are Wheelchair accessible"&gt;
A list of places in Chicago that are wheelchair accessible




&lt;p&gt;&lt;strong&gt;Pretty amazing right?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, what if I told you that all OSM data is in-fact stored in — (surprise), &lt;a href="https://en.wikipedia.org/wiki/OpenStreetMap#Data_storage" rel="noopener noreferrer"&gt;PostgreSQL&lt;/a&gt;?! Thanks to these folks, you can pretty much pull information from anywhere you live!&lt;/p&gt;

&lt;p&gt;In order to mine information from OSM, there is a neat little tool called &lt;a href="http://overpass-turbo.eu/" rel="noopener noreferrer"&gt;Overpass Turbo&lt;/a&gt; &lt;sup id="fnref2"&gt;2&lt;/sup&gt;. Header over there, and click on the Wizard option. If you live in Chicago, you can type something like &lt;code&gt;shop in Chicago&lt;/code&gt; which will run a query to pull out all shop-related information within the region. Once the query has run, download the data in raw OSM format and save it as &lt;em&gt;data.json&lt;/em&gt; in the data folder of your project.&lt;/p&gt;

&lt;p&gt;Now, run &lt;code&gt;python manage.py makemigrations stores --empty&lt;/code&gt; to create a new empty migration file, and add the following code:&lt;/p&gt;


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



&lt;p&gt;In this code, you've defined an operation to be run called &lt;code&gt;load_data&lt;/code&gt; . This method uses the &lt;code&gt;json&lt;/code&gt; library to load the file you just downloaded, and loops over each object that contains information for a particular store. You then pick the information you need, create a Store instance and then save it.&lt;/p&gt;

&lt;p&gt;Now, you can run &lt;code&gt;python manage.py migrate&lt;/code&gt; once again, and it will extract the stores from the JSON dump, and save them in the db. You can run &lt;code&gt;SELECT * FROM stores_store&lt;/code&gt; from &lt;code&gt;psql&lt;/code&gt; and see the results!&lt;/p&gt;

&lt;p&gt;Now that you have a database with spatial querying capabilities that has actual real data stored in it, you can now create a useful application:&lt;/p&gt;

&lt;h2&gt;
  
  
  A Location-based store discovery service
&lt;/h2&gt;

&lt;p&gt;Here's what you'll build:&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstxqq2sclo4wp0pq26uf.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%2Fstxqq2sclo4wp0pq26uf.png" alt="Screenshot of Kartpool's map"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On the UI, you'll have a map with a search box. A user can type in the name of a place (where they live) on the search box to perform a search.&lt;/li&gt;
&lt;li&gt;The search box will make automatic suggestions as the user types.&lt;/li&gt;
&lt;li&gt;On selecting the place from the search results, the app hits an API and fetches the relevant stores information near to that location&lt;/li&gt;
&lt;li&gt;The store locations will then be displayed on the map.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a visualization of how this service might look like&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fet4x712qbl4ruc3llh0y.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fet4x712qbl4ruc3llh0y.gif" alt="A gif showing Kartpool's architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Write an API to retrieve a list of nearby stores from a pair of coordinates
&lt;/h4&gt;

&lt;p&gt;Here are the components that you will need in-order to build this service in Django —&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A URL/API Route&lt;/strong&gt;: You'll define an endpoint called &lt;code&gt;stores/&lt;/code&gt; to which you could pass a pair of coordinate values &lt;code&gt;lat&lt;/code&gt; and &lt;code&gt;lng&lt;/code&gt;in the request query parameters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A View&lt;/strong&gt;: This is essentially a request controller, that helps extract the information from a incoming request, do something with it, and send a response back. So you'll define a view to handle the &lt;code&gt;stores&lt;/code&gt; endpoint, extract the latitude and longitude information from the request, and ask PostgreSQL to look up information for all nearby stores for those pair of coordinates. It is always a good idea to separate your business logic from the request, so you'll write the PostgreSQL querying part inside &lt;em&gt;services.py&lt;/em&gt; file  of your project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Serializer&lt;/strong&gt;: This helps you to control the output of your responses and what fields must they contain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In order to build the above components, you'll use the &lt;a href="https://www.django-rest-framework.org" rel="noopener noreferrer"&gt;Django REST framework,&lt;/a&gt; and the built-in &lt;a href="https://docs.djangoproject.com/en/3.1/ref/contrib/gis/tutorial/#geodjango-tutorial" rel="noopener noreferrer"&gt;GeoDjango&lt;/a&gt; module in your project.&lt;/p&gt;

&lt;h5&gt;
  
  
  Step #1: Define an endpoint
&lt;/h5&gt;

&lt;p&gt;Open &lt;code&gt;urls.py&lt;/code&gt; in your code, and type the following:&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;from&lt;/span&gt; &lt;span class="n"&gt;stores&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;stores_viewsrouter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DefaultRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;stores&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;stores_views&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StoreView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;basename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;stores&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here, you import your Store views from the &lt;code&gt;stores/views.py&lt;/code&gt; file, and create a router instance using Django REST Framework's &lt;code&gt;DefaultRouter&lt;/code&gt; method. Then you'll need to add this route to the list of &lt;code&gt;urlpatterns&lt;/code&gt; — &lt;code&gt;path('', include(router.urls)),&lt;/code&gt;.&lt;/p&gt;
&lt;h5&gt;
  
  
  Step #2: Define the view for your endpoint
&lt;/h5&gt;

&lt;p&gt;The next step is to create the &lt;em&gt;Store&lt;/em&gt; view that you just imported in the previous step. Add the following in &lt;em&gt;stores/views.py&lt;/em&gt;:&lt;/p&gt;


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



&lt;p&gt;A view allows you to write a handler for your API. To put it simply, it's just a Python Class or a function that takes in a web request and returns a response.&lt;/p&gt;

&lt;p&gt;You can use either Django's built-in views, or in this case, a &lt;a href="https://www.django-rest-framework.org/api-guide/viewsets/" rel="noopener noreferrer"&gt;Viewset&lt;/a&gt; — an abstration provided by the Django rest Framework module. Using a Viewset allows you to &lt;a href="https://www.django-rest-framework.org/api-guide/viewsets/#viewset-actions" rel="noopener noreferrer"&gt;standardize&lt;/a&gt; handling of APIs for commonly requested information — like returning a list, or details of a particular Model.&lt;/p&gt;

&lt;p&gt;Since you need to return a list of Stores, you'll use the &lt;a href="https://www.django-rest-framework.org/api-guide/viewsets/#modelviewset" rel="noopener noreferrer"&gt;ModelViewset&lt;/a&gt; and use the built-in &lt;em&gt;list&lt;/em&gt; action for our handler. Along with this, you'll also need to provide the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://docs.djangoproject.com/en/3.1/topics/db/queries/" rel="noopener noreferrer"&gt;Queryset&lt;/a&gt;: This has the effect of querying your underlying database&lt;/li&gt;
&lt;li&gt;A Serializer Class: To configure your view to use a serializer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You'll now need to define the method &lt;code&gt;get_nearby_stores_within&lt;/code&gt; method for querying the database for store related information. Finally, you'll create the serializer &lt;code&gt;NearbyStoreSerializer&lt;/code&gt; which will allow you to define what &lt;code&gt;Store&lt;/code&gt; related information you want to return.&lt;/p&gt;

&lt;h5&gt;
  
  
  Step #3: Define a method to query your database for nearby stores
&lt;/h5&gt;

&lt;p&gt;You'll create the method &lt;code&gt;get_nearby_stores_within&lt;/code&gt; to which you'll pass latitude, longitude, a distance parameter, and an &lt;a href="https://en.wikipedia.org/wiki/Spatial_reference_system" rel="noopener noreferrer"&gt;srid&lt;/a&gt;:&lt;/p&gt;


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


&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.djangoproject.com/en/dev/ref/models/querysets/#annotate" rel="noopener noreferrer"&gt;&lt;code&gt;annotate&lt;/code&gt;&lt;/a&gt;: This method adds a new field (in this case, &lt;em&gt;distance&lt;/em&gt;) to each object that is returned within a query. The value of each distance field is calculated using GeoDjango's &lt;a href="https://docs.djangoproject.com/en/3.1/ref/contrib/gis/functions/#distance" rel="noopener noreferrer"&gt;&lt;em&gt;Distance&lt;/em&gt;&lt;/a&gt; function.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.djangoproject.com/en/3.1/ref/contrib/gis/functions/#distance" rel="noopener noreferrer"&gt;&lt;code&gt;Distance&lt;/code&gt;&lt;/a&gt;: This calculates the distance between two sets of coordinates — coordinates of the store location, and coordinates of the current user's location.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.djangoproject.com/en/3.1/topics/db/queries/#retrieving-specific-objects-with-filters" rel="noopener noreferrer"&gt;&lt;code&gt;filter&lt;/code&gt;&lt;/a&gt;: This allows you to return only a subset of the set of objects. In this case, we want to apply a filter that selects only the objects that lie within the first &lt;em&gt;x&lt;/em&gt; kilometres. For this, we'll use GeoDjango's measurement API &lt;a href="https://docs.djangoproject.com/en/3.1/ref/contrib/gis/measure/#django.contrib.gis.measure.D" rel="noopener noreferrer"&gt;&lt;em&gt;D&lt;/em&gt;&lt;/a&gt; (alias for Distance)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.djangoproject.com/en/3.1/ref/models/querysets/#order-by" rel="noopener noreferrer"&gt;&lt;code&gt;order_by&lt;/code&gt;&lt;/a&gt;: Finally, we'll order the results sorted by the &lt;code&gt;distance&lt;/code&gt; field that we have from the &lt;em&gt;annotate&lt;/em&gt; step.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Step #4: Define a serializer
&lt;/h5&gt;

&lt;p&gt;Open &lt;em&gt;serializers.py&lt;/em&gt; and add the following code:&lt;/p&gt;


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


&lt;p&gt;Your Store model doesn't actually have the distance field, but was annotated while constructing the Response in the earlier step. Therefore, your Serializer must explicitly be told how to look up this information, by providing a method of the form &lt;code&gt;get_annotatedfieldname&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Whew that was a lot! Run &lt;code&gt;python manage.py runserver&lt;/code&gt;, fire up Postman or any API client of your choice (or you could use the browser as well), and go to &lt;a href="http://localhost:8000/stores/?lat=41.9369726&amp;amp;lng=-87.6646324" rel="noopener noreferrer"&gt;http://localhost:8000/stores?lat=12.978624&amp;amp;lng=77.645296&lt;/a&gt; (Replace these values with the latitude/longitude of your own). Based on the latitude and longitude information you passed in the request, you'll see all nearby stores for that location!&lt;/p&gt;

&lt;p&gt;Seeing the list of stores as JSON is a good start. But that's not nearly as fun as seeing all the stores on a map!&lt;/p&gt;

&lt;p&gt;For this, you'll need an HTML page. So here's what you'll do next:&lt;/p&gt;

&lt;h4&gt;
  
  
  Write an API to serve an HTML page
&lt;/h4&gt;

&lt;p&gt;Your app will serve an HTML page with a welcome message when someone heads over to the URL &lt;a href="http://localhost:8080?username=john" rel="noopener noreferrer"&gt;http://localhost:8080/home?username=john&lt;/a&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  Step #1: Define the endpoint
&lt;/h5&gt;

&lt;p&gt;In-order for this feature to work, you'll use the &lt;code&gt;home&lt;/code&gt; Django application.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;urls.py&lt;/code&gt; and add the route for your home page:&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;from&lt;/span&gt; &lt;span class="n"&gt;home&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;home_viewsrouter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;home&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;home_views&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HomePage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;basename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;home&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Step #2: Add the view
&lt;/h5&gt;

&lt;p&gt;Write this inside &lt;code&gt;home/views.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.shortcuts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rest_framework&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;viewsets&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rest_framework.decorators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rest_framework.response&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rest_framework.renderers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TemplateHTMLRenderer&lt;/span&gt;\&lt;span class="c1"&gt;# Create your views here.  
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomePage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewsets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GenericViewSet&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
    &lt;span class="n"&gt;renderer_classes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;TemplateHTMLRenderer&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  
    &lt;span class="n"&gt;template_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;home/index.html&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_params&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upon hitting the endpoint, Django will now look for the file &lt;em&gt;index.html&lt;/em&gt; within the &lt;em&gt;home/templates&lt;/em&gt; folder. Then, it intrapolates the variable &lt;code&gt;username&lt;/code&gt; in the template file with the value that you provide in the query params — so when you open the page, you'll see "&lt;em&gt;Welcome, john&lt;/em&gt;" displayed in the UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Frontend
&lt;/h3&gt;

&lt;p&gt;In your &lt;em&gt;home&lt;/em&gt; application, Here are the relevant functions that you'll work on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;map/addMap()&lt;/code&gt; : For loading the map.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;map/addGeocoder()&lt;/code&gt; : For adding a search box in the map with autocomplete feature.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;api/fetchNearbyStores()&lt;/code&gt; : For making a network request to fetch nearby stores from a given latitude and longitude.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;map/convertToGeoJson()&lt;/code&gt; : For converting the fetched stores to GeoJSON.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;map/plotStoresOnMap&lt;/code&gt; : For plotting the stores on the map.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is some boilerplate JavaScript code inside &lt;em&gt;index.js&lt;/em&gt; that calls the above methods. So you'll just need to write the logic for making the relevant functions work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Since the focus of this tutorial is on logic and not styling, all the relevant boilerplate HTML and CSS code along with the relevant files like stylesheets, icons, and client-side libraries etc have already been included for you. You'll find these files within the &lt;em&gt;/home/static/home&lt;/em&gt; folder.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step #1: Load the map
&lt;/h4&gt;

&lt;p&gt;First, create an account on &lt;a href="https://www.mapbox.com/" rel="noopener noreferrer"&gt;Mapbox&lt;/a&gt; and add an &lt;a href="https://account.mapbox.com/access-tokens/" rel="noopener noreferrer"&gt;access token&lt;/a&gt;. Copy and paste it inside &lt;em&gt;home/js/map.js&lt;/em&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="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your-secret-token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the same file, add the following the &lt;code&gt;addMap()&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&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;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;  
    &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mapbox://styles/mapbox/light-v10&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;77.645296&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;12.978624&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  
    &lt;span class="na"&gt;zoom&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="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NavigationControl&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;map&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Refresh the page, and you should now see a map rendered inside the &lt;em&gt;div&lt;/em&gt; element &lt;code&gt;#map&lt;/code&gt;! Let's also add an autocomplete field in the map. Add the following inside the &lt;code&gt;addGeocoder()&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;geocoder&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;MapboxGeocoder&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&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="nf"&gt;addControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;geocoder&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="nx"&gt;geocoder&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;result&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;data&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;geocoderCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So you'll be able to type the name of your neighborhood on the search box (for example, &lt;em&gt;Lincoln Square, Illiniois, Chicago)&lt;/em&gt;. Upon selecting the place, it will pass the data of that place (along with its coordinates) to the &lt;code&gt;geocoderCallback&lt;/code&gt;. You'll then be able to use the latitude and longitude information to hit the &lt;code&gt;/stores&lt;/code&gt; endpoint that you wrote earlier, and get back a list of stores as JSON.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step #2: Fetch nearby stores and convert them to GeoJSON
&lt;/h4&gt;

&lt;p&gt;For displaying those little green store markers on the map, Mapbox needs data to be in the form of GeoJSON. GeoJSON is a format for encoding a variety of geographic data structures. This is how it looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;  
    &lt;/span&gt;&lt;span class="err"&gt;type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FeatureCollection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  
    &lt;/span&gt;&lt;span class="err"&gt;features:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;  
                &lt;/span&gt;&lt;span class="err"&gt;type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Feature"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  
                &lt;/span&gt;&lt;span class="err"&gt;geometry:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;  
                    &lt;/span&gt;&lt;span class="err"&gt;type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  
                    &lt;/span&gt;&lt;span class="err"&gt;coordinates:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;longitude&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;latitude&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;  
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;  
                &lt;/span&gt;&lt;span class="err"&gt;properties:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;  
                    &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;field&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;value&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  
                    &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;field&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;value&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  
                        &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;  
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;  
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;  
        &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;  
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Write the following within the &lt;code&gt;convertToGeoJson&lt;/code&gt; function inside &lt;em&gt;map.js&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;convertToGeoJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stores&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="na"&gt;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;FeatureCollection&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="na"&gt;features&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;stores&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&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="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;Feature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
            &lt;span class="na"&gt;geometry&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="s1"&gt;Point&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
                &lt;span class="na"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  
            &lt;span class="p"&gt;},&lt;/span&gt;  
            &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
                &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;store&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="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
                &lt;span class="na"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
                &lt;span class="na"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
                &lt;span class="na"&gt;rating&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rating&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
            &lt;span class="p"&gt;}&lt;/span&gt;  
        &lt;span class="p"&gt;}&lt;/span&gt;  
    &lt;span class="p"&gt;})&lt;/span&gt;  
  &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, you'll have a function called &lt;code&gt;plotStoresOnMap&lt;/code&gt; inside that takes in 2 arguments — map, and stores in the form of GeoJSON, and plot those pretty little green icons on the map!&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;function&lt;/span&gt; &lt;span class="nf"&gt;plotStoresOnMap&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;storesGeoJson&lt;/span&gt;&lt;span class="p"&gt;)&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;store&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;storesGeoJson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;features&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="c1"&gt;// create a HTML element for each feature  &lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
        &lt;span class="nx"&gt;el&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="s1"&gt;store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
        &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&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;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&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="s2"&gt;\n`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;  
        &lt;span class="s2"&gt;`approximately &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;distance&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="s2"&gt; km away\n`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;  
        &lt;span class="s2"&gt;`Address: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;N/A&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;  
        &lt;span class="s2"&gt;`Phone: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;phone&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;N/A&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;  
        &lt;span class="s2"&gt;`Rating: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rating&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;N/A&lt;/span&gt;&lt;span class="dl"&gt;"&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="c1"&gt;// make a marker for each feature and add to the map  &lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Marker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLngLat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTo&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;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&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="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="p"&gt;{&lt;/span&gt;  
            &lt;span class="nf"&gt;updateSelectedStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&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="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;For each store, you create an HTML &lt;em&gt;Div&lt;/em&gt; element and add the relevant information of the store such as the name, contact details, how far away it is, etc on the &lt;em&gt;title&lt;/em&gt; attribute. So when you place your mouse over the marker, you'll see this info on a tooltip!&lt;/p&gt;

&lt;p&gt;Then, you create a new Mapbox marker using the element, set its coordinates, and add it to the map! You also have an event listener that watches for any &lt;em&gt;click&lt;/em&gt; events on a marker. If you select a particular store by clicking on it, it will assign a global variable &lt;code&gt;STORE&lt;/code&gt; with the store's unique ID.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: The &lt;code&gt;title&lt;/code&gt; attribute isn't great for accessibility. You'll learn a better technique later.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that's it! Refresh the page, and on the input box displayed on the map, type in a place within the city that you added the data for earlier (for example, &lt;em&gt;Lincoln Park, Chicago&lt;/em&gt;). Upon selecting the place, the &lt;code&gt;geocoderCallback&lt;/code&gt;runs a function called &lt;code&gt;displayNearbyStores&lt;/code&gt;(inside &lt;em&gt;stores.js&lt;/em&gt;). This method fetches the nearby stores using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API" rel="noopener noreferrer"&gt;Fetch API&lt;/a&gt;, converts them to GeoJSON using &lt;code&gt;convertToGeoJson&lt;/code&gt;, and plots them on the map using &lt;code&gt;plotStoresOnMap&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And, we're done!&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;In the next part, you'll learn to extend this service further with additional features needed for the service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A feature to add a &lt;strong&gt;wishlist&lt;/strong&gt; of essential items to purchase from a nearby store using a simple user interface.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;location-based wishlist discovery service&lt;/strong&gt; that will show you wishlists created by other people in the neighborhood.&lt;/li&gt;
&lt;li&gt;Ability to accept a wishlist from another user and purchase the items on their behalf.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So take a break, get something nice for yourself from your favorite store, and proceed with &lt;a href="https://dev.to/egeninc/build-a-community-driven-delivery-app-using-django-postgresql-javascript-part-2-jod"&gt;Part 2&lt;/a&gt;!&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://wiki.openstreetmap.org/wiki/List_of_OSM-based_services" rel="noopener noreferrer"&gt;Here's a list&lt;/a&gt; of several such useful applications built using &lt;a href="https://www.openstreetmap.org" rel="noopener noreferrer"&gt;OSM&lt;/a&gt;. Many of these projects need contributors in various roles — I highly encourage you to find one that you like and offer any help that you can! ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;BTW, did you know that gamers have used this tool to &lt;a href="https://www.reddit.com/r/TheSilphRoad/comments/54sy36/osm_query_to_idd%20run%20the%20query%20to%20get%20information%20entify_possible_nests/" rel="noopener noreferrer"&gt;find pokemon habitats&lt;/a&gt; in the Pokemon Go Game? 😉 ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>django</category>
      <category>postgres</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>While applying for jobs, do you mention skills that you learned via side-projects but never used them at your work?</title>
      <dc:creator>Ashwin Hariharan</dc:creator>
      <pubDate>Wed, 02 Jun 2021 12:11:55 +0000</pubDate>
      <link>https://dev.to/booleanhunter/while-applying-for-jobs-do-you-mention-skills-that-you-learned-via-side-projects-but-never-used-them-at-your-work-1f58</link>
      <guid>https://dev.to/booleanhunter/while-applying-for-jobs-do-you-mention-skills-that-you-learned-via-side-projects-but-never-used-them-at-your-work-1f58</guid>
      <description>&lt;p&gt;Hi everyone! I'm a &lt;a href="//ashwinhariharan.tech"&gt;software engineer&lt;/a&gt; who recently transitioned into Developer Relations. I like programming and enjoy learning and tinkering with new technologies - and they aren't necessarily stuff that I use or require everyday at my workplace. Pretty sure that I'm not the only one! 😇&lt;/p&gt;

&lt;p&gt;When I look at the stuff that I know, I can divide them into 2 broad categories:&lt;/p&gt;

&lt;h2&gt;
  
  
  Grade A skills:
&lt;/h2&gt;

&lt;p&gt;These are stuff that I require very less ramp-up time, because I've had enough exposure with them and use them &lt;em&gt;almost&lt;/em&gt; everyday at work. For me, they happen to be the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript / TypeScript&lt;/li&gt;
&lt;li&gt;Node.js / Express&lt;/li&gt;
&lt;li&gt;MongoDB&lt;/li&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If I'm ever asked questions like &lt;em&gt;"How many years of experience do you have in &lt;code&gt;x&lt;/code&gt;"&lt;/em&gt;, and it happens to fall under Grade A, I can answer it somewhat accurately (for instance, 2 years or 4 years or whatever).&lt;/p&gt;

&lt;h2&gt;
  
  
  Grade B skills:
&lt;/h2&gt;

&lt;p&gt;These are skills that I never had a chance to work on or use in production at work, but I acquired them while working on my side projects. Here are some of my own skills that fall under this category:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;Django&lt;/li&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Scikit-learn&lt;/li&gt;
&lt;li&gt;Keras&lt;/li&gt;
&lt;li&gt;Figma&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this category, answering questions like &lt;em&gt;"how many years of experience do you have"&lt;/em&gt; becomes difficult, because I use them every now and then, not every single day. However, I can still &lt;em&gt;get things done&lt;/em&gt; with them and would like the person going through my profile to know that. I may not be as comfortable in these as my &lt;em&gt;Grade A&lt;/em&gt; skills, but I still know much more than just writing a simple &lt;em&gt;hello world&lt;/em&gt; program (or its equivalent).&lt;/p&gt;




&lt;p&gt;If you can relate with the above, I would love to know from you on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Along with Grade A, do you also mention Grade B skills in your resume, CV or in job applications?&lt;/li&gt;
&lt;li&gt;If the answer to the above is yes, how do you list them? Do you make a distinction between Grade A and Grade B, or do you just club them all together?&lt;/li&gt;
&lt;li&gt;If you're asked "How many years of experience do you have in &lt;code&gt;x&lt;/code&gt; skill", and that skill happens to be a Grade B skill, how do you answer it?&lt;/li&gt;
&lt;li&gt;If in a job posting, you find that 60% of the required skill-set fall under your Grade B and 40% fall under Grade A, do you still apply for the role?&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>discuss</category>
      <category>career</category>
      <category>watercooler</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Idea of an Invigorating Startup Life can Often be an Illusion</title>
      <dc:creator>Ashwin Hariharan</dc:creator>
      <pubDate>Fri, 18 Dec 2020 05:37:32 +0000</pubDate>
      <link>https://dev.to/booleanhunter/the-idea-of-an-invigorating-startup-life-can-often-be-an-illusion-1gai</link>
      <guid>https://dev.to/booleanhunter/the-idea-of-an-invigorating-startup-life-can-often-be-an-illusion-1gai</guid>
      <description>
&lt;span&gt;Photo by &lt;a href="https://unsplash.com/@officestock?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Sebastian Herrmann&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/startups-frustration?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;


&lt;p&gt;Working in a startup can be fun. Many people coming from large companies to work with startups claim that the environment is full of positive vibes, every single day truly feels like an opportunity to do something innovative. The words 'disruptive’ and 'startups’ often go along together.&lt;/p&gt;

&lt;p&gt;Some few years ago I decided to quit my job, mentor in web development workshops and then take a vacation to see my family. They lived in a different city, and I hadn’t seen them for almost an year. Felt like it would be nice to spend some quality time with them, before I start figuring out what the hell to do next.&lt;/p&gt;

&lt;p&gt;I was working with startups all this time in Bengaluru, often described as the Silicon Valley of India LOL —  and while it can be exciting to work in one, it can also be gruelling. In the beginning it feels great. When I just got out of college I was really desperate about landing a good role, and to prove just how much I cared about this wonderful app that they were building.&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%2Fk1t5clwpmfano5svr877.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%2Fk1t5clwpmfano5svr877.gif" alt="Gif meme of a clip from Wolf of Wall Street" width="600" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would basically tell them anything that they needed to hear, and interactions with other fellow developers tell me that they too more-or-less do the same thing!&lt;/p&gt;

&lt;p&gt;Often in the beginning of our career we’re naive and uninformed, and so we’re unable to evaluate our career prospects thoughtfully. If you’re thinking of taking a job in a new venture or already working in such a company, you should really do some introspection —  mostly in the lines of self growth. Basically trying to find the answer to&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can I grow as a person and as a professional?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
In the context of the startup world, this question can be answered by asking yourself a series of sub-questions —&lt;/p&gt;



&lt;h3&gt;
  
  
  Do you find yourself being able to constantly improve your skills or explore new stuff throughout the time that you’re planning to spend there?
&lt;/h3&gt;

&lt;p&gt;During interviews, startup founders talk about the ground breaking stuff that they're doing and often oversell themselves, and you’re inclined to believe them. You’ll be told about the &lt;strong&gt;incredible learning opportunity&lt;/strong&gt; that you’d get, if you joined. For a long time, it was my favourite thing to hear, and often I wouldn't negotiate salaries thinking that the offer was already so generous.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When I finally update my LinkedIn profile, it’s gonna look so awesome!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But that’s oversimplified thinking. It took me a while to realize that there's always gonna be learning in the beginning. If the company is into product development that consists of front-end and back-end, you’ll learn enough to use hacks and make something work.&lt;/p&gt;

&lt;p&gt;A major part of your work will be about implementing new features and fixing bugs — which becomes straight-forward once you learn how to hack your way around code. But after the initial phase, you might not have the feasibility to learn anything more — you’re gonna be restricted to a specific role for a long time and can learn something only if the situation demands for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There might be a hiatus in your learning curve after a while&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For instance, in my case I always wanted to explore machine learning. Or learn Photoshop maybe? But since my job didn’t necessitate for these skills, I had no other choice but to try carving out time after office hours to do all those things.&lt;/p&gt;

&lt;p&gt;Which brings us to the next question —&lt;/p&gt;



&lt;h3&gt;
  
  
  Do you get enough time to do the things that you really really want to do?
&lt;/h3&gt;

&lt;p&gt;In startups, a 9 AM to 6 PM job is almost unheard of. Most of them would tell you that they have flexible timings, but it's mostly for the &lt;strong&gt;company's benefit&lt;/strong&gt;, not yours. Usually you’d see people working late, staying up until midnight almost every other day.&lt;/p&gt;

&lt;p&gt;You might try out time management hacks, take fewer breaks, try to do some of your learning in your office itself. But that’s about the best you can do. There’s always more work.&lt;/p&gt;

&lt;p&gt;In my instance, I thought if I came home early I could spend few hours working on open source projects or watch some videos on Coursera. Maybe work on that half built app that I always wanted to finish. But I couldn’t, since I often had to stay back in office late.&lt;/p&gt;

&lt;p&gt;Trying to do too many things also drains your productivity — you need to be taking enough breaks if you don’t wanna experience burn-outs. Hard work is important, but if you're constantly getting stressed out then you cease to be effective.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Busy&lt;/strong&gt; does not necessarily equate to &lt;strong&gt;productive&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%2Fnfk2egqy0vgt2e6bdehs.jpg" 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%2Fnfk2egqy0vgt2e6bdehs.jpg" alt="Image showing State of Flow" width="800" height="751"&gt;&lt;/a&gt;&lt;br&gt;
&lt;span&gt;Hitting that sweet spot&lt;/span&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%2F7v6mrvnelun9zjd6pug2.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%2F7v6mrvnelun9zjd6pug2.gif" alt="Gif meme of a clip from Wolf of Wall Street" width="560" height="227"&gt;&lt;/a&gt;&lt;br&gt;
&lt;span&gt;You don’t wanna come home everyday looking like this&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;We’re humans after all, we’re not machines that are designed to take in coffee and productivity blogs as inputs and just transform into Elon Musk, being super productive, super motivated all the time.&lt;/p&gt;



&lt;h3&gt;
  
  
  So do you have a life outside of work?
&lt;/h3&gt;

&lt;p&gt;Call it work-life balance, pursuing your interests, passions, hobbies, whatever. They’re all the same.&lt;/p&gt;

&lt;p&gt;Many startups have 6-day work weeks. No Saturdays off meant that you couldn’t attend tech meetups, or mentor in workshops if you wanted to. Good luck in trying to finish that course work on Sunday or pursuing your hobbies, juggling between doing your laundry, spending time with your family and running other errands — because before you even realize it, the day is over.&lt;/p&gt;

&lt;p&gt;You’ll frequently hear this during interviews, or when you talk about getting few days off —&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“We’ve got lot of work to do, and we’re all trying to be as agile as possible and working really hard."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;or this —&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“In another few months once we’ve released our first public beta then things will start going a little easier and we can take it in a relaxed pace"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There’s always pressure to stay ahead of the competition, to ship faster, get users and start becoming profitable. I can't say I blame them entirely — when you’re overwrought, no amount of work seems enough. Convincing your boss to give you a few days off seems like an impossible thing to do.&lt;/p&gt;

&lt;p&gt;What about those startups that offer an unlimited vacation policy? I’ve never been in one of those, but &lt;a href="https://mfbt.ca/unlimited-vacation-and-other-forms-of-guilt-based-management-44413269a184#.g4hmyo6mq" rel="noopener noreferrer"&gt;statistics indicate&lt;/a&gt; that employees take even fewer time off for themselves in those start-ups.&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%2Fas75fvuf7zjd4748olaz.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%2Fas75fvuf7zjd4748olaz.png" alt="Meme of a clip from Wolf of Wall Street" width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Giving up on your interests and hobbies is a bad idea, because these are the very things that make you who you are. They’re not useless, they have an effect on how you approach problems, how you deal with a crisis at work, in your interactions with your co-workers. All adding up to how others perceive you as a professional.&lt;/p&gt;




&lt;h3&gt;
  
  
  So, how was the journey so far?
&lt;/h3&gt;

&lt;p&gt;I have mixed feelings about the whole experience. Often it felt like we were sailing in this small speedboat, trying to overtake those gigantic, slow moving ships. The enthusiasm was often intoxicating —&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%2Fb2xxugh92ei5ejdfeade.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%2Fb2xxugh92ei5ejdfeade.gif" alt="Gif meme of a clip from Wolf of Wall Street" width="200" height="81"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;The unpredictability also meant that things would go bat-shit crazy at times, especially when something breaks in production and it’s demo day…&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%2Feh8tnbfggkhjrrcdt9d8.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%2Feh8tnbfggkhjrrcdt9d8.gif" alt="Gif meme of a clip from Wolf of Wall Street" width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So the answer to whether you should be working in a startup or not is a little more complex than a simple yes or no —&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identify a place where you have a good pace of learning, a place where the work doesn’t overwhelm you. It’s very important that you not just negotiate for salaries, but that you also negotiate your work hours — a fixed number of hours
that you’ll put in every weekday and no more than that, or perhaps Saturdays off, or perhaps remote working options. Also, read these &lt;a href="https://medium.freecodecamp.com/ten-rules-for-negotiating-a-job-offer-ee17cccbdab6#.zemgjm1i5" rel="noopener noreferrer"&gt;Ten rules for Negotiating a job offer&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;It’s always a good idea to have personal side projects, so ensure you’re able to carve out some time everyday to work on those.&lt;/li&gt;
&lt;li&gt;Many early stage startups will not compromise on work hours at all. This is especially true in Asian countries like India. Moreover if you have less than a couple years experience, then it’s likely that you’ll be paid very less.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my case it was the latter, so here’s what I did —&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I decided to take a sabbatical.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you’ve worked for couple of years and saved some money, consider taking a career break for anytime between few months to half a year and do everything that you always wanted to do. You’ll know when it’s the right time to take one.&lt;/p&gt;

&lt;p&gt;It might feel unnerving at first, but later it starts feeling refreshing. You can take the week long vacation that you always dreamed about. Develop good habits. Meet interesting people. Learn to play a musical instrument, because &lt;a href="https://medium.com/javascript-scene/are-programmer-brains-different-2068a52648a7#.63whpk9a4" rel="noopener noreferrer"&gt;that can help you write software better&lt;/a&gt;. Or go on that road trip that you always wanted to go but never could.&lt;/p&gt;

&lt;p&gt;And in the meantime, constantly&lt;/p&gt;

&lt;h4&gt;
  
  
  Identify your own issues and fix those bugs that exist within you. Implement new features in yourself — so that when you get into the swing of things, the next release of you will be super awesome and something to look forward to.
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;BTW, Would you like to learn my negotiation strategies? Do let me know!&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>startup</category>
      <category>programming</category>
      <category>career</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Know if Programming is your Cup of Tea</title>
      <dc:creator>Ashwin Hariharan</dc:creator>
      <pubDate>Wed, 16 Dec 2020 10:46:19 +0000</pubDate>
      <link>https://dev.to/booleanhunter/how-to-know-if-programming-is-your-cup-of-tea-17gh</link>
      <guid>https://dev.to/booleanhunter/how-to-know-if-programming-is-your-cup-of-tea-17gh</guid>
      <description>&lt;span&gt;Photo by &lt;a href="https://unsplash.com/@silvawebdesigns?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Nathan da Silva&lt;/a&gt; on &lt;a href="https://unsplash.com/collections/39329691/programming-blog-posts/6eece19045b50901c393a40c44c09aa8?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;

&lt;p&gt;I've been working in tech for a while now. When I look back, it seems unreal. Back in 2014 while I was still in college, if someone told me that I'd be employed, I'd be overjoyed — that's how screwed up I was at academics.&lt;/p&gt;

&lt;p&gt;In college I found most of the computer science subjects dry. I mean sure, few of them seemed interesting. But none of them were interesting enough to keep me occupied for very long. C?  A nightmare, learning about pointers, &lt;em&gt;malloc&lt;/em&gt; and stuff. Relational databases? Boooring! Visual C? Yuck, all that code just to render some crappy looking house ? I can draw better! Java? Meh.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Can I just go watch Friends on my laptop ? That's fun!"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For the first three years in engineering, that's pretty much how my feelings towards these subjects were. And as you may have guessed, my grades got screwed up, big time (However in my defense, I did enjoy subjects like Unix and Operating systems!).&lt;/p&gt;

&lt;p&gt;But when you're surrounded by people who are &lt;em&gt;(A)&lt;/em&gt; Great at academics, OR &lt;em&gt;(B)&lt;/em&gt; Programming Geeks, OR &lt;em&gt;(C)&lt;/em&gt; Both A and B — it makes you think. Makes you worry. It makes you fear that you may never land a job, that even if you do — you'll likely get one that sucks. I had no one else to blame but myself. As someone who was still figuring out on what to do, the world of programming not only intrigued, but also scared me.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Where do I even begin? What do I do? Programming seems tough. Would I even enjoy it? If people keep saying that it's easy, does that mean I'm just plain dumb ?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It was around this time when web development was taking over the world by storm; people had already begun to explore and see what it had to offer. We had Web Programming as a subject in the final year of engineering, so I began to read HTML5, JS and CSS3. After lots of Googling and studying few examples from programming tutorials, very soon I could write a small program that did 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%2Fw9zxlqmyuq1gtq6ncq63.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%2Fw9zxlqmyuq1gtq6ncq63.gif" width="1366" height="679"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was easy enough to do — define classes in CSS3 for the sphere with position attributes and define transitions for them. Then use &lt;em&gt;onclick&lt;/em&gt; event handlers in JavaScript to change the class-name of the sphere when you click one of the&lt;br&gt;
boxes, and voila- you got a nice sphere that moves where you tell it to!&lt;/p&gt;

&lt;p&gt;And when I saw that little blue ball moving, this is how I felt —&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is so cool! It wasn't nearly as complicated as I thought it would be!&lt;/li&gt;
&lt;li&gt;The whole program took less than 20 lines of code&lt;/li&gt;
&lt;li&gt;It certainly wasn't boring as writing on some black screen using &lt;em&gt;vi&lt;/em&gt; and seeing the outputs again on a black screen that they called the &lt;em&gt;terminal&lt;/em&gt; when I wrote C or Perl code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I got hooked, and started to learn more and more. Very soon, I evolved from that simple program that moves a blue sphere, to writing my resume using HTML5, CSS3 and JavaScript —&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%2Fd1g1lfubxt836dckqiyy.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%2Fd1g1lfubxt836dckqiyy.gif" width="720" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I knew back then that this was still pretty amateur stuff, but I didn't care. For the first time, I knew that programming was indeed my cup of tea, and this wouldn't have been possible if I didn't write that first program in JavaScript.&lt;/p&gt;

&lt;p&gt;Now there are many people who started out with C++ or Java, loved it, and still are writing software using that. But not all of us are like that. &lt;a href="https://medium.freecodecamp.com/one-does-not-simply-learn-to-code-f25bacdc5b62#.s4oo793ws" rel="noopener noreferrer"&gt;One does not simply learn to code&lt;/a&gt;'. Many people give up without even testing the waters, and the ones that do — they quit later, because the water's too deep for them. They eventually think that programming isn't their cup of tea.&lt;/p&gt;

&lt;p&gt;My first programming language was C. Which is allright, but it can take a while before you finally begin to develop applications. Web development, on the other hand, probably has the lowest barrier to entry.&lt;/p&gt;

&lt;p&gt;JavaScript, the primary language of the web, invokes strong reactions from the programming community — people either love it or hate it. I kinda have a &lt;a href="https://dev.to/booleanhunter/comment/ngld"&gt;love-hate relationship&lt;/a&gt; with it as well, and I guess I'll be stuck with it for as long as I make web applications. I for one can't wait to see the web evolve to that point where we can have alternatives.&lt;/p&gt;

&lt;p&gt;That being said, I still think you should pick it as your first language.&lt;/p&gt;

&lt;p&gt;JavaScript is not judgmental. It does not force you learn a dozen concepts before you can display a static web site on the browser. You don't even have to declare data types. You can execute JavaScript code on a terminal, or if you hate the terminal, you can use the developer console in a web browser, or you can embed it in an HTML page and see the output in the browser. It allows you to do both functional programming and object oriented programming.&lt;/p&gt;




&lt;p&gt;I finally got a job at a startup as a Front End Developer. As it turns out, it acted as a sort of ‘gateway' towards learning further programming concepts. Things that sounded boring before, sound very interesting to me now. Pretty soon&lt;br&gt;
I moved on from front-end to full stack — I started to enjoy the process in architecting data models in MongoDB and PostgreSQL, writing RESTful APIs in NodeJS and Golang, using ReactJS for the client, to even starting &lt;a href="https://github.com/booleanhunter" rel="noopener noreferrer"&gt;open source projects&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I do not intend to 'promote' JavaScript in this post — I'm well aware of it's &lt;a href="https://arielelkin.github.io/articles/why-im-not-a-react-native-developer" rel="noopener noreferrer"&gt;quirks&lt;/a&gt;. But we waste so much time — hours and days, or even months trying to figure out whether programming is meant for us, doing things that we don't want to. We'd rather just quit and choose not to do it, because learning things can be a pain in the ass. At such times, having a tool or a method that takes minimal effort to use and that's beginner-friendly can be very helpful. So before you begin to learn, keeping these points in mind will help you manage your expectations-&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every language has it's pitfalls, so does this one. JS is very easy to pick up, but it is pretty hard to master; so don't take it for granted thinking you'll become some sort of 'ninja' or 'rockstar' within a week.&lt;/li&gt;
&lt;li&gt;Understand that what you write in the beginning will mostly be considered ‘crap' by other programmers. Don't give up. It's okay to write shitty code in the beginning when you learn. But if you discover that you like it, then it's very
essential that you &lt;em&gt;don't&lt;/em&gt; write shitty code.&lt;/li&gt;
&lt;li&gt;Indentation matters a lot. It helps you avoid bugs, it also makes sure that your code doesn't look like a sea of vomit when someone sees it.&lt;/li&gt;
&lt;li&gt;And finally - if you have alternatives to JavaScript, consider using them. The JS ecosystem is highly chaotic, new frameworks keep propping up like flies, and JavaScript fatigue is a real thing.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;"All-right. Where do I begin?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I highly recommend starting from here —&lt;a href="https://medium.freecodecamp.com/from-zero-to-front-end-hero-part-1-7d4f7f0bff02#.d4j59lfic" rel="noopener noreferrer"&gt; From Zero to Front-End Hero (Part 1)&lt;/a&gt;. It's very cool, I wish I had this tutorial when I began my journey.&lt;/p&gt;

&lt;p&gt;This would be enough to help you get started. There are about a million things more to remember, but you wouldn't need to know all of them — not in the beginning at least. What's important to know is whether you would enjoy doing this for the foreseeable future — and the sooner you come to a realization, the better.&lt;/p&gt;

&lt;p&gt;So, take a leap of faith. Who knows, maybe you might actually like it! And if you hone your craft and work diligently, you might just find yourself cast in the role of a lifetime.&lt;/p&gt;




&lt;h5&gt;
  
  
  Interested to learn something new? Check out my repositories:
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/booleanhunter/how-to-build-your-own-uber-for-x-app" rel="noopener noreferrer"&gt;How to build Your Own Uber-for-X App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/booleanhunter-tech-blog" rel="noopener noreferrer"&gt;Beginner-friendly projects&lt;/a&gt; built using JavaScript, Python and more!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>career</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
