<?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: jlei523</title>
    <description>The latest articles on DEV Community by jlei523 (@jlei523).</description>
    <link>https://dev.to/jlei523</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%2F220224%2F4f39d854-0764-40ff-85ef-57a90c47078b.JPG</url>
      <title>DEV Community: jlei523</title>
      <link>https://dev.to/jlei523</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jlei523"/>
    <language>en</language>
    <item>
      <title>Is there an opportunity for a Substack + Dev.to hybrid?</title>
      <dc:creator>jlei523</dc:creator>
      <pubDate>Tue, 11 Aug 2020 03:46:38 +0000</pubDate>
      <link>https://dev.to/jlei523/is-there-an-opportunity-for-a-substack-dev-to-hybrid-cej</link>
      <guid>https://dev.to/jlei523/is-there-an-opportunity-for-a-substack-dev-to-hybrid-cej</guid>
      <description>&lt;p&gt;Dev.to lets you reach a large audience instantly. But you don't control your audience and brand. 80% of the benefits go to Dev.to. 20% to you.&lt;/p&gt;

&lt;p&gt;Substack lets you control your audience and your brand. But you have to slowly build your own audience. However, 80% of the benefits go to you. 20% to Substack.&lt;/p&gt;

&lt;p&gt;Is there an opportunity to build a hybrid? &lt;/p&gt;

&lt;p&gt;You get your own website (via a subdomain or your own domain name). Then the product aggregates articles, creates nice category pages, and helps you find audiences.&lt;/p&gt;

&lt;p&gt;If a Dev.to or Substack employee is interested in this concept, please reach out! I'm thinking of building this hybrid myself or I can join your company to do it.&lt;/p&gt;

</description>
      <category>writing</category>
      <category>startup</category>
    </item>
    <item>
      <title>I made an app that generates unlimited Avatars</title>
      <dc:creator>jlei523</dc:creator>
      <pubDate>Fri, 08 Nov 2019 16:00:07 +0000</pubDate>
      <link>https://dev.to/jlei523/i-made-an-app-that-generates-unlimited-avatars-531d</link>
      <guid>https://dev.to/jlei523/i-made-an-app-that-generates-unlimited-avatars-531d</guid>
      <description>&lt;p&gt;&lt;a href="http://getavatars.app/"&gt;GetAvatars.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Would love to hear your feedback.&lt;/p&gt;

&lt;p&gt;This was made using React.js, Next.js, and hosted on Zeit's serverless deployment.&lt;/p&gt;

&lt;p&gt;Artwork credit: &lt;a href="https://avataaars.com/"&gt;https://avataaars.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>emojis</category>
    </item>
    <item>
      <title>Performance Review: AWS RDS Postgres vs Digital Ocean Postgres</title>
      <dc:creator>jlei523</dc:creator>
      <pubDate>Wed, 23 Oct 2019 20:41:47 +0000</pubDate>
      <link>https://dev.to/jlei523/performance-review-aws-rds-postgres-vs-digital-ocean-postgres-3f2b</link>
      <guid>https://dev.to/jlei523/performance-review-aws-rds-postgres-vs-digital-ocean-postgres-3f2b</guid>
      <description>&lt;p&gt;When Digital Ocean announced the availability of a managed Postgres database service, I wanted to answer this question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Would my app’s performance improve if I switched my Postgres instance from AWS to Digital Ocean?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm working on &lt;a href="https://medtally.com" rel="noopener noreferrer"&gt;Medtally.com&lt;/a&gt;, a data-driven medical community. Because nearly every page on Medtally requires fresh data, the speed of the database is crucial to providing a fast experience for users.&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%2Fi.imgur.com%2FwZdhlNi.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%2Fi.imgur.com%2FwZdhlNi.png" alt="medtally"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example page on Medtally. Every page request pulls data from a Postgres database.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Thanks to the generosity of Digital Ocean's Hatch program, I was able to get some free credits to run a test.&lt;/p&gt;

&lt;h1&gt;
  
  
  Test setup:
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;AWS (original)&lt;/strong&gt;: Postgres 11 (2 vCPUs, 4GB RAM, 40GB storage, AWS us-west-1)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Digital Ocean&lt;/strong&gt;: Postgres 11 (2 vCPUs, 4GB RAM, 38GB storage, SFO2)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;App hosting&lt;/strong&gt;: Zeit Now v1 (SFO, AWS us-west-1)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;App stack&lt;/strong&gt;: Express.js, React.js, Next.js (&lt;a href="https://dev.to/jlei523/6-tips-using-next-js-for-your-next-web-app-1hhh"&gt;read my tips on building with Next.js&lt;/a&gt;)&lt;/p&gt;

&lt;h1&gt;
  
  
  How performance was measured:
&lt;/h1&gt;

&lt;p&gt;To measure performance as close to the real-world as possible, I tracked the response time of a common query required to fetch data for our popular health condition pages (&lt;a href="https://medtally.com/condition/ulcerative-colitis" rel="noopener noreferrer"&gt;example&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;I used Node.js' &lt;code&gt;perf_hooks&lt;/code&gt; API and logged the results.&lt;/p&gt;

&lt;p&gt;Here's the example code I used:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const { performance } = require('perf_hooks');&lt;br&gt;
const t0 = performance.now(); //start timer&lt;br&gt;
const data = await fetchDataFromDB(id);&lt;br&gt;
const t1 = performance.now(); //end timer&lt;br&gt;
const responseTime = t1 - t0 //responsetime in ms&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Results after ~2,000+ samples:
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Average response time:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS: 145ms&lt;/li&gt;
&lt;li&gt;Digital Ocean: 233ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Median response time:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS: 63ms&lt;/li&gt;
&lt;li&gt;Digital Ocean: 164ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Digital Ocean's response time was 60% longer on average and 160% by median.&lt;/p&gt;

&lt;p&gt;The app felt noticeably slower while I clicked around. The Medtally app can load pages in as little as 150ms. Adding 100ms to that time would mean 66% slower - a significant increase.&lt;/p&gt;

&lt;p&gt;I was surprised. I expected the performance to be much closer since both databases had the same number of CPUs, RAM size, and disk size.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Some possible reasons why AWS was faster:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The AWS Postgres instance had an advantage in latency because it was hosted in the same physical location as my app (AWS us-west-1 which is San Francisco). However, Digital Ocean's database was also hosted in San Francisco and so the difference should have been negligible and should not have been a 100ms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Update: The difference is only 2ms after I ran a few pinging tests between the AWS us-west-1 and Digital Ocean SFO2. We can rule this reason out.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: I could have eliminated latency as a variable in the test by basing the conclusion on query execution times in &lt;code&gt;pg_stat_statements&lt;/code&gt; instead. I did not have enough time to do this but I may revisit in the future.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;AWS Postgres simply uses faster hardware (CPU, RAM, SSD).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Digital Ocean's managed Postgres service is new and the company needs more time to optimize its performance. AWS RDS has been in service for 9 years compared to less than a year for Digital Ocean.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Back to AWS RDS
&lt;/h1&gt;

&lt;p&gt;I will be sticking with AWS RDS until Digital Ocean improves its performance.&lt;/p&gt;

&lt;p&gt;Besides the performance issues, I really did enjoy using it. The UI/UX is far superior to AWS and so is the documentation. I have high hopes for it in the future and we could use some strong competition in managed database hosting.&lt;/p&gt;

&lt;p&gt;With that said, &lt;a href="https://medtally.com" rel="noopener noreferrer"&gt;Medtally.com&lt;/a&gt; is back to using AWS RDS.&lt;/p&gt;




&lt;p&gt;Interesting in connecting with me? You can find me on &lt;a href="https://twitter.com/josephlei" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>postgres</category>
      <category>performance</category>
      <category>database</category>
    </item>
    <item>
      <title>6 tips: Using Next.js for your next web app
</title>
      <dc:creator>jlei523</dc:creator>
      <pubDate>Thu, 29 Aug 2019 05:40:02 +0000</pubDate>
      <link>https://dev.to/jlei523/6-tips-using-next-js-for-your-next-web-app-1hhh</link>
      <guid>https://dev.to/jlei523/6-tips-using-next-js-for-your-next-web-app-1hhh</guid>
      <description>&lt;p&gt;Next.js is indispensable when you want to build a high performance React app. &lt;/p&gt;

&lt;p&gt;It’s being used to power some of my apps such as &lt;a href="https://medtally.com"&gt;Medtally&lt;/a&gt;, a data-driven medical community and &lt;a href="https://truehome.hk"&gt;True Home&lt;/a&gt;, an automated home valuation tool for properties in Hong Kong.&lt;/p&gt;

&lt;p&gt;Along the way, I learned a few tricks and figured out a few “gotchas” of the framework that beginners might find useful. And without further ado, let’s get started:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. You need to cache the data from getInitialProps or the browser’s back button behavior will break.
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;getInitialProps&lt;/code&gt; is used to fetch data for the page but it also fires when the user presses the back button on the browser. This causes the browser to scroll to the position where you previously left off but without the remote data from &lt;code&gt;getInitialProps&lt;/code&gt; needed to render. You can read more about this issue &lt;a href="https://github.com/zeit/next.js/issues/3303"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To solve this problem, you need to cache the data on the client side after fetching it. Here’s a quick and simple way to do it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;getInitialProps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;asPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;//if data is in cache then use the cache&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;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;someID&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;someID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//if not, then fetch from server&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`someAPI`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//check if client, if so store the data in the cache. &lt;/span&gt;
      &lt;span class="c1"&gt;//If you don't do this check, there will be a separate cache stored on the server since Next.js does server side rendering as well.&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;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;someID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="p"&gt;(){&lt;/span&gt;

       &lt;span class="c1"&gt;//your components&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;You can also do this &lt;a href="https://stackoverflow.com/questions/42980613/how-do-i-implement-caching-in-redux"&gt;with Redux&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Use Next.js’ Link component instead of Router.push so Google can crawl your URLs
&lt;/h3&gt;

&lt;p&gt;Google’s crawler doesn’t see links written like this: &lt;code&gt;&amp;lt;div onClick={handleClickWithRouter}&amp;gt;Go to About Page!&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So avoid writing your links with Next’s Router if possible. Instead, use Next’s &lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt; component like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Link&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;next/link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;Click&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/about&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;here&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&amp;gt;{' '&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;      &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;read&lt;/span&gt; &lt;span class="nx"&gt;more&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Next.js works better with Material UI React than Semantic UI React
&lt;/h3&gt;

&lt;p&gt;If you’re trying to choose between Material UI and Semantic UI React component libraries to go along with your Next.js app, you should choose Material UI.&lt;/p&gt;

&lt;p&gt;Semantic UI’s responsive components aren’t built in a way that works well with Next.js’ server-side rendering because they look for the browser’s &lt;code&gt;window&lt;/code&gt; object which isn’t available on the server.&lt;/p&gt;

&lt;p&gt;If you must use Semantic UI, you can hack it together by following this GitHub &lt;a href="https://github.com/Semantic-Org/Semantic-UI-React/issues/3361"&gt;ticket&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On the other hand, Material UI’s responsive components only use media queries which means your components should render in the same way on the server and in the client.&lt;/p&gt;

&lt;p&gt;If I convinced you, head over to Material UI’s &lt;a href="https://github.com/mui-org/material-ui/tree/next/examples/nextjs"&gt;Next.js example&lt;/a&gt; to get started.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. If you use isomorphic-unfetch to do your data fetching, you’ll need to provide the absolute URL
&lt;/h3&gt;

&lt;p&gt;The Next.js creators recommend a library called &lt;code&gt;isomorphic-unfetch&lt;/code&gt; for data fetching. It has a much smaller bundle size than &lt;code&gt;axios&lt;/code&gt; and works well on both the client and the server.&lt;/p&gt;

&lt;p&gt;However, isomorphic-unfetch requires an absolute URL or it will fail. I’m assuming it has something to do with the different environments (client &amp;amp; server) on which your code can be executed. Relative URLs are just not explicit &amp;amp; reliable enough in this case.&lt;/p&gt;

&lt;p&gt;You can construct an absolute URL from getInitialProps like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;getInitialProps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;asPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;baseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Host&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;relativeURL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Store your URLs in the database if you want the prettiest of pretty URLs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;URLs should be pretty so people want to click on them when they see them on Google.&lt;/p&gt;

&lt;p&gt;You generally want to avoid having the database table id in your URL like this: &lt;code&gt;/post/245/i-love-nextjs/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ideally, your URLs should be something like this: &lt;code&gt;/post/i-love-nextjs&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;But this URL is missing the id 245 needed to fetch the data from the database.&lt;/p&gt;

&lt;p&gt;To solve this problem, you’ll need to store the URL in the database like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;title&lt;/th&gt;
&lt;th&gt;Content&lt;/th&gt;
&lt;th&gt;url&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;245&lt;/td&gt;
&lt;td&gt;I love Next.js&lt;/td&gt;
&lt;td&gt;because...&lt;/td&gt;
&lt;td&gt;/post/i-love-nextjs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On the server, write code to fetch data using the URL in lieu of the post id when someone requests mywebsite.com/post/i-love-nextjs. Here’s an example using Express.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/post/:slug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;actualPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;//get the data using '/post/i-love-nextjs' as the id&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetchDataWithURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&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;postContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;  &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;actualPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;postContent&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 recommend &lt;code&gt;slugify&lt;/code&gt; if you need a library to convert text into URLs.&lt;br&gt;
If you have many URLs and you’re using a relational database, you should consider adding an index to the url column so that your lookup query runs faster.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;CSS breaks in prod but not development when using Material UI with Next.js
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function createPageContext() {
  return {
    theme,
    sheetsManager: new Map(),
    sheetsRegistry: new SheetsRegistry(),


    //add this to fix broken css in prod
    generateClassName: createGenerateClassName({
      productionPrefix: "prod"
    })


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



&lt;p&gt;If this happens to you, try adding this line to your getPageContext.js file:&lt;/p&gt;

&lt;p&gt;And that’s it!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Search autocomplete for 2 million records with React &amp; AWS CloudSearch
</title>
      <dc:creator>jlei523</dc:creator>
      <pubDate>Thu, 29 Aug 2019 05:25:20 +0000</pubDate>
      <link>https://dev.to/jlei523/search-autocomplete-for-2-million-records-with-react-aws-cloudsearch-3bld</link>
      <guid>https://dev.to/jlei523/search-autocomplete-for-2-million-records-with-react-aws-cloudsearch-3bld</guid>
      <description>&lt;p&gt;&lt;a href="https://truehome.hk"&gt;True Home&lt;/a&gt; is a bootstrapped web app that provides a home value estimate for every property in Hong Kong — this means over 2 million homes.&lt;/p&gt;

&lt;p&gt;Allowing users to look up their home as efficiently as possible became a challenge.&lt;/p&gt;

&lt;p&gt;To make it easy for users to find their home, we built a search autocomplete service using the following stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS CloudSearch&lt;/li&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;react-autosuggest &amp;amp; autosuggest-highlight modules&lt;/li&gt;
&lt;li&gt;Express.js server&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A few tidbits on Hong Kong real estate:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The majority of the population lives in tall apartment buildings that can have hundreds of units.&lt;/li&gt;
&lt;li&gt;Every building in Hong Kong has a unique name like “The Kennedy on Belchers”.&lt;/li&gt;
&lt;li&gt;Hong Kongers don’t generally refer to where they live by an address such as “123 Main Street”. Rather, they use their building name and district like “The Belchers Block A in Causeway Bay”.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QFpbKzD_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1288/1%2Ao_RsSmbu786B3ZraDlw1nA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QFpbKzD_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1288/1%2Ao_RsSmbu786B3ZraDlw1nA.png" alt="HK buildings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;People in Hong Kong live like this. The middle building also happens to be my current home!&lt;/p&gt;

&lt;h2&gt;
  
  
  Our search autocomplete requirements:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Can’t use Google Places API because there is no way to connect the results to our database records.&lt;/li&gt;
&lt;li&gt;Can’t use Algolia because it’s simply far too expensive for our bootstrapped app ($700 USD/month for 2 million records).&lt;/li&gt;
&lt;li&gt;Users should be able to search by the building’s name.&lt;/li&gt;
&lt;li&gt;Users should be able to search for their exact unit by the building name and the unit number.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Before we build, we design!
&lt;/h2&gt;

&lt;p&gt;To design the look and feel of our search functionality, I used Sketch App and drew inspirations from where I once worked, Redfin.&lt;/p&gt;

&lt;p&gt;Nailing the design early was important because it helped me figure out what tools I needed to use and what data was required.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oHnmAxx8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/5568/1%2AJVrUB_GxTaN5WVHYnIRlJA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oHnmAxx8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/5568/1%2AJVrUB_GxTaN5WVHYnIRlJA.png" alt="design"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Now let’s prep the data for AWS CloudSearch
&lt;/h2&gt;

&lt;p&gt;You can prep your data in JSON, CSV, xml or txt formats. We chose JSON because batch uploading only supports JSON and xml formats.&lt;/p&gt;

&lt;p&gt;True Home has two search categories: buildings and units. &lt;/p&gt;

&lt;p&gt;Here’s an example of how our JSON file looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
  {
    "buildingaddress": "8 LEUNG TAK STREET",
    "Name": "EIGHT REGENCY (Tuen Mun)",
    "type": "building"
  },
  {
    "buildingaddress": "8 LEUNG TAK STREET",
    "Name": "31/F FLAT N - NA EIGHT REGENCY (Tuen Mun)",
    "type": "unit"
  }
] 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Uploading data to AWS CloudSearch
&lt;/h2&gt;

&lt;p&gt;You can upload data in two ways: via the AWS GUI console or via the terminal through &lt;code&gt;aws&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;The maximum file size you can upload to AWS CloudSearch is only 5mb. This presented a problem for us because we had 2 million records totaling 900mb of data to upload!&lt;/p&gt;

&lt;p&gt;To solve this problem, we had to generate 180 JSON files, each slightly under 5mb, and batch uploaded them via the aws command line tool.&lt;br&gt;
Here’s the bash script we used to loop through all 180 JSON files and upload to our endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for VARIABLE in $(ls *.json); do echo $VARIABLE; aws cloudsearchdomain --endpoint-url {ENDPOINT URL here} upload-documents --content-type application/json --documents $VARIABLE; sleep 1s; done
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing the search results
&lt;/h2&gt;

&lt;p&gt;One nice thing AWS CloudSearch provides is the ability to test search your data immediately in the console.&lt;/p&gt;

&lt;p&gt;Here, we can test our newly uploaded data:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P4nAmLde--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/2236/1%2ACYwkIZFO8EDvyFyNdKsRXA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4nAmLde--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/2236/1%2ACYwkIZFO8EDvyFyNdKsRXA.png" alt="aws"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up an Express.js endpoint for AWS CloudSearch
&lt;/h2&gt;

&lt;p&gt;Once you’ve verified that you can search your newly uploaded data, let’s spin up an API on the server to query the data. True Home happens to use Express.js.&lt;/p&gt;

&lt;p&gt;The data flow works like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;User types a search → search query is sent to Express server → Express server gets data from CloudSearch endpoint → Express sends search results back to browser&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Wait a minute! Why do we need to go through a server? Why not just query the CloudSearch endpoint directly from the browser?&lt;/p&gt;

&lt;p&gt;Unfortunately, CloudSearch doesn’t support CORS which means you either have to go through a server like Express.js or set up some kind of proxy service, both of which will add latency to each query.&lt;/p&gt;

&lt;p&gt;Luckily for us, the latency hit isn’t too big because our server and CloudSearch instance are hosted in the same AWS location.&lt;/p&gt;

&lt;p&gt;Here’s an example of how to set up the Express API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/autocomplete/:searchString&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cloudSearchEndpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your endpoint here&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`  &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/2013-01-01/search?q=~&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;
        &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchString&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;return=_all_fields%2C_score&amp;amp;highlight.label=%7B%22max_phrases%22%3A3%2C%22format%22%3A%22text%22%2C%22pre_tag%22%3A%22*%23*%22%2C%22post_tag%22%3A%22*%25*%22%7D&amp;amp;highlight.unitcode=%7B%22max_phrases%22%3A3%2C%22format%22%3A%22text%22%2C%22pre_tag%22%3A%22*%23*%22%2C%22post_tag%22%3A%22*%25*%22%7D&amp;amp;sort=_score+desc`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;CloudSearch has official Javascript support but I had a hard time getting it to work for some reason. AWS documentation, in general, is lacking.&lt;br&gt;
As a workaround, I simply used the auto-generated endpoint from the testing tool as my Express fetch URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the React component
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Cg5FCHEH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/2056/1%2AwXgz3bA5QUbGLU3w6Usq2w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Cg5FCHEH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/2056/1%2AwXgz3bA5QUbGLU3w6Usq2w.png" alt="react"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;True Home’s React search component is built with react-autosuggest. We chose this module because it has excellent documentation and easy to follow examples.&lt;/p&gt;

&lt;p&gt;Initially, I was worried about the difficulty of highlighting the words as the user typed but autosuggest-highlight made this a breeze.&lt;/p&gt;

&lt;p&gt;All in all, the front-end code took about 4 hours to complete. Most of the time was spent on formatting the data from CloudSearch and the rest was spent on styling the component.&lt;/p&gt;

&lt;p&gt;Here’s &lt;a href="https://gist.github.com/jlei523/dbe6b37540da793b5ada5cee9b00e58a"&gt;True Home’s search component&lt;/a&gt; in its entirety for reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  The result
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://thumbs.gfycat.com/WhisperedAfraidAoudad-mobile.mp4"&gt;https://thumbs.gfycat.com/WhisperedAfraidAoudad-mobile.mp4&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: Search autocomplete is surprisingly easy to build with modern tools but I wouldn’t use CloudSearch again
&lt;/h2&gt;

&lt;p&gt;The whole feature took about 32 hours to complete — much faster than I initially thought as I had no prior experience with search.&lt;/p&gt;

&lt;p&gt;As a comparison, it took well over a month to build Redfin’s search functionality back in 2014 by a far more experienced engineer. Granted, Redfin’s search had more requirements, more data, and more platforms to support.&lt;/p&gt;

&lt;p&gt;The most time-consuming parts were prepping the data for CloudSearch and looking up CloudSearch’s awful and sparse documentation.&lt;/p&gt;

&lt;p&gt;Amazon doesn’t seem to be improving CloudSearch anymore. The last major update was all the way back in 2013. I suspect that this is due to Elastisearch surpassing Solr (what CloudSearch is based on) in popularity.&lt;/p&gt;

&lt;p&gt;If I had to do this over again, I would choose Elasticsearch over CloudSearch because the former has better documentation and supports CORS.&lt;/p&gt;

&lt;p&gt;And that’s it!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>aws</category>
      <category>search</category>
      <category>react</category>
    </item>
  </channel>
</rss>
