<?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: digitallyinduced</title>
    <description>The latest articles on DEV Community by digitallyinduced (@digitallyinduced).</description>
    <link>https://dev.to/digitallyinduced</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%2F567093%2F584c93e8-440e-4fa7-9fd8-b174b236edeb.jpg</url>
      <title>DEV Community: digitallyinduced</title>
      <link>https://dev.to/digitallyinduced</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/digitallyinduced"/>
    <language>en</language>
    <item>
      <title>thin.dev: Realtime-by-default BaaS (Backend-as-a-Service)</title>
      <dc:creator>digitallyinduced</dc:creator>
      <pubDate>Mon, 02 May 2022 16:56:07 +0000</pubDate>
      <link>https://dev.to/digitallyinduced/thindev-realtime-by-default-baas-backend-as-a-service-32po</link>
      <guid>https://dev.to/digitallyinduced/thindev-realtime-by-default-baas-backend-as-a-service-32po</guid>
      <description>&lt;p&gt;In this article, you will learn how to use &lt;a href="https://thin.dev/"&gt;Thin Backend&lt;/a&gt;'s react API to build a chat application like WhatsApp or Signal, deploying it to production along the way - &lt;strong&gt;all within 10 minutes&lt;/strong&gt;!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://thin.dev/"&gt;Thin&lt;/a&gt; is a new Backend-as-a-Service offering by digitally induced! React (or Svelte,...) apps built using its toolkit are realtime by default, meaning it's easier to build an app that reacts to data updates everywhere all the time than it is to keep data out-of-date!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Preparation
&lt;/h2&gt;

&lt;p&gt;I assume a basic understanding of relational databases and React (including hooks) throughout this article. I also assume you have a GitHub account and know how to commit and push using git. You should also have a recent version of node and npm installed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minute 1: Sign up for Thin Backend
&lt;/h2&gt;

&lt;p&gt;Go to &lt;a href="https://thin.dev/NewSession"&gt;https://thin.dev/NewSession&lt;/a&gt;, choosing "Continue with GitHub". After going through all the required steps, you will be greeted by your project overview:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M2qAGfh5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b989v54csklu7ytxubk7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M2qAGfh5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b989v54csklu7ytxubk7.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Minute 2: Create the project
&lt;/h2&gt;

&lt;p&gt;Click on the &lt;code&gt;+ New Project&lt;/code&gt; button and enter a name for your project (for example, "Realtime Chat"), and click on &lt;code&gt;Create Project&lt;/code&gt; when you're happy with it.&lt;/p&gt;

&lt;p&gt;You'll be greeted by the Onboarding:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4Z__Md7f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/82of4f1ya5ypnsy332b1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4Z__Md7f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/82of4f1ya5ypnsy332b1.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to use the Onboarding later to build a to-do app, but we will continue differently.&lt;/p&gt;

&lt;p&gt;Click on &lt;code&gt;Frontend&lt;/code&gt; in the left navigation bar. We'll use this tab to generate a boilerplate frontend.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1DeLqvXu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yktx0kbyxbi83bggf033.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1DeLqvXu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yktx0kbyxbi83bggf033.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;code&gt;+ New Vercel Project&lt;/code&gt;. Vercel is a platform to deploy frontend frameworks and static sites. We'll use it to get our frontend deployed quickly and easily.&lt;/p&gt;

&lt;p&gt;The following screen will greet you:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Sk1_x1xi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ugoiezt46qlak0s194yy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Sk1_x1xi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ugoiezt46qlak0s194yy.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose the &lt;code&gt;TypeScript React Starter&lt;/code&gt; template from among the choices, but feel free to take a quick look at the other options. Don't be surprised that a new tab will open for Vercel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JTc_ytcW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rga04nclv5lqpj7po2p0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JTc_ytcW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rga04nclv5lqpj7po2p0.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose GitHub as a provider, also on the following screen. Enter your project's name (it doesn't have to match exactly) as a repository name:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HtM07TZs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0x01km5qyef6wbq8fox6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HtM07TZs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0x01km5qyef6wbq8fox6.png" alt="Image description" width="640" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, click on &lt;code&gt;Create&lt;/code&gt; to actually create your project. It'll take a short while, after which you will be prompted to configure your project:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b-Psl_xw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mn750jwi4ls9qegndmv7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b-Psl_xw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mn750jwi4ls9qegndmv7.png" alt="Image description" width="640" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By clicking on the &lt;code&gt;Learn More&lt;/code&gt; link, a new tab will open, showing you the value to enter into the input box. This will be the URL at which the backend of the chat app is accessible. Simply click on the displayed link to copy it and enter it on Vercel.&lt;/p&gt;

&lt;p&gt;Then click the &lt;code&gt;Deploy&lt;/code&gt; button. It will take a few seconds, after which your project is already online, and you will be redirected back to Thin!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o1J9qnL9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vw1pijyuniwxio2c6gko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o1J9qnL9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vw1pijyuniwxio2c6gko.png" alt="Image description" width="640" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's check that everything is working so far by clicking the &lt;code&gt;Frontend&lt;/code&gt; button in the left sidebar. It should redirect you to the login for your new chat app. Create a new account here, then continue below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minute 3-4: Modifying the schema
&lt;/h2&gt;

&lt;p&gt;We could already create users because every new Thin Backend project automatically comes with authentication built-in. You can see some of that implementation by checking out the &lt;code&gt;users&lt;/code&gt; table in the schema designer. To do that, click on &lt;code&gt;Schema&lt;/code&gt; in the left navigation bar on Thin Backend. You should see a screen like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EZqxfBL4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9rnyp6q04z2yojgbwtjq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EZqxfBL4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9rnyp6q04z2yojgbwtjq.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, a &lt;code&gt;users&lt;/code&gt; table is already set up. Users, by default, have a few columns that are relevant for Thin Backend that we don't need to care about. For us, only the &lt;code&gt;email&lt;/code&gt; is interesting since we can use it to show to users later to choose with whom to chat.&lt;/p&gt;

&lt;p&gt;At the bottom, you can also see a section titled &lt;code&gt;Policies&lt;/code&gt;. Thin Backend uses PostgreSQL under the hood. Policies are a way to allow users to access and modify the rows of tables only if some conditions are met. By default you can see that users can &lt;code&gt;read if: id = ihp_user_id()&lt;/code&gt; and &lt;code&gt;write if: false&lt;/code&gt;. That means users can only see their own user record, and no user can edit any record.&lt;/p&gt;

&lt;p&gt;For simplicity, we'll allow all users to view the records of all users to have an easier time listing all the options to who we can send messages. To do so, right-click on the policy and select &lt;code&gt;Edit Policy&lt;/code&gt;. A modal will open, allowing you to edit the policy. Change the content of the &lt;code&gt;Visible if&lt;/code&gt; box to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is a terrible idea for an actual production app. We're simply doing this for simplicity and to get something running asap.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A little popover will appear after you save:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--irh5z7YL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06n9ub3vqxcuhwacxlnf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--irh5z7YL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06n9ub3vqxcuhwacxlnf.png" alt="Image description" width="800" height="62"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When making changes to the schema, those changes do not immediately apply to the underlying database. Instead, Thin Backend uses Migrations to allow you to customize how the data in the schema should be converted.&lt;/p&gt;

&lt;p&gt;For now, we'll just ignore the popover and make a few more changes to the schema.&lt;/p&gt;

&lt;p&gt;If we want to allow users to chat with one another, we need to save the messages they send. To do that, we'll create a new table. Click on the little &lt;code&gt;+&lt;/code&gt; next to the &lt;code&gt;Tables&lt;/code&gt; header on the left, and a modal will open, prompting you for a name. Let's call this table &lt;code&gt;messages&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RpNJbFjD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j18ab3x9qn19oco2bi4w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RpNJbFjD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j18ab3x9qn19oco2bi4w.png" alt="Image description" width="500" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;code&gt;Create Table&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can notice a few things now. First, there's a little icon next to the &lt;code&gt;messages&lt;/code&gt; table in the list on the left:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I276RSAn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ei19fgaatk1c7wqautd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I276RSAn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ei19fgaatk1c7wqautd.png" alt="Image description" width="200" height="22"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This icon tells us that we haven't created any policies for this table yet, which will make it inaccessible to our frontend for security reasons. We'll fix that in a bit.&lt;/p&gt;

&lt;p&gt;You should also notice three large buttons in the list of columns. Right now, our messages only have an &lt;code&gt;id&lt;/code&gt;, but they should get some more columns to save who sent them, who they were sent to, when they were sent, and what content they contain. Thin Backend suggests a few columns that are commonly used. Two of those we want: &lt;code&gt;created_at&lt;/code&gt; and &lt;code&gt;user_id&lt;/code&gt;. We'll use &lt;code&gt;created_at&lt;/code&gt; to sort the messages later, and &lt;code&gt;user_id&lt;/code&gt; is a good choice for saving who sent the message.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XmFRCNSe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f7us61woa1fkuero8vxx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XmFRCNSe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f7us61woa1fkuero8vxx.png" alt="Image description" width="880" height="75"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you click on those two suggestions, you will notice a few things happen.&lt;/p&gt;

&lt;p&gt;First, the columns are added to the table as expected. Additionally, at the bottom, you will see a policy has been created. That's because Thin Backend assumes that once you link something to a user, only that user should have access to it. That's a very reasonable default and dangerous to forget! We will only have to make a slight adjustment in our case in a bit.&lt;/p&gt;

&lt;p&gt;Second, below the policies, you can see a few indices created. Thin Backend assumes that queries based on the &lt;code&gt;user_id&lt;/code&gt; and &lt;code&gt;created_at&lt;/code&gt; columns are common, so it creates indices for those too. I suggest keeping them for our project, but if Thin Backend generates something you don't need for your project, you can delete it without issue.&lt;/p&gt;

&lt;p&gt;The goal is to generate the correct thing for 90% of cases and allow you to adjust everything for the other 10% of cases.&lt;/p&gt;

&lt;p&gt;We still require one more column: a &lt;code&gt;recipient_id&lt;/code&gt;, so we know to whom a message was sent!&lt;/p&gt;

&lt;p&gt;Click on the little &lt;code&gt;+&lt;/code&gt; icon in the top-right to create that column. A modal will open, allowing you to configure a new column for the table.&lt;/p&gt;

&lt;p&gt;Let's set the name to &lt;code&gt;recipient_id&lt;/code&gt;. You'll notice Thin Backend notices that a UUID is expected here and selects the correct type for the column for us. We'll only have to change the default value. There's no sensible default since we always want to set the &lt;code&gt;recipient_id&lt;/code&gt; ourselves. Simply change the dropdown’s value from &lt;code&gt;uuid_generate_v4()&lt;/code&gt; to &lt;code&gt;no default&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---Xfb0KVq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/69cbh645zmtcw0a7fmjv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---Xfb0KVq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/69cbh645zmtcw0a7fmjv.png" alt="Image description" width="500" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now click on &lt;code&gt;Create Column&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We now have to add a foreign key constraint to that column, as Thin Backend did not recognize that &lt;code&gt;recipient_id&lt;/code&gt; should reference the &lt;code&gt;users&lt;/code&gt; table. To do so, right-click the newly created column and click on &lt;code&gt;Add Foreign Key Constraint&lt;/code&gt;. A modal should open:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DZziRcnJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3pamzhzd03txuk29b71l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DZziRcnJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3pamzhzd03txuk29b71l.png" alt="Image description" width="500" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The defaults are precisely like we want, so we can click on &lt;code&gt;Add Constraint&lt;/code&gt;. You can see that it worked because it says' FOREIGN KEY: users' on the right side of the column's details.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v-pcXi0H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1j223nnjfm3ddcfjhe7m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v-pcXi0H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1j223nnjfm3ddcfjhe7m.png" alt="Image description" width="880" height="76"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we know when messages are created and who sent them to whom, we can add the last required column, which will contain the message's contents: &lt;code&gt;content&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Click on the little &lt;code&gt;+&lt;/code&gt; in the top-right again, calling the new column &lt;code&gt;content&lt;/code&gt;, keeping the default type of &lt;code&gt;Text&lt;/code&gt; and &lt;code&gt;no default&lt;/code&gt;. After clicking on &lt;code&gt;Create Column&lt;/code&gt;, your &lt;code&gt;messages&lt;/code&gt; table should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6L8gWnck--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jmnf5gukp36qigos1ayr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6L8gWnck--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jmnf5gukp36qigos1ayr.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's only one more step missing before migrating the database to our new schema. We have to edit the &lt;code&gt;messages&lt;/code&gt; policy. Right now, users are only able to read messages they sent themselves, but they also should be able to read messages they receive. Therefore, right-click the &lt;code&gt;Users can manage their messages&lt;/code&gt; policy at the bottom, choosing &lt;code&gt;Edit Policy&lt;/code&gt; in the context menu.&lt;/p&gt;

&lt;p&gt;Change the &lt;code&gt;Visible if&lt;/code&gt; part of the policy from &lt;code&gt;user_id = ihp_user_id()&lt;/code&gt; to &lt;code&gt;user_id = ihp_user_id() OR recipient_id = ihp_user_id()&lt;/code&gt;, which will allow users to read all rows where either the &lt;code&gt;user_id&lt;/code&gt; or the &lt;code&gt;recipient_id&lt;/code&gt; is equal to their user record’s &lt;code&gt;id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After clicking on &lt;code&gt;Update Policy&lt;/code&gt;, your table should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--88av_gvY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qzzmsb6wlzpw38q5d40w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--88av_gvY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qzzmsb6wlzpw38q5d40w.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can finally apply these changes to the database by clicking on the &lt;code&gt;Migrate DB&lt;/code&gt; button at the bottom of the screen. Thin Backend will automatically generate a new migration. Migrations are nothing more than the SQL statements required to get the current database schema to match the schema with our edits. If we had production data already, we might have to be careful in converting from one state of the database to the next to correctly convert the data, which would mean manually adjusting the generated migration. Since we don't have any important data yet, though (only the user we created when signing up to our app), we can safely save and run the generated migration by clicking on the &lt;code&gt;Run Migration&lt;/code&gt; button:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gw39qi6N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0i89m0zhok4e1kf6yj19.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gw39qi6N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0i89m0zhok4e1kf6yj19.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After clicking the button, the migration will run, which should take at most a few seconds, after which you will see a list of past migrations, currently only containing the one we just created. A little green arrow at the right indicates that Thin Backend executed the migration successfully:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rJoeqEJx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7zgrh7aqo3ks6dvntnjr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rJoeqEJx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7zgrh7aqo3ks6dvntnjr.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By clicking on &lt;code&gt;Schema Designer&lt;/code&gt; in the top navigation, you can see that the &lt;code&gt;Unmigrated Changes&lt;/code&gt; message is now gone, indicating that the database matches what we see in the &lt;code&gt;Schema Designer&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minute 4: Cloning the Frontend locally
&lt;/h2&gt;

&lt;p&gt;Now that we've finished our backend, we can take care of our frontend. The first step will be to clone our generated frontend repository so that we can work with it locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/YOURUSERNAME/REPOSITORYNAME.git
&lt;span class="nb"&gt;cd &lt;/span&gt;REPOSITORYNAME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you open the project with your favorite code editor, for example, VSCode, you will see the following folder structure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KoBqr2G9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q106ozflxpk0b7pn2kbg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KoBqr2G9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q106ozflxpk0b7pn2kbg.png" alt="Image description" width="300" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will primarily be working within the &lt;code&gt;app.tsx&lt;/code&gt; file, containing all the react code. If you were to create a production-ready app, you would, of course, set up the project to your liking, but for simplicity's sake, we will stay in a single file for this article.&lt;/p&gt;

&lt;p&gt;Before we go and write code, we'll want to get a development server running and connected to our backend to verify that everything works as expected before deploying it.&lt;/p&gt;

&lt;p&gt;We must first tell the frontend where it can access our backend by setting the &lt;code&gt;BACKEND_URL&lt;/code&gt; environment variable. The easiest way to do that is to create a file called &lt;code&gt;.env&lt;/code&gt; in the project directory and add a line 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;&lt;span class="nv"&gt;BACKEND_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://REPLACE_ME 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can get the value for the right of the equals sign from Thin Backend itself - it's the same value we entered when we created the project on Vercel. You can find it by clicking on &lt;code&gt;Docs&lt;/code&gt; in the left navigation. You will see a section in the right sidebar, as in the screenshot below. Simply click on the URL to copy it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Kg9WeCp0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ktsfxieshf50pvq4pqp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Kg9WeCp0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ktsfxieshf50pvq4pqp.png" alt="Image description" width="293" height="123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So in my case, the final &lt;code&gt;.env&lt;/code&gt; file would look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ptihUgiN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/94lkjk9uz1jo43mqseaj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ptihUgiN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/94lkjk9uz1jo43mqseaj.png" alt="Image description" width="495" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, let's test that everything's working as expected by first installing all dependencies and then starting the development server:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;If you now open &lt;code&gt;localhost:3000&lt;/code&gt;, you will be redirected to a login form, where you can log in using the details you used when creating an account on your own site earlier - or you can create a new user, of course.&lt;/p&gt;

&lt;p&gt;Once logged in, you should see a mostly empty screen, apart from a slightly darkened bar at the top with your email. If you click on your email, you get the option to log out again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gVsU5ntJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vio9xo8h815ypcusfvst.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gVsU5ntJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vio9xo8h815ypcusfvst.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last thing we'll do before we implement our frontend is to install the project-specific types, so we can use our editor's autocompletion and don't get any type errors in our editor.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;Schema Designer&lt;/code&gt; on Thin Backend, and click on &lt;code&gt;Type Definitions&lt;/code&gt; in the top navigation. By clicking on the &lt;code&gt;npm install ...&lt;/code&gt; command for TypeScript, and running that locally in our project's directory, we should be all set!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;clone&lt;/li&gt;
&lt;li&gt;add BACKEND_URL env variable&lt;/li&gt;
&lt;li&gt;install types&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Minute 5: Listing users to chat with
&lt;/h2&gt;

&lt;p&gt;A chat app without the option for users to chat with others is pretty useless. To chat with someone else, we first need to choose with whom to chat. For simplicity, we'll simply list all users that signed up for our app - so everyone can send messages to everyone else.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;app.tsx&lt;/code&gt; file. There's a bit of code in there already, giving us a basic structure. Feel free to take a look to understand what's going on. Most imports are added only for convenience; they aren't actually used yet.&lt;/p&gt;

&lt;p&gt;For listing the users, let’s create a component called &lt;code&gt;Users&lt;/code&gt; to list all users we can chat with. To do so, create a new function in &lt;code&gt;app.tsx&lt;/code&gt; called &lt;code&gt;Users&lt;/code&gt;. I’ll add the function after the &lt;code&gt;AppNavbar&lt;/code&gt; component. For now, I’ll simply return an empty &lt;code&gt;div&lt;/code&gt; from it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Users&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's render that component by adding it below the &lt;code&gt;&amp;lt;AppNavbar/&amp;gt;&lt;/code&gt; line in the &lt;code&gt;App&lt;/code&gt; component (between lines 15 and 16). The &lt;code&gt;App&lt;/code&gt; component should now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// With `useQuery()` you can access your database:&lt;/span&gt;
    &lt;span class="c1"&gt;// &lt;/span&gt;
    &lt;span class="c1"&gt;//     const todos = useQuery(query('todos').orderBy('createdAt'));&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ThinBackend&lt;/span&gt; &lt;span class="na"&gt;requireLogin&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AppNavbar&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ThinBackend&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's get all users from the backend by modifying the &lt;code&gt;Users&lt;/code&gt; component. Inside it, add a new variable called &lt;code&gt;users&lt;/code&gt;, setting its value to the result of the &lt;code&gt;useQuery&lt;/code&gt; hook from Thin Backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&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;useQuery&lt;/code&gt; hook allows us to fetch information from the backend, which will automatically refresh in realtime whenever the data on the backend changes. Your editor might give you a type error right now, as &lt;code&gt;useQuery&lt;/code&gt; expects a query as an argument, so it knows what to fetch. Let's pass it a simple query to fetch all users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&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;You might have noticed that you got autocompletion for every part of this, which is one benefit of using typescript and installing the types provided by Thin Backend.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;users&lt;/code&gt; variable now contains the list of all users or &lt;code&gt;null&lt;/code&gt; if the fetch isn't completed. Let's deal with that second case first, by adding a simple &lt;code&gt;if&lt;/code&gt; statement to return a "Loading..." message to display:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otherwise, we’ll render the users as a list like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;users&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;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-link"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&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;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Currently, the &lt;code&gt;Users&lt;/code&gt; component looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Users&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;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&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;users&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;users&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;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-link"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="si"&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;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you open the local app in your browser, you might see a short "Loading..." message appear, quickly replaced by an entry for every user you created so far - which is probably just yourself.               &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WZ-Z4Flb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x4l27ywnvibkpwf10bl1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WZ-Z4Flb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x4l27ywnvibkpwf10bl1.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before we continue, let's create a few users to chat with, so it's not quite as evident that we're only talking to ourselves. Log out and create 2-3 new user accounts, so the list is a bit more populated:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You might be redirected to the deployed version of your app after creating accounts. If that happens, simply go back to &lt;code&gt;[localhost:3000](http://localhost:3000)&lt;/code&gt; to create the next account or log in there.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P_DswICM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9gpm7nuaxwnny1ivbk2z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P_DswICM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9gpm7nuaxwnny1ivbk2z.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With a few more users available to chat, we can now hide ourselves from the list by adding a simple filter to the query.&lt;/p&gt;

&lt;p&gt;Let’s change the line that defines the &lt;code&gt;users&lt;/code&gt; variable to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;whereNot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you check the list now, the user you logged in as should not appear in the list anymore.&lt;/p&gt;

&lt;p&gt;We modified the &lt;code&gt;query('users')&lt;/code&gt; part using &lt;code&gt;.whereNot('id', getCurrentUserId())&lt;/code&gt;. &lt;code&gt;getCurrentUserId&lt;/code&gt; is a function imported from &lt;code&gt;thin-backend&lt;/code&gt; which will return the current user id as long as you are logged in. &lt;code&gt;whereNot&lt;/code&gt; allows you to filter the data in the database by only including rows where the column passed as a first argument ("id") does not equal the second argument. So effectively, we query for all users where the "id" does not match the currently logged-in user's id, resulting in all users except the one we logged in as.&lt;/p&gt;

&lt;p&gt;Now that we have a list of users to chat with, let's quickly allow selecting them. We'll add a simple state variable to the &lt;code&gt;App&lt;/code&gt; component to keep track of the user we've currently selected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// add this import so we can make TypeScript happy&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&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;thin-backend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// add this to the App component&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;selectedChat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSelectedChat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s add a &lt;code&gt;onUserSelect&lt;/code&gt; prop to the &lt;code&gt;Users&lt;/code&gt; component which is called by an &lt;code&gt;onClick&lt;/code&gt; handler on the &lt;code&gt;button&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;onUserSelect&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;onUserSelect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
                &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;btn btn-link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;onUserSelect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
            &lt;span class="o"&gt;&amp;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 we can pass the &lt;code&gt;setSelectedProp&lt;/code&gt; function to the &lt;code&gt;Users&lt;/code&gt; component with that prop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// replace this line in the App component&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// with this one&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Users&lt;/span&gt; &lt;span class="na"&gt;onUserSelect&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setSelectedChat&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the &lt;code&gt;App&lt;/code&gt; component keeps track of the user we're chatting with!&lt;/p&gt;

&lt;h2&gt;
  
  
  Minute 6: Allow sending messages to users
&lt;/h2&gt;

&lt;p&gt;Next, let's create the &lt;code&gt;Chat&lt;/code&gt; component, which we'll eventually use to chat with another user; for now, we'll just use it to display the currently selected user.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;Chat&lt;/code&gt; function component that accepts a &lt;code&gt;user&lt;/code&gt; prop of type &lt;code&gt;User&lt;/code&gt; and renders a simple &lt;code&gt;div&lt;/code&gt; containing the user’s email:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Chat&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="nl"&gt;user&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&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;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s render that component from our &lt;code&gt;App&lt;/code&gt; component by adding it below the &lt;code&gt;Users&lt;/code&gt; component if the &lt;code&gt;selectedChat&lt;/code&gt; isn’t &lt;code&gt;null&lt;/code&gt;, and pass the &lt;code&gt;selectedChat&lt;/code&gt; to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;selectedChat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSelectedChat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ThinBackend&lt;/span&gt; &lt;span class="na"&gt;requireLogin&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AppNavbar&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Users&lt;/span&gt; &lt;span class="na"&gt;onUserSelect&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setSelectedChat&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;selectedChat&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Chat&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;selectedChat&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ThinBackend&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When clicking on the different users, the email below the list should now change in the browser.&lt;/p&gt;

&lt;p&gt;Let’s change the markup a bit to render the &lt;code&gt;Chat&lt;/code&gt; component on the right side next to the &lt;code&gt;Users&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;selectedChat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSelectedChat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ThinBackend&lt;/span&gt; &lt;span class="na"&gt;requireLogin&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AppNavbar&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'row'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'col'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Users&lt;/span&gt; &lt;span class="na"&gt;onUserSelect&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setSelectedChat&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'col'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;selectedChat&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Chat&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;selectedChat&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ThinBackend&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Things should now look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ow1iAaze--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6hgu0dgdl1pydihybwo8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ow1iAaze--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6hgu0dgdl1pydihybwo8.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have a &lt;code&gt;Chat&lt;/code&gt; component with access to the user we've selected, we can finally create the necessary code to send messages to that user.&lt;/p&gt;

&lt;p&gt;Let’s add a &lt;code&gt;form&lt;/code&gt; element inside the &lt;code&gt;Chat&lt;/code&gt; component with a simple input and a submit button, which we can use to send messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Chat&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="nl"&gt;user&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&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;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"input-group mt-4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
                &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"form-control"&lt;/span&gt;
                &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Type a message..."&lt;/span&gt;
                &lt;span class="na"&gt;aria-describedby&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"send-button"&lt;/span&gt;
                &lt;span class="na"&gt;autoFocus&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'submit'&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-primary"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Send&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have added a bit of simple markup to make it look prettier.&lt;/p&gt;

&lt;p&gt;To make this work, we'll need to handle the submit event of the form - we can do that by creating a &lt;code&gt;handleSend&lt;/code&gt; function within the &lt;code&gt;Chat&lt;/code&gt; component that we pass to the &lt;code&gt;onSubmit&lt;/code&gt; attribute of the &lt;code&gt;form&lt;/code&gt; element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// add this before the return in Chat:&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleSend&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;FormEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLFormElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&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="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;namedItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLInputElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="c1"&gt;// this is where the magic happens:&lt;/span&gt;
      &lt;span class="nx"&gt;createRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages&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;recipientId&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;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important call here is the one to &lt;code&gt;createRecord&lt;/code&gt;. Thin Backend provides this function. The first argument tells Thin Backend to create a new record in the &lt;code&gt;messages&lt;/code&gt; table, and the second argument contains all the required fields we need to set for the record to be complete (it'll use the default values if we don't override them). Since Thin Backend uses the currently logged-in user's id as &lt;code&gt;user_id&lt;/code&gt; by default (as defined in the schema) and generates a new &lt;code&gt;id&lt;/code&gt; for the message automatically, we only need to set the &lt;code&gt;recipient_id&lt;/code&gt; to the id of the user we've selected to chat with, and the &lt;code&gt;content&lt;/code&gt; to the input's value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minute 7: Display sent messages
&lt;/h2&gt;

&lt;p&gt;There's only one problem: once we send the message, there's no way to read it!&lt;/p&gt;

&lt;p&gt;So the next step is to display all messages sent into the current chat.&lt;/p&gt;

&lt;p&gt;To do so, we'll write another query using &lt;code&gt;useQuery&lt;/code&gt; inside the &lt;code&gt;Chat&lt;/code&gt; component. Let's start simple and get all messages the user has access to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can display this list of messages by adding these lines above the &lt;code&gt;form&lt;/code&gt; tag and below the &lt;code&gt;{user.email}&lt;/code&gt; line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;messages&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;message&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;from-me&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;to-me&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'message-content'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&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;content&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All we're doing here is mapping over the messages, displaying each message's content inside two divs. We also assign CSS classes based on whether the current user sent the message or received it by comparing the message's &lt;code&gt;userId&lt;/code&gt; to the current user's id (accessed with &lt;code&gt;getCurrentUserId()&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;For the CSS classes to do anything, add the following styles to &lt;code&gt;public/app.css&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.message.from-me&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.message.to-me&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.message&lt;/span&gt; &lt;span class="nc"&gt;.message-content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.message.to-me&lt;/span&gt; &lt;span class="nc"&gt;.message-content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;lightgray&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.message.from-me&lt;/span&gt; &lt;span class="nc"&gt;.message-content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;lightblue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&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;I won't go over this in detail; it just makes things look a bit better. Feel free to take a closer look if you want, though.&lt;/p&gt;

&lt;p&gt;After changing the CSS, you might have to reload the app in the browser to see the effects.&lt;/p&gt;

&lt;p&gt;Currently, we display all messages in every chat and no particular order. Since that doesn't make sense, let's modify the query we use to fetch the messages only to fetch those messages sent to us from the user we have selected to chat with and those we sent to them. After &lt;code&gt;query('messages')&lt;/code&gt;, we can add a &lt;code&gt;.where(..)&lt;/code&gt; condition as we did earlier, filtering the messages we sent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&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;That still doesn’t limit the messages to those sent to the selected person though, so we’ll add another &lt;code&gt;.where&lt;/code&gt; condition based on the recipient:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recipientId&lt;/span&gt;&lt;span class="dl"&gt;'&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Multiple where conditions like this will be combined using &lt;code&gt;AND&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now we have all of the messages we sent, but we’re now missing the messages we received. To fix that, we can add a set of conditions using &lt;code&gt;.or&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recipientId&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recipientId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&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;.or&lt;/code&gt; takes a list of conditions just as you can add them to &lt;code&gt;query('messages')&lt;/code&gt;. You might have to add an import for &lt;code&gt;where&lt;/code&gt; to the import list of &lt;code&gt;thin-backend&lt;/code&gt; at the top of the file.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.or&lt;/code&gt; uses all previous conditions for the left side of the resulting &lt;code&gt;OR&lt;/code&gt; condition. If you want a bit more clarity, you can wrap the initial conditions with a single call to &lt;code&gt;.where&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recipientId&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recipientId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&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;I, personally, don't think that's necessary, though.&lt;/p&gt;

&lt;p&gt;If we check the result in the browser, messages will now only show in the correct chat! However, even if the order of messages might look correct, we have done nothing to ensure this will stay consistent. To always ensure that messages are displayed in the correct order, we can add a simple &lt;code&gt;.orderBy('createdAt')&lt;/code&gt; to the query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recipientId&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recipientId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&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;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;createdAt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, the correct messages show, in the proper order!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SCVHZ3DV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a4wn9him6z6wpwev5okd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SCVHZ3DV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a4wn9him6z6wpwev5okd.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Minute 8: Realtime
&lt;/h2&gt;

&lt;p&gt;We haven't done anything to make our app realtime yet... But the &lt;code&gt;useQuery&lt;/code&gt; hook is realtime by default, meaning we don't have to do anything! Thin Backend will push new data to each client who requires it over a WebSocket connection. You can quickly try it out by opening a new private window in which you sign in as another user, sending messages between clients - the messages will show up in each browser without requiring a reload of the page!&lt;/p&gt;

&lt;p&gt;I guess we can use this time for doing something else then.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minute 8b: Commit and push (deploy)
&lt;/h2&gt;

&lt;p&gt;Now that we've finished the frontend let's deploy it to production. Commit all changes and push them to your repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add app.tsx public/app.css package.json package-lock.json
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"implement realtime chat"&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vercel will notice a change on the main branch of our repository and deploy the changes automatically.&lt;/p&gt;

&lt;p&gt;Enjoy your new app and possibly spare time!&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving on
&lt;/h2&gt;

&lt;p&gt;From here, you can continue playing around with &lt;a href="https://thin.dev/"&gt;Thin Backend&lt;/a&gt;. Maybe you want to allow users to set a different display name instead of displaying their email? Maybe you want users to request a chat with others, so they can't just send messages to anyone willy-nilly? Perhaps you want to implement group chat functionality?&lt;/p&gt;

&lt;p&gt;If you don't feel comfortable trying things out alone, you might also want to follow the normal Onboarding process. It'll lead you through creating another app, similar to this article, but possibly give you some new information not covered here or covered differently.&lt;/p&gt;

&lt;p&gt;What will you build with &lt;a href="https://thin.dev/"&gt;Thin Backend&lt;/a&gt;? Tell us in the comments!    &lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Build and deploy a real-time react chat app in under 10 minutes</title>
      <dc:creator>digitallyinduced</dc:creator>
      <pubDate>Fri, 25 Feb 2022 10:11:07 +0000</pubDate>
      <link>https://dev.to/digitallyinduced/build-and-deploy-a-real-time-react-chat-app-in-under-10-minutes-2gie</link>
      <guid>https://dev.to/digitallyinduced/build-and-deploy-a-real-time-react-chat-app-in-under-10-minutes-2gie</guid>
      <description>&lt;p&gt;In this article, you will learn how to use &lt;a href="https://ihpbackend.digitallyinduced.com/"&gt;IHP Backend&lt;/a&gt;'s react API to build a chat application like WhatsApp or Signal, deploying it to production along the way - all within 10 minutes!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://ihpbackend.digitallyinduced.com/"&gt;IHP Backend&lt;/a&gt; is a new Backend-as-a-Service offering by digitally induced! React apps built using its toolkit are realtime by default, meaning it's easier to build an app that reacts to data updates everywhere all the time than it is to keep data out-of-date!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Preparation
&lt;/h2&gt;

&lt;p&gt;I assume a basic understanding of relational databases and React (including hooks) throughout this article. I also assume you have a GitHub account and know how to commit and push using git. You should also have a recent version of node and npm installed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minute 1: Sign up for IHP Backend
&lt;/h2&gt;

&lt;p&gt;Go to &lt;a href="https://ihpbackend.digitallyinduced.com/NewSession"&gt;https://ihpbackend.digitallyinduced.com/NewSession&lt;/a&gt;, choosing "Continue with GitHub". After going through all the required steps, you will be greeted by your project overview:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M2qAGfh5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b989v54csklu7ytxubk7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M2qAGfh5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b989v54csklu7ytxubk7.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Minute 2: Create the project
&lt;/h2&gt;

&lt;p&gt;Click on the &lt;code&gt;+ New Project&lt;/code&gt; button and enter a name for your project (for example, "Realtime Chat"), and click on &lt;code&gt;Create Project&lt;/code&gt; when you're happy with it.&lt;/p&gt;

&lt;p&gt;You'll be greeted by the Onboarding:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4Z__Md7f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/82of4f1ya5ypnsy332b1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4Z__Md7f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/82of4f1ya5ypnsy332b1.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to use the Onboarding later to build a to-do app, but we will continue differently.&lt;/p&gt;

&lt;p&gt;Click on &lt;code&gt;Frontend&lt;/code&gt; in the left navigation bar. We'll use this tab to generate a boilerplate frontend.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1DeLqvXu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yktx0kbyxbi83bggf033.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1DeLqvXu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yktx0kbyxbi83bggf033.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;code&gt;+ New Vercel Project&lt;/code&gt;. Vercel is a platform to deploy frontend frameworks and static sites. We'll use it to get our frontend deployed quickly and easily.&lt;/p&gt;

&lt;p&gt;The following screen will greet you:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Sk1_x1xi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ugoiezt46qlak0s194yy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Sk1_x1xi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ugoiezt46qlak0s194yy.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose the &lt;code&gt;TypeScript React Starter&lt;/code&gt; template from among the choices, but feel free to take a quick look at the other options. Don't be surprised that a new tab will open for Vercel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JTc_ytcW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rga04nclv5lqpj7po2p0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JTc_ytcW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rga04nclv5lqpj7po2p0.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose GitHub as a provider, also on the following screen. Enter your project's name (it doesn't have to match exactly) as a repository name:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HtM07TZs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0x01km5qyef6wbq8fox6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HtM07TZs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0x01km5qyef6wbq8fox6.png" alt="Image description" width="640" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, click on &lt;code&gt;Create&lt;/code&gt; to actually create your project. It'll take a short while, after which you will be prompted to configure your project:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b-Psl_xw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mn750jwi4ls9qegndmv7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b-Psl_xw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mn750jwi4ls9qegndmv7.png" alt="Image description" width="640" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By clicking on the &lt;code&gt;Learn More&lt;/code&gt; link, a new tab will open, showing you the value to enter into the input box. This will be the URL at which the backend of the chat app is accessible. Simply click on the displayed link to copy it and enter it on Vercel.&lt;/p&gt;

&lt;p&gt;Then click the &lt;code&gt;Deploy&lt;/code&gt; button. It will take a few seconds, after which your project is already online, and you will be redirected back to IHP Backend!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o1J9qnL9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vw1pijyuniwxio2c6gko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o1J9qnL9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vw1pijyuniwxio2c6gko.png" alt="Image description" width="640" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's check that everything is working so far by clicking the &lt;code&gt;Frontend&lt;/code&gt; button in the left sidebar. It should redirect you to the login for your new chat app. Create a new account here, then continue below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minute 3-4: Modifying the schema
&lt;/h2&gt;

&lt;p&gt;We could already create users because every new IHP Backend project automatically comes with authentication built-in. You can see some of that implementation by checking out the &lt;code&gt;users&lt;/code&gt; table in the schema designer. To do that, click on &lt;code&gt;Schema&lt;/code&gt; in the left navigation bar on IHP Backend. You should see a screen like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EZqxfBL4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9rnyp6q04z2yojgbwtjq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EZqxfBL4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9rnyp6q04z2yojgbwtjq.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, a &lt;code&gt;users&lt;/code&gt; table is already set up. Users, by default, have a few columns that are relevant for IHP Backend that we don't need to care about. For us, only the &lt;code&gt;email&lt;/code&gt; is interesting since we can use it to show to users later to choose with whom to chat.&lt;/p&gt;

&lt;p&gt;At the bottom, you can also see a section titled &lt;code&gt;Policies&lt;/code&gt;. IHP Backend uses PostgreSQL under the hood. Policies are a way to allow users to access and modify the rows of tables only if some conditions are met. By default you can see that users can &lt;code&gt;read if: id = ihp_user_id()&lt;/code&gt; and &lt;code&gt;write if: false&lt;/code&gt;. That means users can only see their own user record, and no user can edit any record.&lt;/p&gt;

&lt;p&gt;For simplicity, we'll allow all users to view the records of all users to have an easier time listing all the options to who we can send messages. To do so, right-click on the policy and select &lt;code&gt;Edit Policy&lt;/code&gt;. A modal will open, allowing you to edit the policy. Change the content of the &lt;code&gt;Visible if&lt;/code&gt; box to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is a terrible idea for an actual production app. We're simply doing this for simplicity and to get something running asap.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A little popover will appear after you save:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--irh5z7YL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06n9ub3vqxcuhwacxlnf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--irh5z7YL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06n9ub3vqxcuhwacxlnf.png" alt="Image description" width="800" height="62"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When making changes to the schema, those changes do not immediately apply to the underlying database. Instead, IHP Backend uses Migrations to allow you to customize how the data in the schema should be converted.&lt;/p&gt;

&lt;p&gt;For now, we'll just ignore the popover and make a few more changes to the schema.&lt;/p&gt;

&lt;p&gt;If we want to allow users to chat with one another, we need to save the messages they send. To do that, we'll create a new table. Click on the little &lt;code&gt;+&lt;/code&gt; next to the &lt;code&gt;Tables&lt;/code&gt; header on the left, and a modal will open, prompting you for a name. Let's call this table &lt;code&gt;messages&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RpNJbFjD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j18ab3x9qn19oco2bi4w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RpNJbFjD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j18ab3x9qn19oco2bi4w.png" alt="Image description" width="500" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;code&gt;Create Table&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can notice a few things now. First, there's a little icon next to the &lt;code&gt;messages&lt;/code&gt; table in the list on the left:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I276RSAn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ei19fgaatk1c7wqautd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I276RSAn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ei19fgaatk1c7wqautd.png" alt="Image description" width="200" height="22"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This icon tells us that we haven't created any policies for this table yet, which will make it inaccessible to our frontend for security reasons. We'll fix that in a bit.&lt;/p&gt;

&lt;p&gt;You should also notice three large buttons in the list of columns. Right now, our messages only have an &lt;code&gt;id&lt;/code&gt;, but they should get some more columns to save who sent them, who they were sent to, when they were sent, and what content they contain. IHP Backend suggests a few columns that are commonly used. Two of those we want: &lt;code&gt;created_at&lt;/code&gt; and &lt;code&gt;user_id&lt;/code&gt;. We'll use &lt;code&gt;created_at&lt;/code&gt; to sort the messages later, and &lt;code&gt;user_id&lt;/code&gt; is a good choice for saving who sent the message.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XmFRCNSe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f7us61woa1fkuero8vxx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XmFRCNSe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f7us61woa1fkuero8vxx.png" alt="Image description" width="880" height="75"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you click on those two suggestions, you will notice a few things happen.&lt;/p&gt;

&lt;p&gt;First, the columns are added to the table as expected. Additionally, at the bottom, you will see a policy has been created. That's because IHP Backend assumes that once you link something to a user, only that user should have access to it. That's a very reasonable default and dangerous to forget! We will only have to make a slight adjustment in our case in a bit.&lt;/p&gt;

&lt;p&gt;Second, below the policies, you can see a few indices created. IHP Backend assumes that queries based on the &lt;code&gt;user_id&lt;/code&gt; and &lt;code&gt;created_at&lt;/code&gt; columns are common, so it creates indices for those too. I suggest keeping them for our project, but if IHP Backend generates something you don't need for your project, you can delete it without issue.&lt;/p&gt;

&lt;p&gt;The goal is to generate the correct thing for 90% of cases and allow you to adjust everything for the other 10% of cases.&lt;/p&gt;

&lt;p&gt;We still require one more column: a &lt;code&gt;recipient_id&lt;/code&gt;, so we know to whom a message was sent!&lt;/p&gt;

&lt;p&gt;Click on the little &lt;code&gt;+&lt;/code&gt; icon in the top-right to create that column. A modal will open, allowing you to configure a new column for the table.&lt;/p&gt;

&lt;p&gt;Let's set the name to &lt;code&gt;recipient_id&lt;/code&gt;. You'll notice IHP Backend notices that a UUID is expected here and selects the correct type for the column for us. We'll only have to change the default value. There's no sensible default since we always want to set the &lt;code&gt;recipient_id&lt;/code&gt; ourselves. Simply change the dropdown’s value from &lt;code&gt;uuid_generate_v4()&lt;/code&gt; to &lt;code&gt;no default&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---Xfb0KVq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/69cbh645zmtcw0a7fmjv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---Xfb0KVq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/69cbh645zmtcw0a7fmjv.png" alt="Image description" width="500" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now click on &lt;code&gt;Create Column&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We now have to add a foreign key constraint to that column, as IHP Backend did not recognize that &lt;code&gt;recipient_id&lt;/code&gt; should reference the &lt;code&gt;users&lt;/code&gt; table. To do so, right-click the newly created column and click on &lt;code&gt;Add Foreign Key Constraint&lt;/code&gt;. A modal should open:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DZziRcnJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3pamzhzd03txuk29b71l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DZziRcnJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3pamzhzd03txuk29b71l.png" alt="Image description" width="500" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The defaults are precisely like we want, so we can click on &lt;code&gt;Add Constraint&lt;/code&gt;. You can see that it worked because it says' FOREIGN KEY: users' on the right side of the column's details.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v-pcXi0H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1j223nnjfm3ddcfjhe7m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v-pcXi0H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1j223nnjfm3ddcfjhe7m.png" alt="Image description" width="880" height="76"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we know when messages are created and who sent them to whom, we can add the last required column, which will contain the message's contents: &lt;code&gt;content&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Click on the little &lt;code&gt;+&lt;/code&gt; in the top-right again, calling the new column &lt;code&gt;content&lt;/code&gt;, keeping the default type of &lt;code&gt;Text&lt;/code&gt; and &lt;code&gt;no default&lt;/code&gt;. After clicking on &lt;code&gt;Create Column&lt;/code&gt;, your &lt;code&gt;messages&lt;/code&gt; table should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6L8gWnck--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jmnf5gukp36qigos1ayr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6L8gWnck--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jmnf5gukp36qigos1ayr.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's only one more step missing before migrating the database to our new schema. We have to edit the &lt;code&gt;messages&lt;/code&gt; policy. Right now, users are only able to read messages they sent themselves, but they also should be able to read messages they receive. Therefore, right-click the &lt;code&gt;Users can manage their messages&lt;/code&gt; policy at the bottom, choosing &lt;code&gt;Edit Policy&lt;/code&gt; in the context menu.&lt;/p&gt;

&lt;p&gt;Change the &lt;code&gt;Visible if&lt;/code&gt; part of the policy from &lt;code&gt;user_id = ihp_user_id()&lt;/code&gt; to &lt;code&gt;user_id = ihp_user_id() OR recipient_id = ihp_user_id()&lt;/code&gt;, which will allow users to read all rows where either the &lt;code&gt;user_id&lt;/code&gt; or the &lt;code&gt;recipient_id&lt;/code&gt; is equal to their user record’s &lt;code&gt;id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After clicking on &lt;code&gt;Update Policy&lt;/code&gt;, your table should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--88av_gvY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qzzmsb6wlzpw38q5d40w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--88av_gvY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qzzmsb6wlzpw38q5d40w.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can finally apply these changes to the database by clicking on the &lt;code&gt;Migrate DB&lt;/code&gt; button at the bottom of the screen. IHP Backend will automatically generate a new migration. Migrations are nothing more than the SQL statements required to get the current database schema to match the schema with our edits. If we had production data already, we might have to be careful in converting from one state of the database to the next to correctly convert the data, which would mean manually adjusting the generated migration. Since we don't have any important data yet, though (only the user we created when signing up to our app), we can safely save and run the generated migration by clicking on the &lt;code&gt;Run Migration&lt;/code&gt; button:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gw39qi6N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0i89m0zhok4e1kf6yj19.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gw39qi6N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0i89m0zhok4e1kf6yj19.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After clicking the button, the migration will run, which should take at most a few seconds, after which you will see a list of past migrations, currently only containing the one we just created. A little green arrow at the right indicates that IHP Backend executed the migration successfully:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rJoeqEJx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7zgrh7aqo3ks6dvntnjr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rJoeqEJx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7zgrh7aqo3ks6dvntnjr.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By clicking on &lt;code&gt;Schema Designer&lt;/code&gt; in the top navigation, you can see that the &lt;code&gt;Unmigrated Changes&lt;/code&gt; message is now gone, indicating that the database matches what we see in the &lt;code&gt;Schema Designer&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minute 4: Cloning the Frontend locally
&lt;/h2&gt;

&lt;p&gt;Now that we've finished our backend, we can take care of our frontend. The first step will be to clone our generated frontend repository so that we can work with it locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/YOURUSERNAME/REPOSITORYNAME.git
&lt;span class="nb"&gt;cd &lt;/span&gt;REPOSITORYNAME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you open the project with your favorite code editor, for example, VSCode, you will see the following folder structure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KoBqr2G9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q106ozflxpk0b7pn2kbg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KoBqr2G9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q106ozflxpk0b7pn2kbg.png" alt="Image description" width="300" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will primarily be working within the &lt;code&gt;app.tsx&lt;/code&gt; file, containing all the react code. If you were to create a production-ready app, you would, of course, set up the project to your liking, but for simplicity's sake, we will stay in a single file for this article.&lt;/p&gt;

&lt;p&gt;Before we go and write code, we'll want to get a development server running and connected to our backend to verify that everything works as expected before deploying it.&lt;/p&gt;

&lt;p&gt;We must first tell the frontend where it can access our backend by setting the &lt;code&gt;BACKEND_URL&lt;/code&gt; environment variable. The easiest way to do that is to create a file called &lt;code&gt;.env&lt;/code&gt; in the project directory and add a line 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;&lt;span class="nv"&gt;BACKEND_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://REPLACE_ME 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can get the value for the right of the equals sign from IHP Backend itself - it's the same value we entered when we created the project on Vercel. You can find it by clicking on &lt;code&gt;Docs&lt;/code&gt; in the left navigation. You will see a section in the right sidebar, as in the screenshot below. Simply click on the URL to copy it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Kg9WeCp0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ktsfxieshf50pvq4pqp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Kg9WeCp0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ktsfxieshf50pvq4pqp.png" alt="Image description" width="293" height="123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So in my case, the final &lt;code&gt;.env&lt;/code&gt; file would look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ptihUgiN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/94lkjk9uz1jo43mqseaj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ptihUgiN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/94lkjk9uz1jo43mqseaj.png" alt="Image description" width="495" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, let's test that everything's working as expected by first installing all dependencies and then starting the development server:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;If you now open &lt;code&gt;localhost:3000&lt;/code&gt;, you will be redirected to a login form, where you can log in using the details you used when creating an account on your own site earlier - or you can create a new user, of course.&lt;/p&gt;

&lt;p&gt;Once logged in, you should see a mostly empty screen, apart from a slightly darkened bar at the top with your email. If you click on your email, you get the option to log out again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gVsU5ntJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vio9xo8h815ypcusfvst.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gVsU5ntJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vio9xo8h815ypcusfvst.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last thing we'll do before we implement our frontend is to install the project-specific types, so we can use our editor's autocompletion and don't get any type errors in our editor.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;Schema Designer&lt;/code&gt; on IHP Backend, and click on &lt;code&gt;Type Definitions&lt;/code&gt; in the top navigation. By clicking on the &lt;code&gt;npm install ...&lt;/code&gt; command for TypeScript, and running that locally in our project's directory, we should be all set!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;clone&lt;/li&gt;
&lt;li&gt;add BACKEND_URL env variable&lt;/li&gt;
&lt;li&gt;install types&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Minute 5: Listing users to chat with
&lt;/h2&gt;

&lt;p&gt;A chat app without the option for users to chat with others is pretty useless. To chat with someone else, we first need to choose with whom to chat. For simplicity, we'll simply list all users that signed up for our app - so everyone can send messages to everyone else.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;app.tsx&lt;/code&gt; file. There's a bit of code in there already, giving us a basic structure. Feel free to take a look to understand what's going on. Most imports are added only for convenience; they aren't actually used yet.&lt;/p&gt;

&lt;p&gt;For listing the users, let’s create a component called &lt;code&gt;Users&lt;/code&gt; to list all users we can chat with. To do so, create a new function in &lt;code&gt;app.tsx&lt;/code&gt; called &lt;code&gt;Users&lt;/code&gt;. I’ll add the function after the &lt;code&gt;AppNavbar&lt;/code&gt; component. For now, I’ll simply return an empty &lt;code&gt;div&lt;/code&gt; from it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Users&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's render that component by adding it below the &lt;code&gt;&amp;lt;AppNavbar/&amp;gt;&lt;/code&gt; line in the &lt;code&gt;App&lt;/code&gt; component (between lines 15 and 16). The &lt;code&gt;App&lt;/code&gt; component should now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// With `useQuery()` you can access your database:&lt;/span&gt;
    &lt;span class="c1"&gt;// &lt;/span&gt;
    &lt;span class="c1"&gt;//     const todos = useQuery(query('todos').orderBy('createdAt'));&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IHPBackend&lt;/span&gt; &lt;span class="na"&gt;requireLogin&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AppNavbar&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IHPBackend&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's get all users from the backend by modifying the &lt;code&gt;Users&lt;/code&gt; component. Inside it, add a new variable called &lt;code&gt;users&lt;/code&gt;, setting its value to the result of the &lt;code&gt;useQuery&lt;/code&gt; hook from IHP Backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&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;useQuery&lt;/code&gt; hook allows us to fetch information from the backend, which will automatically refresh in realtime whenever the data on the backend changes. Your editor might give you a type error right now, as &lt;code&gt;useQuery&lt;/code&gt; expects a query as an argument, so it knows what to fetch. Let's pass it a simple query to fetch all users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&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;You might have noticed that you got autocompletion for every part of this, which is one benefit of using typescript and installing the types provided by IHP Backend.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;users&lt;/code&gt; variable now contains the list of all users or &lt;code&gt;null&lt;/code&gt; if the fetch isn't completed. Let's deal with that second case first, by adding a simple &lt;code&gt;if&lt;/code&gt; statement to return a "Loading..." message to display:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otherwise, we’ll render the users as a list like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;users&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;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-link"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&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;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Currently, the &lt;code&gt;Users&lt;/code&gt; component looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Users&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;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&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;users&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;users&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;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-link"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="si"&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;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you open the local app in your browser, you might see a short "Loading..." message appear, quickly replaced by an entry for every user you created so far - which is probably just yourself.               &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WZ-Z4Flb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x4l27ywnvibkpwf10bl1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WZ-Z4Flb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x4l27ywnvibkpwf10bl1.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before we continue, let's create a few users to chat with, so it's not quite as evident that we're only talking to ourselves. Log out and create 2-3 new user accounts, so the list is a bit more populated:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You might be redirected to the deployed version of your app after creating accounts. If that happens, simply go back to &lt;code&gt;[localhost:3000](http://localhost:3000)&lt;/code&gt; to create the next account or log in there.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P_DswICM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9gpm7nuaxwnny1ivbk2z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P_DswICM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9gpm7nuaxwnny1ivbk2z.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With a few more users available to chat, we can now hide ourselves from the list by adding a simple filter to the query.&lt;/p&gt;

&lt;p&gt;Let’s change the line that defines the &lt;code&gt;users&lt;/code&gt; variable to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;whereNot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you check the list now, the user you logged in as should not appear in the list anymore.&lt;/p&gt;

&lt;p&gt;We modified the &lt;code&gt;query('users')&lt;/code&gt; part using &lt;code&gt;.whereNot('id', getCurrentUserId())&lt;/code&gt;. &lt;code&gt;getCurrentUserId&lt;/code&gt; is a function imported from &lt;code&gt;ihp-backend&lt;/code&gt; which will return the current user id as long as you are logged in. &lt;code&gt;whereNot&lt;/code&gt; allows you to filter the data in the database by only including rows where the column passed as a first argument ("id") does not equal the second argument. So effectively, we query for all users where the "id" does not match the currently logged-in user's id, resulting in all users except the one we logged in as.&lt;/p&gt;

&lt;p&gt;Now that we have a list of users to chat with, let's quickly allow selecting them. We'll add a simple state variable to the &lt;code&gt;App&lt;/code&gt; component to keep track of the user we've currently selected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// add this import so we can make TypeScript happy&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&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;ihp-backend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// add this to the App component&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;selectedChat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSelectedChat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s add a &lt;code&gt;onUserSelect&lt;/code&gt; prop to the &lt;code&gt;Users&lt;/code&gt; component which is called by an &lt;code&gt;onClick&lt;/code&gt; handler on the &lt;code&gt;button&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;onUserSelect&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;onUserSelect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
                &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;btn btn-link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;onUserSelect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
            &lt;span class="o"&gt;&amp;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 we can pass the &lt;code&gt;setSelectedProp&lt;/code&gt; function to the &lt;code&gt;Users&lt;/code&gt; component with that prop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// replace this line in the App component&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// with this one&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Users&lt;/span&gt; &lt;span class="na"&gt;onUserSelect&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setSelectedChat&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the &lt;code&gt;App&lt;/code&gt; component keeps track of the user we're chatting with!&lt;/p&gt;

&lt;h2&gt;
  
  
  Minute 6: Allow sending messages to users
&lt;/h2&gt;

&lt;p&gt;Next, let's create the &lt;code&gt;Chat&lt;/code&gt; component, which we'll eventually use to chat with another user; for now, we'll just use it to display the currently selected user.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;Chat&lt;/code&gt; function component that accepts a &lt;code&gt;user&lt;/code&gt; prop of type &lt;code&gt;User&lt;/code&gt; and renders a simple &lt;code&gt;div&lt;/code&gt; containing the user’s email:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Chat&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="nl"&gt;user&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&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;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s render that component from our &lt;code&gt;App&lt;/code&gt; component by adding it below the &lt;code&gt;Users&lt;/code&gt; component if the &lt;code&gt;selectedChat&lt;/code&gt; isn’t &lt;code&gt;null&lt;/code&gt;, and pass the &lt;code&gt;selectedChat&lt;/code&gt; to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;selectedChat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSelectedChat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IHPBackend&lt;/span&gt; &lt;span class="na"&gt;requireLogin&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AppNavbar&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Users&lt;/span&gt; &lt;span class="na"&gt;onUserSelect&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setSelectedChat&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;selectedChat&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Chat&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;selectedChat&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IHPBackend&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When clicking on the different users, the email below the list should now change in the browser.&lt;/p&gt;

&lt;p&gt;Let’s change the markup a bit to render the &lt;code&gt;Chat&lt;/code&gt; component on the right side next to the &lt;code&gt;Users&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;selectedChat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSelectedChat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IHPBackend&lt;/span&gt; &lt;span class="na"&gt;requireLogin&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AppNavbar&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'row'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'col'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Users&lt;/span&gt; &lt;span class="na"&gt;onUserSelect&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setSelectedChat&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'col'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;selectedChat&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Chat&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;selectedChat&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IHPBackend&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Things should now look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ow1iAaze--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6hgu0dgdl1pydihybwo8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ow1iAaze--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6hgu0dgdl1pydihybwo8.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have a &lt;code&gt;Chat&lt;/code&gt; component with access to the user we've selected, we can finally create the necessary code to send messages to that user.&lt;/p&gt;

&lt;p&gt;Let’s add a &lt;code&gt;form&lt;/code&gt; element inside the &lt;code&gt;Chat&lt;/code&gt; component with a simple input and a submit button, which we can use to send messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Chat&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="nl"&gt;user&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&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;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"input-group mt-4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
                &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"form-control"&lt;/span&gt;
                &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Type a message..."&lt;/span&gt;
                &lt;span class="na"&gt;aria-describedby&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"send-button"&lt;/span&gt;
                &lt;span class="na"&gt;autoFocus&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'submit'&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-primary"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Send&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have added a bit of simple markup to make it look prettier.&lt;/p&gt;

&lt;p&gt;To make this work, we'll need to handle the submit event of the form - we can do that by creating a &lt;code&gt;handleSend&lt;/code&gt; function within the &lt;code&gt;Chat&lt;/code&gt; component that we pass to the &lt;code&gt;onSubmit&lt;/code&gt; attribute of the &lt;code&gt;form&lt;/code&gt; element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// add this before the return in Chat:&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleSend&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;FormEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLFormElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&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="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;namedItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLInputElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="c1"&gt;// this is where the magic happens:&lt;/span&gt;
      &lt;span class="nx"&gt;createRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages&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;recipientId&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;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important call here is the one to &lt;code&gt;createRecord&lt;/code&gt;. IHP Backend provides this function. The first argument tells IHP Backend to create a new record in the &lt;code&gt;messages&lt;/code&gt; table, and the second argument contains all the required fields we need to set for the record to be complete (it'll use the default values if we don't override them). Since IHP Backend uses the currently logged-in user's id as &lt;code&gt;user_id&lt;/code&gt; by default (as defined in the schema) and generates a new &lt;code&gt;id&lt;/code&gt; for the message automatically, we only need to set the &lt;code&gt;recipient_id&lt;/code&gt; to the id of the user we've selected to chat with, and the &lt;code&gt;content&lt;/code&gt; to the input's value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minute 7: Display sent messages
&lt;/h2&gt;

&lt;p&gt;There's only one problem: once we send the message, there's no way to read it!&lt;/p&gt;

&lt;p&gt;So the next step is to display all messages sent into the current chat.&lt;/p&gt;

&lt;p&gt;To do so, we'll write another query using &lt;code&gt;useQuery&lt;/code&gt; inside the &lt;code&gt;Chat&lt;/code&gt; component. Let's start simple and get all messages the user has access to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can display this list of messages by adding these lines above the &lt;code&gt;form&lt;/code&gt; tag and below the &lt;code&gt;{user.email}&lt;/code&gt; line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;messages&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;message&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;from-me&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;to-me&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'message-content'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&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;content&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All we're doing here is mapping over the messages, displaying each message's content inside two divs. We also assign CSS classes based on whether the current user sent the message or received it by comparing the message's &lt;code&gt;userId&lt;/code&gt; to the current user's id (accessed with &lt;code&gt;getCurrentUserId()&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;For the CSS classes to do anything, add the following styles to &lt;code&gt;public/app.css&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.message.from-me&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.message.to-me&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.message&lt;/span&gt; &lt;span class="nc"&gt;.message-content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.message.to-me&lt;/span&gt; &lt;span class="nc"&gt;.message-content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;lightgray&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.message.from-me&lt;/span&gt; &lt;span class="nc"&gt;.message-content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;lightblue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&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;I won't go over this in detail; it just makes things look a bit better. Feel free to take a closer look if you want, though.&lt;/p&gt;

&lt;p&gt;After changing the CSS, you might have to reload the app in the browser to see the effects.&lt;/p&gt;

&lt;p&gt;Currently, we display all messages in every chat and no particular order. Since that doesn't make sense, let's modify the query we use to fetch the messages only to fetch those messages sent to us from the user we have selected to chat with and those we sent to them. After &lt;code&gt;query('messages')&lt;/code&gt;, we can add a &lt;code&gt;.where(..)&lt;/code&gt; condition as we did earlier, filtering the messages we sent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&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;That still doesn’t limit the messages to those sent to the selected person though, so we’ll add another &lt;code&gt;.where&lt;/code&gt; condition based on the recipient:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recipientId&lt;/span&gt;&lt;span class="dl"&gt;'&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Multiple where conditions like this will be combined using &lt;code&gt;AND&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now we have all of the messages we sent, but we’re now missing the messages we received. To fix that, we can add a set of conditions using &lt;code&gt;.or&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recipientId&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recipientId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&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;.or&lt;/code&gt; takes a list of conditions just as you can add them to &lt;code&gt;query('messages')&lt;/code&gt;. You might have to add an import for &lt;code&gt;where&lt;/code&gt; to the import list of &lt;code&gt;ihp-backend&lt;/code&gt; at the top of the file.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.or&lt;/code&gt; uses all previous conditions for the left side of the resulting &lt;code&gt;OR&lt;/code&gt; condition. If you want a bit more clarity, you can wrap the initial conditions with a single call to &lt;code&gt;.where&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recipientId&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recipientId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&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;I, personally, don't think that's necessary, though.&lt;/p&gt;

&lt;p&gt;If we check the result in the browser, messages will now only show in the correct chat! However, even if the order of messages might look correct, we have done nothing to ensure this will stay consistent. To always ensure that messages are displayed in the correct order, we can add a simple &lt;code&gt;.orderBy('createdAt')&lt;/code&gt; to the query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recipientId&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recipientId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserId&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;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;createdAt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, the correct messages show, in the proper order!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SCVHZ3DV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a4wn9him6z6wpwev5okd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SCVHZ3DV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a4wn9him6z6wpwev5okd.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Minute 8: Realtime
&lt;/h2&gt;

&lt;p&gt;We haven't done anything to make our app realtime yet... But the &lt;code&gt;useQuery&lt;/code&gt; hook is realtime by default, meaning we don't have to do anything! IHP Backend will push new data to each client who requires it over a WebSocket connection. You can quickly try it out by opening a new private window in which you sign in as another user, sending messages between clients - the messages will show up in each browser without requiring a reload of the page!&lt;/p&gt;

&lt;p&gt;I guess we can use this time for doing something else then.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minute 8b: Commit and push (deploy)
&lt;/h2&gt;

&lt;p&gt;Now that we've finished the frontend let's deploy it to production. Commit all changes and push them to your repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add app.tsx public/app.css package.json package-lock.json
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"implement realtime chat"&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vercel will notice a change on the main branch of our repository and deploy the changes automatically.&lt;/p&gt;

&lt;p&gt;Enjoy your new app and possibly spare time!&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving on
&lt;/h2&gt;

&lt;p&gt;From here, you can continue playing around with &lt;a href="https://ihpbackend.digitallyinduced.com/"&gt;IHP Backend&lt;/a&gt;. Maybe you want to allow users to set a different display name instead of displaying their email? Maybe you want users to request a chat with others, so they can't just send messages to anyone willy-nilly? Perhaps you want to implement group chat functionality?&lt;/p&gt;

&lt;p&gt;If you don't feel comfortable trying things out alone, you might also want to follow the normal Onboarding process. It'll lead you through creating another app, similar to this article, but possibly give you some new information not covered here or covered differently.&lt;/p&gt;

&lt;p&gt;What will you build with &lt;a href="https://ihpbackend.digitallyinduced.com/"&gt;IHP Backend&lt;/a&gt;? Tell us in the comments!    &lt;/p&gt;

</description>
      <category>react</category>
      <category>tutorial</category>
      <category>typescript</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Haskell as an alternative to TypeScript</title>
      <dc:creator>digitallyinduced</dc:creator>
      <pubDate>Wed, 11 Aug 2021 11:20:33 +0000</pubDate>
      <link>https://dev.to/digitallyinduced/haskell-as-an-alternative-to-typescript-1091</link>
      <guid>https://dev.to/digitallyinduced/haskell-as-an-alternative-to-typescript-1091</guid>
      <description>&lt;p&gt;If you've been using TypeScript for your web development, you have understood the value that static typing brings to your productivity. However, TypeScript still has some issues in regards to type safety. This article is supposed to show these issues and help you see how another statically typed language - Haskell - is solving them. In the end, I hope that you will have learned something, and might be interested in continuing your journey into type-safe programming by trying out Haskell for your next project. If you choose to do so, I recommend using IHP, a productivity-focused web framework with which you can build full-stack web-apps (but APIs work just fine as well).&lt;/p&gt;

&lt;h2&gt;
  
  
  The anti-pattern that is &lt;code&gt;any&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;any&lt;/code&gt; type included in TypeScript can cause many issues, as it straight up disables typechecking for whatever it's used for. Once you use it, you might as well write plain JavaScript. But it might actually be worse than that: people might be highly confident in their refactoring or new code if they don't get any type errors. What they might not notice is that an &lt;code&gt;any&lt;/code&gt; somewhere else in the code where they didn't change anything is not throwing a type error, although it would if the correct type were used.&lt;/p&gt;

&lt;p&gt;So why would anyone use &lt;code&gt;any&lt;/code&gt;? I think there's a few different reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the correct type is not known to the developer at the time of writing,&lt;/li&gt;
&lt;li&gt;the correct type would be too complex, or&lt;/li&gt;
&lt;li&gt;it's implicitly used by TypeScript because it can't figure out the correct type by itself (this one can be disabled using the &lt;code&gt;noImplicitAny&lt;/code&gt; setting)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, if the correct type is not known by the developer, that could either mean it'd be useful for them to do some research into what it actually is, or, more likely, the type cannot be known at that point in time. In any case using TypeScript's &lt;code&gt;unknown&lt;/code&gt; type is much better, as that is a type-safe way of accepting any value, that also disallows any interaction with the value that might be problematic. This is the type to use in case of migrating an existing JS codebase to TypeScript, as it keeps the guards of TypeScript intact.&lt;/p&gt;

&lt;p&gt;If the correct type is too complex for the developer to figure out or to bother typing, and TypeScript isn't able to do so either, there's little you can do except use &lt;code&gt;unknown&lt;/code&gt; again, which definitely isn't ideal. The developer probably already understands things about the type, but using &lt;code&gt;unknown&lt;/code&gt; means that TypeScript will not, which will just lead to unnecessary checks.&lt;/p&gt;

&lt;p&gt;In case Haskell was used instead, the likelihood of Haskell figuring out the type by itself (this is called type inference btw) is much higher. This is because in Haskell &lt;code&gt;any&lt;/code&gt; isn't even an option, so it can't confuse the compiler. But also, ghc - the most widespread Haskell compiler - has years and years of effort behind it. And lastly, as long as you stick to basic Haskell, it is specifically designed to enable type inference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations of being a super-set of JavaScript
&lt;/h2&gt;

&lt;p&gt;TypeScript has the core design-goal of being a super-set of JavaScript. What that means is that any JavaScript code is valid TypeScript code as well (if the transpiler is configured to accept it), and you only need to add code to add more definitive types to the code. This is great if you need to convert an existing codebase from JavaScript to TypeScript, as it allows you to work in very small increments.&lt;/p&gt;

&lt;p&gt;The problem is that new projects built using TypeScript still suffer from many of the problems of JavaScript:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;difficult-to-understand behavior that doesn't behave like in other languages (for example the &lt;code&gt;this&lt;/code&gt; keyword)&lt;/li&gt;
&lt;li&gt;many things on &lt;a href="https://wtfjs.com/"&gt;https://wtfjs.com/&lt;/a&gt;
...and probably more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So while I applaud converting an existing JavaScript codebase to TypeScript, I would never start a new project in either if there's good alternatives, which there always are for backend development. Also, using the right tools the need for a lot of frontend code can be eliminated, while maintaining a highly-interactive webapp. &lt;a href="https://ihp.digitallyinduced.com/"&gt;Check out IHP for my favorite solution right now&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Being a super-set of JavaScript, many type definitions are a lot more complex to write than they would otherwise need to be as well. Check out the following examples, which are type definitions for equivalent Haskell and TypeScript code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// A function that takes a tuple of two values and returns the first:

// TypeScript
type first = &amp;lt;A, B&amp;gt; ([a, b]: [A, B]) =&amp;gt; A

// Haskell
first :: (a, b) -&amp;gt; a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// a map function for lists/arrays

// TypeScript
type map = &amp;lt;A, B&amp;gt; ((a: A) =&amp;gt; B, list: A[]) =&amp;gt; B[]

// Haskell
map :: (a -&amp;gt; b) -&amp;gt; [a] -&amp;gt; [b]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  TypeScript can't type everything
&lt;/h2&gt;

&lt;p&gt;If you're using TypeScript you see the advantages of having static typing available, so we don't need to go over them. As such, it should bother you whenever you cannot actually use static types to encode useful information. Here are a few cases in which TypeScript cannot actually fulfill its promise of type-safety, and the way that Haskell solves it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Side-Effects
&lt;/h3&gt;

&lt;p&gt;If you've been following recent trends in software development, especially in the JavaScript world, you know that functional programming principles are becoming more and more popular. React is built on them! One of the things people are advocating for are "pure functions": functions that, given the same arguments, always return the same result and that don't have side-effects. What makes these functions so great is that they are very easy to reason about, and there are various techniques for performance optimization that are much easier using them, if not impossible without them.&lt;/p&gt;

&lt;p&gt;When coding in TypeScript, figuring out whether or not a function is pure requires you to read its source code and figure out how it works, which might lead to other functions that you have to do the same thing for. This gets tedious and could easily be avoided if the information of function purity was included in the function's type.&lt;/p&gt;

&lt;p&gt;In Haskell, this is done by making every function pure by-default. Whenever you need to have a side-effect in a function, you need to declare this on the type-level. Not doing so is a compiler error.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failures
&lt;/h3&gt;

&lt;p&gt;As long as everything works as expected, TypeScript works just fine. However, when something goes wrong, you will likely have to interact with an error that gives you more information. Before you can even do so though, you need to be aware that an error can occur - and this is one of the shortcomings of TypeScript. It is impossible to declare on the type-level that a function might throw an error. This means you still need rigorous testing to figure this out, and even then you might run into unhandled errors in production due to not having tested thoroughly enough.&lt;/p&gt;

&lt;p&gt;In Haskell it is most common to force handling errors by making the return type include the information of whether or not the function succeeded or not. Depending on the actual function, you might get no, some, or a lot of information in case of a failure, but you can be sure that you are handling these cases. The two most common ways of representing the option for failure are the &lt;code&gt;Maybe&lt;/code&gt; and &lt;code&gt;Either&lt;/code&gt; types.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Maybe&lt;/code&gt; type can represent success and failure by containing the success value in the success case, or &lt;code&gt;Nothing&lt;/code&gt; in the case of a failure. This is also basically Haskell's way of handling &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt;, which prevents from using these values in a way that they don't support, which might cause issues.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Either&lt;/code&gt; type works in a very similar way: in the case of a success it simply contains the value that the function would return anyways. But in case of a failure it contains some other value. The type of this is clearly defined, which means that there's no ambiguity in how to handle it.&lt;/p&gt;

&lt;p&gt;What's best about these techniques is that it does not only &lt;em&gt;allow&lt;/em&gt; you to handle the error case in a type-safe fashion, it &lt;em&gt;forces&lt;/em&gt; you to handle error cases where they might occur. If the function doesn't force you to handle the error case, no error can occur.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One place where typing the failure state should clearly be possible are promises, however TypeScript has no default way of doing this, which means that in case you run into a failure in your asynchronous code, TypeScript is not going to help you in figuring out the value you got in place of the success value.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;TypeScript is a great way to improve an existing JavaScript codebase, and to bring a bit more productivity and safety to your frontend code. However, the language has issues, some of which are so ingrained in the language itself that they are not going to be solved with time. So whenever you can, try opting for a language that has less design issues at its core, such as Haskell, and avoid writing JavaScript/TypeScript wherever possible.&lt;/p&gt;

&lt;p&gt;To allow you to do this, we at digitally induced have written IHP - an opinionated full-stack web framework based on Haskell, with a focus on developer experience and productivity, without sacrificing user experience. To try it out for your next full-stack project or backend, &lt;a href="https://ihp.digitallyinduced.com/Guide/installation.html"&gt;get started using the Guide&lt;/a&gt; and join our &lt;a href="https://ihp.digitallyinduced.com/community/"&gt;community forum&lt;/a&gt; or &lt;a href="https://ihp.digitallyinduced.com/Slack"&gt;active slack workspace&lt;/a&gt; to get any support you might need. We try to help wherever we can.&lt;/p&gt;

&lt;p&gt;Do you have any thoughts on TypeScript that you want to share? Did I miss anything? I'd love to hear your opinions and thoughts!&lt;/p&gt;

</description>
      <category>haskell</category>
      <category>typescript</category>
      <category>functional</category>
      <category>ihp</category>
    </item>
    <item>
      <title>How IHP uses Haskell's Type System to enforce good patterns</title>
      <dc:creator>digitallyinduced</dc:creator>
      <pubDate>Wed, 04 Aug 2021 12:56:30 +0000</pubDate>
      <link>https://dev.to/digitallyinduced/how-ihp-uses-haskell-s-type-system-to-enforce-good-patterns-2kjn</link>
      <guid>https://dev.to/digitallyinduced/how-ihp-uses-haskell-s-type-system-to-enforce-good-patterns-2kjn</guid>
      <description>&lt;p&gt;Good patterns and clean code are what differentiates a production application from a legacy application. In a lot of cases, many production applications become legacy applications with time, because patterns aren't enforced and therefore ignored, wrongly interpreted, or otherwise abandoned.&lt;/p&gt;

&lt;p&gt;IHP uses Haskell as its language of choice, and one big reason for this is the typesafety that Haskell provides. After reading this article you'll hopefully understand how IHP is making use of Haskell's strong typesystem to enforce proper use of patterns shared between all IHP applications, which prevents your production webapp from becoming a legacy webapp.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sidenote:&lt;/em&gt; at digitally induced we have multiple older IHP apps, none of which we consider "legacy", even if they've been running for quite some time. If we need to make changes to them, it is very easy to get back into them and understand what is going on, as all IHP apps follow similar patterns. We know what to expect, and where to find the code we're looking for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Model-View-Controller
&lt;/h2&gt;

&lt;p&gt;Let's get the biggest point out of the way first. IHP uses the popular Model-View-Controller pattern, which is characterized by the three parts giving the pattern its name:&lt;/p&gt;

&lt;p&gt;The Model is the data of the application, which has a static structure when running, but variable content, as the content is user-generated. In IHP all data types are auto-generated from the database schema, ensuring that the code you write is always compatible with the database.&lt;/p&gt;

&lt;p&gt;The View is a simple mapping that turns data into Html. Using Haskell's type system, this is enforced by defining every view as a pure function (a function without side-effects that always produces the same output if it receives the same input). You might have heard that that's the core idea of React as well, and it's a reason React is so popular: the render function should simply take the current component's state and render Html based on that. However, React has the problem that everything else is also possible in the view, and in a way even requires it to be there, including updating state. In IHP, the view really fulfills this promise, and it's enforced by the type system.&lt;/p&gt;

&lt;p&gt;The Controller is the part of the application that contains the actual business logic, and should be the only place in the application that is able to interact with the outside world, including the database (also known as IO: Input and Output). In Haskell, doing IO isn't possible everywhere, only in functions that have been declared to be able to do it. IHP makes use of that by defining the actions (endpoints of controllers) as the only functions that can run IO things. Even if someone is tempted to fetch data from the database in the View, they can't, because the controller is simply going to prevent it from working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fetch all required information
&lt;/h2&gt;

&lt;p&gt;As described above, the View is a function mapping data to Html. However, this data is different from view to view of course. Since the data has to be fetched by the controller, the view defines a data structure which the controller needs to completely fetch the information for to render the view, which makes sure that no information is ever missing in the view, not even accidentally. And when some information is not necessary anymore, you can just remove it from the view data structure, which will cause compiler errors everywhere where you're still fetching it (where there could now be unnecessary code).&lt;/p&gt;

&lt;p&gt;To read more about how passing the data from controller actions to the view works, &lt;a href="https://ihp.digitallyinduced.com/Guide/controller.html#passing-data-from-the-action-to-the-view"&gt;read the documentation here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  No missing information for links
&lt;/h2&gt;

&lt;p&gt;When the user wants to interact with the website, they mainly do so via links. Usually links are simply strings, which means that if parameters are required but missing, this can only be detected via trial-and-error. Using IHP's &lt;code&gt;pathTo&lt;/code&gt; and &lt;code&gt;urlTo&lt;/code&gt; functions, you can build the links between pages of your application in a typesafe way, which means you're not going to forget to send new necessary information when an endpoint requires it, and will not forget to remove it when it's unnecessary anymore. Renaming is a non-issue as well, and typos are (again) compiler errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  IDs cannot be used to query the wrong database table
&lt;/h2&gt;

&lt;p&gt;In IHP, IDs are (by default) UUIDs. But even if you use Integer-based IDs instead, you could run into a situation where you'd accidentally use a user-ID for querying a different table, and wouldn't be any wiser, since both are UUIDs. In IHP on the other hand, all IDs are wrapped once more, making the type of the ID &lt;code&gt;Id User&lt;/code&gt; for example. You then can't use this ID to for example fetch a product.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If for some obscure reason you still need to do this, or you get the ID as another type and need to convert it to this special type, that's of course possible.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This special type also allows the &lt;code&gt;fetch&lt;/code&gt; function that queries the database for a single row with the given ID to be super simple to call: since the ID already contains the information of which table it's for, you don't have to do any more work than passing the ID to the function, and it will take care of the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ensuring proper HTML
&lt;/h2&gt;

&lt;p&gt;Views in IHP are written in Haskell, using something called HSX, which is the same basic idea as JSX in React. That means you write the HTML you would normally write, and can easily include dynamic Haskell code where needed.&lt;/p&gt;

&lt;p&gt;Since HSX is just syntactic sugar for other Haskell functions though, it is typesafe! That means you can't use attributes for elements where the spec doesn't allow for it, and many markup errors (like forgetting to close a tag) are caught at compile-time.&lt;/p&gt;

&lt;p&gt;If you need to use custom attributes, that's what &lt;code&gt;data-&lt;/code&gt; attributes are for, and they are fully supported. Just like custom web components.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Bonus:&lt;/em&gt; beginners in React often want to quickly output the content of some data they have, and try to just inline the variable in their JSX. They are then often surprised to see &lt;code&gt;[Object object]&lt;/code&gt;, since converting an object to a string in JS will lead to this result. In HSX, this will call &lt;code&gt;show&lt;/code&gt; on the provided data if possible, leading to the expected result.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;Maybe&lt;/code&gt; and the dreaded &lt;code&gt;NullPointerException&lt;/code&gt; (or &lt;code&gt;TypeError: variable is undefined&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;While technically not IHP-exclusive, &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt; do not exist in Haskell. Instead, if you need to represent something not being there, you can use &lt;code&gt;Nothing&lt;/code&gt;, which is a value for the &lt;code&gt;Maybe&lt;/code&gt; type. Using this, you can represent that something might not be there, which will force you to handle that case. But once you've handled that case, you don't have to handle it again - something that I've seen a lot in medium to larger codebases, where it's not always entirely clear where a value might come from.&lt;/p&gt;

&lt;p&gt;What this means in essence is that you will never get a &lt;code&gt;NullPointerException&lt;/code&gt; or a &lt;code&gt;TypeError: yourVariable is undefined&lt;/code&gt; when using IHP!&lt;/p&gt;

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

&lt;p&gt;IHP makes as much use of Haskell's types as possible, leading to less bugs and an easier-to-grasp codebase that won't become legacy. If this article peaked your interest in IHP, &lt;a href="https://ihp.digitallyinduced.com/Guide/installation.html"&gt;you can get started using the Guide&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>functional</category>
      <category>typescript</category>
      <category>haskell</category>
      <category>ihp</category>
    </item>
    <item>
      <title>Displaying Real-Time Data in Your Web Application Without Hassle: IHP Auto Refresh ✨</title>
      <dc:creator>digitallyinduced</dc:creator>
      <pubDate>Thu, 15 Jul 2021 06:42:59 +0000</pubDate>
      <link>https://dev.to/digitallyinduced/displaying-real-time-data-in-your-web-application-without-hassle-ihp-auto-refresh-4e7a</link>
      <guid>https://dev.to/digitallyinduced/displaying-real-time-data-in-your-web-application-without-hassle-ihp-auto-refresh-4e7a</guid>
      <description>&lt;p&gt;&lt;strong&gt;In the IHP framework we have one feature that typically looks like dark magic to people coming from other frameworks: Auto Refresh ✨. Ever wanted to display real-time data in your web application, but found WebSockets too much of a hassle? Add a single keyword in your IHP controller, and you are all set.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Auto Refresh offers a way to re-render the HTML views of your application when the underlying data changes, as long as the page is still open inside the user’s browser window. Auto Refresh replaces manually polling for status updates or complicated Websocket code.&lt;/p&gt;

&lt;p&gt;Without any manual work we can have e.g. live updating dashboards, chat message views that automatically display new messages when they arrive, or news feeds that automatically display new activities.&lt;/p&gt;

&lt;p&gt;If you have a react.js background, you can think of an Auto Refresh view as a react component where the state is the actual backend PostgreSQL database. Whenever the database state changes, the view is re-rendered. Like a normal react component. The only major difference in our approach is that all the rendering is happening on the server side. So you have access to all your application code. &lt;/p&gt;

&lt;h2&gt;
  
  
  Real-world example: IHP Cloud
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ihpcloud.com/" rel="noopener noreferrer"&gt;IHP Cloud&lt;/a&gt; is a deployment platform for IHP applications. After clicking on the “Deploy now” button, the user is redirected to a deployment status page. Similar to GitHub actions you can see the current status of the deployment and all the logs. Once the deployment is finished, the status turns green.&lt;/p&gt;

&lt;p&gt;Before IHP Auto Refresh was invented, this page was implemented using a manual ajax polling every 1 second. This was very error prone and polling too fast sometimes caused the CPU coolers to turn on.&lt;/p&gt;

&lt;p&gt;Here’s how it looks now with Auto Refresh:&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%2Fihp.digitallyinduced.com%2Fblog%2Fauto-refresh-how-does-it-work%2Fihp-cloud.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%2Fihp.digitallyinduced.com%2Fblog%2Fauto-refresh-how-does-it-work%2Fihp-cloud.gif" alt="Preview of auto refresh on the progress page of IHP cloud"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The progress bar, logs and the deployment status is automatically updated without any app-specific javascript. Whenever the deployment progresses, the IHP server pushes an updated view to the browser.&lt;/p&gt;

&lt;p&gt;Auto Refresh can be activated for any IHP controller action by adding a single &lt;code&gt;autoRefresh&lt;/code&gt; keyword in front of the action.&lt;/p&gt;

&lt;p&gt;Here’s the code of the above deployment status view: &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%2Fihp.digitallyinduced.com%2Fblog%2Fauto-refresh-how-does-it-work%2Fihp-cloud-code.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%2Fihp.digitallyinduced.com%2Fblog%2Fauto-refresh-how-does-it-work%2Fihp-cloud-code.png" alt="Code editor with an arrow pointing to the use of the autoRefresh function to enable it for an action"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see that there’s an &lt;code&gt;autoRefresh&lt;/code&gt; keyword in front of the “do” in the first line of the action. This enables auto refresh. This single keyword is all it needs for IHP auto refresh to get going.&lt;/p&gt;

&lt;p&gt;If we would remove the &lt;code&gt;autoRefresh&lt;/code&gt; keyword, we would need to manually refresh the page all the time to see the latest deployment status.&lt;/p&gt;

&lt;p&gt;Let’s take a look at what IHP’s &lt;code&gt;autoRefresh&lt;/code&gt; keyword does. &lt;/p&gt;

&lt;h2&gt;
  
  
  IHP Auto Refresh: Technical Overview
&lt;/h2&gt;

&lt;p&gt;When we first load a page like the deployment status page above, the &lt;code&gt;autoRefresh&lt;/code&gt; keyword activates IHP Auto Refresh for this action. Once activated it starts tracking all SQL queries the controller action is running against the database. It inspects the &lt;code&gt;SELECT * FROM …&lt;/code&gt; queries and keeps track of all the tables that have been used.&lt;/p&gt;

&lt;p&gt;After the page is initially rendered on the browser, a small JavaScript function provided by IHP will connect back to the Server using a WebSocket connection.&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%2Fihp.digitallyinduced.com%2Fblog%2Fauto-refresh-how-does-it-work%2Fauto-refresh-1.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%2Fihp.digitallyinduced.com%2Fblog%2Fauto-refresh-how-does-it-work%2Fauto-refresh-1.png" alt="Diagram showing how browser and server interact in the case of an autoRefresh action. First, the browser makes a GET request for the action, in this case /ShowDeployment. The server executes two SELECT statements, which are automatically recognized using by autoRefresh. The server sends the initial HTML to the browser, which initiates a new websocket connection to the server. The server then watches the tables used in the SELECT statements for changes."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the WebSocket connection is established, IHP starts watching the tables your action has been using for changes.&lt;/p&gt;

&lt;p&gt;In our example view from above, it starts watching on the deployments and deployment_logs tables, as these have been used for rendering the initial deployment status page.&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%2Fihp.digitallyinduced.com%2Fblog%2Fauto-refresh-how-does-it-work%2Fauto-refresh-2.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%2Fihp.digitallyinduced.com%2Fblog%2Fauto-refresh-how-does-it-work%2Fauto-refresh-2.png" alt="A diagram showing how the server watches for data changes to send new data to the browser. First, the server tells the postgres database that it wants to "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now a job worker starts working on the deployment. It will insert new log lines into the &lt;code&gt;deployment_logs&lt;/code&gt; table and at the end update the &lt;code&gt;deployments&lt;/code&gt; table to mark the deployment status as completed.&lt;/p&gt;

&lt;p&gt;Whenever IHP auto refresh is notified about a table change via &lt;code&gt;pg_notify&lt;/code&gt;, it will re-run the action on the server-side. When the generated HTML looks different than the HTML generated on the initial page load, it will send the new HTML to the browser using the WebSocket connection.&lt;/p&gt;

&lt;p&gt;Whenever the JavaScript on the browser-side receives new HTML, it will update the current page using a DOM-diff approach (using &lt;a href="https://github.com/patrick-steele-idem/morphdom" rel="noopener noreferrer"&gt;morphdom&lt;/a&gt;). So only DOM nodes that have actually changed between the initial page load and the updated HTML will be updated.&lt;/p&gt;

&lt;p&gt;All of this happens behind the scenes.&lt;/p&gt;

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

&lt;p&gt;Auto Refresh is a very handy tool to make “live” views that always display the latest data. It can replace most needs for manual ajax polling or complicated WebSocket update code. Because it’s so quick to set up, you typically end up using auto refresh in places where you otherwise wouldn’t add any auto-update logic, when you’d have to manually write it.&lt;/p&gt;

&lt;p&gt;If you're new to IHP, &lt;a href="https://youtu.be/UbDtS_mUMpI" rel="noopener noreferrer"&gt;check out the demo video 📺&lt;/a&gt;.&lt;br&gt;
You can &lt;a href="https://ihp.digitallyinduced.com/Guide/auto-refresh.html" rel="noopener noreferrer"&gt;learn more about IHP Auto Refresh in the documentation 📚&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>haskell</category>
      <category>ihp</category>
      <category>functional</category>
    </item>
    <item>
      <title>Two Quick IHP tips to dynamically assign classes</title>
      <dc:creator>digitallyinduced</dc:creator>
      <pubDate>Fri, 09 Jul 2021 07:28:42 +0000</pubDate>
      <link>https://dev.to/digitallyinduced/two-quick-ihp-tips-to-dynamically-assign-classes-3cc1</link>
      <guid>https://dev.to/digitallyinduced/two-quick-ihp-tips-to-dynamically-assign-classes-3cc1</guid>
      <description>&lt;p&gt;Here are two quick tips for &lt;a href="https://ihp.digitallyinduced.com/"&gt;IHP&lt;/a&gt;:&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the classes helper
&lt;/h2&gt;

&lt;p&gt;...to dynamically assign classes to elements in your views. It's an easy way to simplify your code by a lot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- BEFORE&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;usersLinkClasses&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;UsersAction&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="kt"&gt;Users&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;invitesLinkClasses&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;InvitesAction&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="kt"&gt;Invites&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="kr"&gt;where&lt;/span&gt;
    &lt;span class="n"&gt;usersLinkClasses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"nav-link"&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="kr"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isActivePath&lt;/span&gt; &lt;span class="kt"&gt;UsersAction&lt;/span&gt;
        &lt;span class="kr"&gt;then&lt;/span&gt; &lt;span class="s"&gt;" active"&lt;/span&gt;
        &lt;span class="kr"&gt;else&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
    &lt;span class="n"&gt;invitesLinkClasses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"nav-link"&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="kr"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isActivePath&lt;/span&gt; &lt;span class="kt"&gt;InvitesAction&lt;/span&gt;
        &lt;span class="kr"&gt;then&lt;/span&gt; &lt;span class="s"&gt;" active"&lt;/span&gt;
        &lt;span class="kr"&gt;else&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;

&lt;span class="c1"&gt;-- AFTER&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"nav-link"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isActivePath&lt;/span&gt; &lt;span class="kt"&gt;UsersAction&lt;/span&gt;&lt;span class="p"&gt;)]}&lt;/span&gt; &lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;UsersAction&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="kt"&gt;Users&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"nav-link"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isActivePath&lt;/span&gt; &lt;span class="kt"&gt;InvitesAction&lt;/span&gt;&lt;span class="p"&gt;)]}&lt;/span&gt; &lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;InvitesAction&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="kt"&gt;Invites&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use &lt;code&gt;isActivePathOrSub&lt;/code&gt; instead of &lt;code&gt;isActivePath&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If you read the previous example, you might have noticed that I used &lt;code&gt;isActivePath&lt;/code&gt; to dynamically assign the &lt;code&gt;active&lt;/code&gt; class to the links. Well, using &lt;code&gt;isActivePathOrSub&lt;/code&gt; this will also work if the path is a sub-path. In the following example, both &lt;code&gt;/Settings&lt;/code&gt; and &lt;code&gt;/Settings/Profile&lt;/code&gt; would match and cause the link to get the &lt;code&gt;active&lt;/code&gt; class. &lt;a href="https://ihp.digitallyinduced.com/api-docs/IHP-ViewSupport.html#v:isActivePathOrSub"&gt;Learn more in the documentation&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"nav-link"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isActivePathOrSub&lt;/span&gt; &lt;span class="s"&gt;"/Settings"&lt;/span&gt;&lt;span class="p"&gt;)]}&lt;/span&gt; &lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;SettingsAction&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="kt"&gt;Settings&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this peaked your interest, &lt;a href="https://ihp.digitallyinduced.com/Guide/index.html"&gt;get started with IHP now&lt;/a&gt; and tell us what you think!&lt;/p&gt;

</description>
      <category>functional</category>
      <category>todayilearned</category>
      <category>ihp</category>
      <category>haskell</category>
    </item>
    <item>
      <title>IHP v0.11.0 brings Server-Side Components and Joins!</title>
      <dc:creator>digitallyinduced</dc:creator>
      <pubDate>Thu, 17 Jun 2021 09:00:21 +0000</pubDate>
      <link>https://dev.to/digitallyinduced/ihp-v0-11-0-brings-server-side-components-and-joins-4pp2</link>
      <guid>https://dev.to/digitallyinduced/ihp-v0-11-0-brings-server-side-components-and-joins-4pp2</guid>
      <description>&lt;p&gt;We've just released IHP v0.11.0, which includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server-Side-Components, similar to React or elm&lt;/li&gt;
&lt;li&gt;Joins, for more complex queries&lt;/li&gt;
&lt;li&gt;IHP.FileStorage, which makes it easy to store files on AWS S3 or any other compatible service&lt;/li&gt;
&lt;li&gt;...and more!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Server-Side-Components aka. you don't need React
&lt;/h2&gt;

&lt;p&gt;IHP's new Server-Side-Components allow you to build React-like components on the server (as the name implies). This allows you to build highly interactive pages without having to worry about server vs. client state, and no change of environment - as everything is still in Haskell.&lt;/p&gt;

&lt;p&gt;Let's look at an example component, a basic counter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- everything starts with the normal module definition and imports&lt;/span&gt;
&lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Web.Component.Counter&lt;/span&gt; &lt;span class="kr"&gt;where&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;IHP.ViewPrelude&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;IHP.ServerSideComponent.Types&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;IHP.ServerSideComponent.ControllerFunctions&lt;/span&gt;

&lt;span class="c1"&gt;-- Followed by a type definition for the state.&lt;/span&gt;
&lt;span class="c1"&gt;-- This means there will never be any surprise what the state contains.&lt;/span&gt;
&lt;span class="c1"&gt;-- In this case it's simply an Int, so a number.&lt;/span&gt;
&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;Counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Counter&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="o"&gt;!&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;-- Then comes a list of possible actions that can be used to update the state.&lt;/span&gt;
&lt;span class="c1"&gt;-- If you've used redux or the elm architecture, this should be familiar.&lt;/span&gt;
&lt;span class="c1"&gt;-- Defining what actions are available means it's there won't be any surprises here either, and future developers can immediately see what behavior this component supports.&lt;/span&gt;
&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;CounterController&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;IncrementCounterAction&lt;/span&gt;
    &lt;span class="kr"&gt;deriving&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Eq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;-- just one line of boilerplate to generate some code that we really don't want to write ourselves...&lt;/span&gt;
&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deriveSSC&lt;/span&gt; &lt;span class="n"&gt;''CounterController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;-- The heart of any component are&lt;/span&gt;
&lt;span class="c1"&gt;-- 1. the render function, taking care of turning the state into html&lt;/span&gt;
&lt;span class="c1"&gt;-- 2. action handlers, which update the state based on selected actions&lt;/span&gt;
&lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="kt"&gt;Component&lt;/span&gt; &lt;span class="kt"&gt;Counter&lt;/span&gt; &lt;span class="kt"&gt;CounterController&lt;/span&gt; &lt;span class="kr"&gt;where&lt;/span&gt;
    &lt;span class="c1"&gt;-- but first, we need to define the default state&lt;/span&gt;
    &lt;span class="n"&gt;initialState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Counter&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="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;-- the render function takes the state and return HTML&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="kt"&gt;Counter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;hsx&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        Current: {value} &amp;lt;br /&amp;gt;
        &amp;lt;button onclick="callServerAction('IncrementCounterAction')"&amp;gt;Plus One&amp;lt;/button&amp;gt;
    &lt;span class="o"&gt;|]&lt;/span&gt;

    &lt;span class="c1"&gt;-- this is the handler for the IncrementCounterAction&lt;/span&gt;
    &lt;span class="c1"&gt;-- the function takes a state and the action, modifies the state (in this case incrementing the counter's value) and returns the new value&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="kt"&gt;IncrementCounterAction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;
            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;incrementField&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pure&lt;/span&gt;

&lt;span class="c1"&gt;-- this allows us to use 'incrementField' to increment the counter&lt;/span&gt;
&lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="kt"&gt;SetField&lt;/span&gt; &lt;span class="s"&gt;"value"&lt;/span&gt; &lt;span class="kt"&gt;Counter&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt; &lt;span class="kr"&gt;where&lt;/span&gt; &lt;span class="n"&gt;setField&lt;/span&gt; &lt;span class="n"&gt;value'&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;counter&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;value'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the result is a beautiful counter, all controlled server-side:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F2072185%2F121806628-ee284a80-cc50-11eb-9602-061046783c7b.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%2Fuser-images.githubusercontent.com%2F2072185%2F121806628-ee284a80-cc50-11eb-9602-061046783c7b.gif" alt="A short video showing part of a webpage with the headline "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's a little gif showcasing a more complex use-case of a filterable and sortable table:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F2072185%2F121806754-7a3a7200-cc51-11eb-9d31-ec8254b2824a.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%2Fuser-images.githubusercontent.com%2F2072185%2F121806754-7a3a7200-cc51-11eb-9d31-ec8254b2824a.gif" alt="A short video showing a webpage with a table with titles and " title=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ihp.digitallyinduced.com/Guide/server-side-components.html" rel="noopener noreferrer"&gt;To learn more check out the documentation.&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Joins
&lt;/h2&gt;

&lt;p&gt;If you've tried to do complex queries involving joins of multiple tables in IHP, this was previously a little bit of a hassle. With first-class support for joins, there's now a completely type-safe way to do anything you can imagine.&lt;/p&gt;

&lt;p&gt;Here's an example of how you can select all posts that were written by a user with the name "Tom":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;tomPosts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="kt"&gt;Post&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;innerJoin&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="n"&gt;authorId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="n"&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="n"&gt;filterWhereJoinedTable&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Tom"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's a more complex example that selects all posts that were tagged by a tag named "haskell" in the case of a many-to-many relationship:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="kt"&gt;Posts&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;innerJoin&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="kt"&gt;Tagging&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="n"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;innerJoinThirdTable&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="kt"&gt;Tag&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="kt"&gt;Tagging&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="n"&gt;tagId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;filterWhereJoinedTable&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="kt"&gt;Tag&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="n"&gt;tagText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"haskell"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  IHP.FileStorage to store files in AWS S3-compatible services
&lt;/h2&gt;

&lt;p&gt;Instead of having to manually use the APIs of AWS S3, you can now use our special support for these services to upload anything you'd like.&lt;/p&gt;

&lt;p&gt;Check out this example for an action used to upload a logo of a company to S3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="kt"&gt;UpdateCompanyAction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;companyId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;company&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="n"&gt;companyId&lt;/span&gt;
    &lt;span class="n"&gt;company&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fill&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;'&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;uploadToStorage&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="n"&gt;logoUrl&lt;/span&gt;
        &lt;span class="o"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;ifValid&lt;/span&gt; &lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="kr"&gt;case&lt;/span&gt;
            &lt;span class="kt"&gt;Left&lt;/span&gt; &lt;span class="n"&gt;company&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="kt"&gt;EditView&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="kt"&gt;Right&lt;/span&gt; &lt;span class="n"&gt;company&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
                &lt;span class="n"&gt;company&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;company&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;updateRecord&lt;/span&gt;
                &lt;span class="n"&gt;redirectTo&lt;/span&gt; &lt;span class="kt"&gt;EditCompanyAction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see it's not much different from any other action used to update the company data. All the magic happens due to one line: &lt;code&gt;|&amp;gt; uploadToStorage #logoUrl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can do much more though, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use ImageMagick to do pre-processing of the uploaded image file (resizing, conversion,...)&lt;/li&gt;
&lt;li&gt;generate secure, signed download URLs for private files&lt;/li&gt;
&lt;li&gt;in development, files are stored on-disk in the &lt;code&gt;static/&lt;/code&gt; folder for ease of debugging and offline-only development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Find out how to use it and configure it for your project &lt;a href="http://ihp.digitallyinduced.com/Guide/file-storage.html" rel="noopener noreferrer"&gt;in the documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Case Insensitivity
&lt;/h2&gt;

&lt;p&gt;We've added &lt;code&gt;filterWhereCaseInsensitive&lt;/code&gt; and &lt;code&gt;validateIsUniqueCaseInsensitive&lt;/code&gt;, which do exactly what you expect them to do.&lt;/p&gt;

&lt;p&gt;If you're using IHP's login, be aware that these functions are now used for checking the emails. This increases UX for your users, but in case someone messed up and created two accounts - one with some letters of their email uppercase, one not - you will have to migrate that account.&lt;/p&gt;

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

&lt;p&gt;If you haven't tried out IHP yet, now's a great time to do so. We'll be at ZuriHack (huge Haskell Hackathon), so &lt;a href="https://zfoh.ch/zurihac2021/" rel="noopener noreferrer"&gt;sign up for free&lt;/a&gt; there if you haven't already and get an introduction to IHP from us directly!&lt;/p&gt;

</description>
      <category>functional</category>
      <category>haskell</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Why you should use IHP instead of express</title>
      <dc:creator>digitallyinduced</dc:creator>
      <pubDate>Wed, 03 Mar 2021 10:29:45 +0000</pubDate>
      <link>https://dev.to/digitallyinduced/why-you-should-use-ihp-instead-of-express-53id</link>
      <guid>https://dev.to/digitallyinduced/why-you-should-use-ihp-instead-of-express-53id</guid>
      <description>&lt;p&gt;express is one of the most popular options for writing webapps these days. IHP on the other hand is the fastest growing Haskell web framework.&lt;/p&gt;

&lt;p&gt;If you've been using express, this article is supposed to give you an intro into all of the good stuff you might be missing out on.&lt;/p&gt;

&lt;h1&gt;
  
  
  Live Reloading
&lt;/h1&gt;

&lt;p&gt;express and related tooling are notoriously known for the fact that to start any project, one will first have to do a lot of configuring to get nice developer tooling set up, but also to get everything running at all.&lt;/p&gt;

&lt;p&gt;IHP works on a different ideology: at digitally induced, we aim to make software engineering feel magical again. As such, configuration is turned down to a minimum, while providing the best developer tools out-of-the-box.&lt;/p&gt;

&lt;p&gt;One such tool is live reloading. While you can get some form of automatic server restarts on file save set up with express, it'll take a while to configure everything properly. And in the end, states will likely not be preserved. Additionally, and webpage that's been opened that requires reloading will have to be reloaded by hand (unless you want to spend multiple hours with configuration again).&lt;/p&gt;

&lt;p&gt;When using IHP, all of that is done for you. Whatever you change - whether that's frontend code, or backend code - it'll only require you to save the file for you to see the result in your browser, as the included developer tools reload everything for you. Even better, the website doesn't even have to do a full reload - instead, only the parts of the page are updated that actually need updating.&lt;/p&gt;

&lt;h1&gt;
  
  
  Websockets and Auto Refresh
&lt;/h1&gt;

&lt;p&gt;So live reload is great for developing, but there's a similar situation in production apps too: if the data on the server changes, we might want to update what's shown in an already opened website. When using express, you'll have to do some ajax to get that to work.&lt;/p&gt;

&lt;p&gt;With IHP, all the necessary tools are included to set up a websocket connection to the client very easily. If you happened to build a webapp using express instead, you'll have to pick up a second library for that, and coordinate between the two.&lt;/p&gt;

&lt;p&gt;However, since most websocket connections are used for the same thing (updating what the client sees based on changing information from the server), IHP makes it even simpler for developers: it's got auto refresh.&lt;/p&gt;

&lt;p&gt;By simply setting an action (the equivalent of a route) to have auto refresh enabled, any changes will be pushed to all open clients via a websocket connection whenever the database has updated data for the relevant page.&lt;/p&gt;

&lt;p&gt;It'll literately require you to do nothing more than type &lt;code&gt;autoRefresh&lt;/code&gt; before the code of the route. That's 11 letters. And you're done.&lt;/p&gt;

&lt;h1&gt;
  
  
  Code generators
&lt;/h1&gt;

&lt;p&gt;Speaking of saving keystrokes: in projects there tends to be some amount of boilerplate. And that's really annoying to type. With IHP, typing boilerplate is a thing of the past. Code generators allow you to generate everything that's not specific to your implementation, which means it takes seconds to get set up for new features.&lt;/p&gt;

&lt;p&gt;If you use the web ui instead of the CLI to do the code generation, you even get nice syntax-highlighted previews of what will be generated, so you can make sure everything works as expected. But honestly, I've never had a case where it didn't do what I wanted it to.&lt;/p&gt;

&lt;h1&gt;
  
  
  Functional Programming
&lt;/h1&gt;

&lt;p&gt;JavaScript is seeing a big move towards the paradigm of functional programming. Routes are simply functions in express, React is is defaulting to function components, and functions like &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt; and &lt;code&gt;reduce&lt;/code&gt; are all becoming more and more popular.&lt;/p&gt;

&lt;p&gt;Since IHP uses Haskell - a purely functional programming language - you get all of that goodness too. Only better, because it's the whole concept of Haskell.&lt;/p&gt;

&lt;p&gt;Being purely functional has other advantages than just being trending though. Pure functions do not have side effects. When something does go wrong, it's really easy to find out where to look for the error, because there's only a handful of places the bug could hide. While in express it could be nearly everywhere. The reason for this is that reasoning about the things a function does by just looking at its type signature is really easy. Speaking of which...&lt;/p&gt;

&lt;h1&gt;
  
  
  Type-safety
&lt;/h1&gt;

&lt;p&gt;JavaScript is a weakly and dynamically typed language. Haskell on the other hand is strongly and statically typed. That means that while JavaScript allows you to use any variable everywhere (even one that hasn't even been created before), Haskell will make sure that whatever variable is being used, makes sense at that place.&lt;/p&gt;

&lt;p&gt;Lots of people will say that dynamic and weak typing have advantages, and that's certainly true - there's definitely situations in which it is advantageous. But in most cases, there's only a limited subset of values that make sense, and that's where strong, static typing will prevent a whole host of bugs. Just look at the popularity of typescript, which attempts to bring strong, static typing to JavaScript. Since it's tacked-on it isn't as expressive as Haskell's type system, which has been developed for years and years already.&lt;/p&gt;

&lt;p&gt;If you're a Javascript developer and find writing typescript code annoying, I fully understand you. While typescript is great in its idea to bring strong, static typing to the web, it is lacking good type inference. That means that most people will have to write long, complicated type definitions by hand. In most cases Haskell will infer the type for you, meaning you get all the benefits, without the costs.&lt;/p&gt;

&lt;p&gt;The result is a language that will show you lots of bugs before you even run your code. You see lots of people saying they'd rather have code not compile for hours, instead of compiling and having to hunt down unnoticed bugs for days. And that's exactly what Haskell can do for you. Just try it, and you'll learn to love the compiler.&lt;/p&gt;

&lt;h1&gt;
  
  
  Faster performance thanks to Haskell green-threads
&lt;/h1&gt;

&lt;p&gt;Speaking of compiling. It's well-known that compiled languages tend to be faster than interpreted languages. That is another reason to use IHP, as Haskell will make your web app faster without you having to do anything, just because you use Haskell. Since express is running on Javascript, which is an interpreted language, it's very hard to get a get performance.&lt;/p&gt;

&lt;p&gt;To increase performance, multithreading is a useful tool that allows a developer to get big bang for your buck. That is, if you're able to use it. Which you won't be if you use express, since Javascript is a single-threaded language.&lt;/p&gt;

&lt;p&gt;Sure, there's async-await and promises, but everything still stays in one thread. Which makes reasoning about your program easy, but it doesn't allow you to get as performant as you otherwise could be.&lt;/p&gt;

&lt;p&gt;When using Haskell, you have it much easier. Multithreading is already done for you. You won't have to reason about race conditions and all those complicated situations that could arise by using async-await. Instead, the Haskell compiler is smart enough to multi-thread the parts of your application using something called green threads, without you having to do anything - and you will not run into any reasoning issues because of it. It basically is a free performance boost!&lt;/p&gt;

&lt;h1&gt;
  
  
  Deployment
&lt;/h1&gt;

&lt;p&gt;Due to its popularity, deploying express applications has never been easier. At digitally induced we understand that, which is why we've built &lt;a href="https://ihpcloud.com/"&gt;ihp-cloud&lt;/a&gt;. It's as simple as creating a git repository and telling us where to find it, and your repository can be deployed.&lt;/p&gt;

&lt;h1&gt;
  
  
  Community
&lt;/h1&gt;

&lt;p&gt;No doubt, express has a great community and lots of documentation. IHP's community is quickly growing too, however, and you'll always find help in our community slack at &lt;a href="https://ihp.digitallyinduced.com/Slack"&gt;https://ihp.digitallyinduced.com/Slack&lt;/a&gt;, where we from digitally induced (the company behind IHP) will do everything to assist you in getting started with IHP and Haskell.&lt;/p&gt;

&lt;p&gt;If you want to learn first-hand what the community of IHP is doing, feel free to join the first global IHP Meetup on March 17th, 2021 at 18:00 CET: &lt;a href="https://ihp-community-events.mailchimpsites.com/"&gt;Join IHP Global Meetup&lt;/a&gt;. We hope to see you there!&lt;/p&gt;

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

&lt;p&gt;If &lt;code&gt;undefined&lt;/code&gt; errors, configuration messes, hours of hunting bugs annoy you as much as it annoys us, now's the perfect time to try out IHP. Follow the &lt;a href="https://ihp.digitallyinduced.com/Guide/index.html"&gt;Guide&lt;/a&gt; to get set up, build, and deploy your first web app in minutes - then tell us what you liked and where we can improve even further. We hope to see you on March 17th!&lt;/p&gt;

</description>
      <category>functional</category>
      <category>haskell</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>IHP v0.9 is out!</title>
      <dc:creator>digitallyinduced</dc:creator>
      <pubDate>Mon, 01 Mar 2021 15:39:04 +0000</pubDate>
      <link>https://dev.to/digitallyinduced/ihp-v0-9-is-out-48e7</link>
      <guid>https://dev.to/digitallyinduced/ihp-v0-9-is-out-48e7</guid>
      <description>&lt;p&gt;&lt;strong&gt;Better performance, new plugins, logging, websockets, more query builder functions and more!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On friday we released a new version of IHP - the productivity-focused, magical-feeling-inducing haskell web framework for the future!&lt;/p&gt;

&lt;p&gt;To celebrate, here's the some of the awesomeness you can try out right now, after following the &lt;a href="https://ihp.digitallyinduced.com/Guide/index.html"&gt;Getting Started Guide&lt;/a&gt;:&lt;/p&gt;

&lt;h1&gt;
  
  
  New plugins
&lt;/h1&gt;

&lt;p&gt;While we're still developing the plugin API, we're already building some for you to use! This way we can make sure that the API makes sense and supports all use-cases. Now, with the new release, you can use these two new plugins with your IHP Apps:&lt;/p&gt;

&lt;h2&gt;
  
  
  ihp-sentry
&lt;/h2&gt;

&lt;p&gt;Error reporting is vital to web app development, and sentry is a popular solution for this. As such, it's now easier than ever to integrate it into your IHP app. Check out the plugin's readme for details on how to get it running inside your IHP app: &lt;a href="https://github.com/digitallyinduced/ihp-sentry"&gt;https://github.com/digitallyinduced/ihp-sentry&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ihp-zip
&lt;/h2&gt;

&lt;p&gt;Sometimes your users want to download a lot of files at once. Especially with the DSGVO, users could always request all their data from your website. What better way to handle this than implementing a simple zip-download containing everything they ask for? Previously this might've been a hassle, but now with the new ihp-zip plugin, it's a breeze. Just like before, check out the plugin's readme for installation and usage instructions: &lt;a href="https://github.com/digitallyinduced/ihp-zip"&gt;https://github.com/digitallyinduced/ihp-zip&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  WebSockets
&lt;/h1&gt;

&lt;p&gt;IHP has always used websockets to update the frontend, and now you can easily use a custom websocket connection too! Just think of the possibilities: chat applications, real-time updates, games...&lt;/p&gt;

&lt;p&gt;Of course, to get started you'll have to learn how to tap into this newfound power. Don't worry, we've got you covered: &lt;a href="https://ihp.digitallyinduced.com/Guide/websockets.html"&gt;https://ihp.digitallyinduced.com/Guide/websockets.html&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Better Performance
&lt;/h1&gt;

&lt;p&gt;Everyone loves themselves a faster server. It's best when you don't even have to do anything to get the benefits. So, IHP v0.9 uses GHC's new non-moving memory, which should improve performance of all those awesome IHP apps out there! (Not that there was a performance issue before... But faster is always better, isn't it?)&lt;/p&gt;

&lt;h1&gt;
  
  
  Logging
&lt;/h1&gt;

&lt;p&gt;We've already talked about error reporting. However, logging is just as important. And IHP now has its own Logging module ready for you to use! Say goodbye to &lt;code&gt;putStrLn&lt;/code&gt;, and say welcome our new overlords &lt;code&gt;Log.debug&lt;/code&gt;, &lt;code&gt;Log.info&lt;/code&gt;, &lt;code&gt;Log.error&lt;/code&gt;, &lt;code&gt;Log.warn&lt;/code&gt;, &lt;code&gt;Log.fatal&lt;/code&gt; and &lt;code&gt;Log.unknown&lt;/code&gt;! You can even customize what a single line of logging looks like. Check out the detailed documentation: &lt;a href="https://ihp.digitallyinduced.com/Guide/logging.html"&gt;https://ihp.digitallyinduced.com/Guide/logging.html&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Query Builder Functions
&lt;/h1&gt;

&lt;p&gt;There's a whole bunch of these:&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;withTransaction&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;...allows you to run multiple queries in a single database transaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;setJust&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;...so you can simply do &lt;code&gt;project |&amp;gt; setJust #userId (get #id user)&lt;/code&gt; instead of &lt;code&gt;project |&amp;gt; set #userId (Just (get #id user))&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;distinct&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;To remove duplicate rows from your query results.&lt;/p&gt;

&lt;p&gt;...and more!&lt;/p&gt;

&lt;h1&gt;
  
  
  Increased Magic
&lt;/h1&gt;

&lt;p&gt;Yepp, you heard that right. Actually this is related to AutoRoute. You can finally use more than just UUIDs and &lt;code&gt;Id Project&lt;/code&gt;-like arguments for your controllers, without having to manually configure &lt;code&gt;parseArgument&lt;/code&gt;. This simply means: less boilerplate, more magic, more productivity! We wrote it, so you don't have to.&lt;/p&gt;

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

&lt;p&gt;This release held a bunch of awesome things, and we're happy that it's finally out there. If you want to read up on all the other awesome things that changed, here's the link to the changelog on Github: &lt;a href="https://github.com/digitallyinduced/ihp/releases/tag/v0.9.0"&gt;https://github.com/digitallyinduced/ihp/releases/tag/v0.9.0&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you still haven't checked out IHP, now's as good a time as any. Also, join us in our community Slack: &lt;a href="https://ihp.digitallyinduced.com/Slack"&gt;https://ihp.digitallyinduced.com/Slack&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That's it! Tell us what your favorite feature of this release is, and till next time!&lt;/p&gt;

</description>
      <category>functional</category>
      <category>haskell</category>
    </item>
    <item>
      <title>Haskell: The Good Parts</title>
      <dc:creator>digitallyinduced</dc:creator>
      <pubDate>Wed, 24 Feb 2021 14:51:55 +0000</pubDate>
      <link>https://dev.to/digitallyinduced/haskell-the-good-parts-1gle</link>
      <guid>https://dev.to/digitallyinduced/haskell-the-good-parts-1gle</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was originally published on October 29th 2020 on the &lt;a href="https://ihp.digitallyinduced.com/ShowPost?postId=14ed1d41-5ea4-4608-9c96-465443cd6e55"&gt;IHP blog&lt;/a&gt;. Some tiny edits have made it into this version of the post.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There’s been a recent blog post &lt;a href="https://www.snoyman.com/blog/2020/10/haskell-bad-parts-1"&gt;"Haskell: The Bad Parts"&lt;/a&gt; in the haskell community. To keep things in balance and to spread some positive vibes we should also talk about the good parts of the haskell programming language and it’s ecosystem.&lt;/p&gt;

&lt;p&gt;Here are some of the best parts we encountered while using Haskell at digitally induced. We focus on the advantages in the web dev space because that is what we are currently working on.&lt;/p&gt;

&lt;h1&gt;
  
  
  Type Safety
&lt;/h1&gt;

&lt;p&gt;Haskell has one of the most impressive type systems of any programming language in practical use. If you have used TypeScript or other type safe languages in the past, you should be aware of the great advantages of having a type-checked codebase. Now think TypeScript - but 10x better. That’s how Haskell feels like.&lt;/p&gt;

&lt;p&gt;You save a lot of time debugging runtime errors. Once the compiler approved your code, you can be pretty sure that it is working. This kind of development process is usually a lot more fun than debugging why something is &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once your system has hit a certain size and when new feature requests are rolling in, you will want to make changes and refactor some parts of your code base. With Haskell you feel empowered to make changes to any part of your codebase.&lt;/p&gt;

&lt;p&gt;Compare this to the ruby ecosystem: When working with rails you usually need to have lots of tests or otherwise you cannot confidently refactor code after things are running in production. And even then: things will break. With the power of the type safety provided by Haskell, we can make refactorings whenever we want.&lt;/p&gt;

&lt;p&gt;It really is a blessing.&lt;/p&gt;

&lt;h1&gt;
  
  
  Managed Side Effects
&lt;/h1&gt;

&lt;p&gt;The way you deal with the file system, external APIs and user input is way different in Haskell than in other less functional programming languages. Your program consists of a main routine that handles the side effects and calls all your pure functions that do the real business logic.&lt;/p&gt;

&lt;p&gt;Systems built this way scale really well because there are less moving parts. Additionally, pure functions can be easily tested and changed later on.&lt;/p&gt;

&lt;p&gt;Most other languages encourage you to do side effects in an unrestricted way. For example: when working in Java, a call to an object method might indirectly change the state of many related objects. This means you cannot easily reason about what a method call does. In Haskell most functions are pure and thus don't trigger side effects like this. And when they do, you can see this already by the function's type signature.&lt;/p&gt;

&lt;p&gt;Haskell forces you to manage your side effects in a more careful way. You can still do IO and have mutable state, you just need to make this explicit inside the type signature. This leads to a far more robust system in overall.&lt;/p&gt;

&lt;h1&gt;
  
  
  Performance
&lt;/h1&gt;

&lt;p&gt;Out of the box the performance of Haskell based web applications is great. It just feels faster than your typical Rails or PHP application. Thanks to its highly optimized runtime system it can also &lt;a href="https://www.yesodweb.com/blog/2011/03/preliminary-warp-cross-language-benchmarks"&gt;handle way more requests than a nodejs application&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And you get all that without ever thinking about performance at all.&lt;/p&gt;

&lt;h1&gt;
  
  
  Tooling
&lt;/h1&gt;

&lt;p&gt;In 2020 it’s finally good. Thanks to &lt;a href="https://github.com/haskell/haskell-language-server"&gt;Haskell Language Server&lt;/a&gt; (&lt;a href="https://github.com/haskell/haskell-language-server/releases/tag/1.0.0"&gt;which just released version 1.0.0!&lt;/a&gt;) there’s now an easy way to have type information, documentation on hover and smart refactorings inside your text editor.&lt;/p&gt;

&lt;p&gt;With nix, cabal and stack we have the best tools for managing Haskell dependencies. Cabal hell is a thing of the past.&lt;/p&gt;

&lt;p&gt;Great things are also happening to the Haskell compiler itself. &lt;a href="https://github.com/ghc-proposals/ghc-proposals/pull/282"&gt;We can soon write dot expressions as you know from most other programming languages&lt;/a&gt;: project.name instead of name project.&lt;/p&gt;

&lt;h1&gt;
  
  
  Hiring Haskell Developers
&lt;/h1&gt;

&lt;p&gt;Haskell is a secret super power in that regard. The Haskell community consists of many very smart and talented software engineers. Haskell developers usually learn about Haskell because they care about their craft and about building high quality software products instead of learning about it to get a high paying job. Exactly the kind of people you want in your team.&lt;/p&gt;

&lt;h1&gt;
  
  
  2020 Haskell is Ready for Prime Time
&lt;/h1&gt;

&lt;p&gt;For years there has been this trend of growing use of type safety as well as the growing use of functional programming techniques. What language could fill this space better than Haskell. Haskell has really matured in the last years and in 2020 it feels like it’s finally ready to conquer the world.&lt;/p&gt;

&lt;p&gt;If this post made you interested, &lt;a href="https://ihp.digitallyinduced.com/Guide/index.html"&gt;check out IHP, our batteries-included haskell web framework&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>haskell</category>
      <category>functional</category>
      <category>performance</category>
      <category>typescript</category>
    </item>
    <item>
      <title>IHP and elm - towards a functional web</title>
      <dc:creator>digitallyinduced</dc:creator>
      <pubDate>Mon, 25 Jan 2021 15:34:48 +0000</pubDate>
      <link>https://dev.to/digitallyinduced/ihp-and-elm-towards-a-functional-web-2581</link>
      <guid>https://dev.to/digitallyinduced/ihp-and-elm-towards-a-functional-web-2581</guid>
      <description>&lt;p&gt;At digitallyinduced we are working on IHP, the fastest-growing, open-source haskell web framework out there, because we believe functional programming to be the future. We want to make it accessible to everyone, even functional-programming-beginners!&lt;/p&gt;

&lt;p&gt;To reach that goal, we have recently added an elm template, which allows you to get started with elm perfectly set up and ready to go in a new IHP project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Getting started is as simple as &lt;a href="https://ihp.digitallyinduced.com/Guide/installation.html"&gt;installing the &lt;code&gt;ihp-new&lt;/code&gt; command&lt;/a&gt;, and then running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ihp-new --elm my-new-project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll get a fully set up IHP project with elm in a new folder called &lt;code&gt;my-new-project&lt;/code&gt;. Check out the &lt;a href="https://ihp.digitallyinduced.com/Guide/your-first-project.html"&gt;Guide&lt;/a&gt; to learn what to do with the project, including where to find the elm code (hint: it's in &lt;code&gt;my-new-project/elm/&lt;/code&gt; ;) ), and how to deploy the project for free using ihpcloud!&lt;/p&gt;

&lt;p&gt;Try it out, and tell us how you like it!&lt;/p&gt;

</description>
      <category>functional</category>
      <category>haskell</category>
      <category>elm</category>
    </item>
  </channel>
</rss>
