<?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: Omar Bahareth</title>
    <description>The latest articles on DEV Community by Omar Bahareth (@obahareth).</description>
    <link>https://dev.to/obahareth</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%2F30971%2Fe3b99f19-51b9-4c74-acb4-919beb2ea33b.png</url>
      <title>DEV Community: Omar Bahareth</title>
      <link>https://dev.to/obahareth</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/obahareth"/>
    <language>en</language>
    <item>
      <title>Are there functions similar to Ruby's `dig` in other languages?</title>
      <dc:creator>Omar Bahareth</dc:creator>
      <pubDate>Thu, 22 Aug 2019 15:18:26 +0000</pubDate>
      <link>https://dev.to/obahareth/are-there-functions-similar-to-ruby-s-dig-in-other-languages-4c08</link>
      <guid>https://dev.to/obahareth/are-there-functions-similar-to-ruby-s-dig-in-other-languages-4c08</guid>
      <description>&lt;p&gt;I really like using Ruby's &lt;code&gt;Array#dig&lt;/code&gt; and &lt;code&gt;Hash#dig&lt;/code&gt; operator (introduced in Ruby 2.3) to quickly and safely access deeply nested structures&lt;/p&gt;

&lt;p&gt;I would love to see different versions of it in other languages so I can use them more too!&lt;/p&gt;

&lt;p&gt;Here's how dig works:&lt;/p&gt;

&lt;p&gt;Assuming we have an &lt;code&gt;orders&lt;/code&gt; array that looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;customer: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"Customer 1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;phone: &lt;/span&gt;&lt;span class="s2"&gt;"1234"&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="ss"&gt;id: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

&lt;span class="c1"&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 easily navigate through this structure with &lt;code&gt;dig&lt;/code&gt; like so&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:phone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "1234"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also not worry about any of the "in-between" objects not existing, as it will return nil as soon it finds something that doesn't exist.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:phone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It returns &lt;code&gt;nil&lt;/code&gt; the moment it finds that &lt;code&gt;customer&lt;/code&gt; doesn't exist, and it makes me avoid checking if keys exist every time I want to access a nested object.&lt;/p&gt;

&lt;p&gt;What are some cool ways to access nested data like this in other languages? I ask because I want to learn and because I probably do it in overly-convoluted ways at the moment.&lt;/p&gt;

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

</description>
      <category>ruby</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Engineering Management: An Overview</title>
      <dc:creator>Omar Bahareth</dc:creator>
      <pubDate>Sun, 16 Jun 2019 20:07:21 +0000</pubDate>
      <link>https://dev.to/obahareth/engineering-management-an-overview-1814</link>
      <guid>https://dev.to/obahareth/engineering-management-an-overview-1814</guid>
      <description>&lt;p&gt;I was an engineer for most of my career and one day I became a manager and I was quite lost and afraid in the beginning, I realized I hadn’t shared my experiences from back then. I’ve been doing engineering management on and off for the past couple of years and I decided to share a short summary of what I learned.&lt;/p&gt;

&lt;p&gt;A lot of this is stuff I learned from books, articles, websites, and some of this is stuff I learned on the job. Full credit to all the resources I learned from is at the bottom in the “Great resources” section, and I heavily recommend exploring them if this article captured your interest. I like to use the term “Maker” (engineer, designer, or anyone who makes stuff) and “Manager”, both of which I learned from the book Leading Snowflakes. I wrote this from the perspective of someone who has been a Maker for most of his career and transitioned to Management a couple of years ago.&lt;/p&gt;

&lt;h1&gt;
  
  
  What to expect
&lt;/h1&gt;

&lt;h2&gt;
  
  
  You need to write, a lot
&lt;/h2&gt;

&lt;p&gt;Meeting notes, peer feedback, feedback to you, documenting a task before you hand it to an engineer, and so on. Think of yourself as the standard for documentation in your organization or team, because you kind of represent that now. You’ll get so much in your plate that it’s impossible to remember it all, writing is your best friend.&lt;/p&gt;

&lt;h2&gt;
  
  
  You’ll talk to people and be in meetings a lot more
&lt;/h2&gt;

&lt;p&gt;Management is all about people interactions, you’ll be dealing with people a lot more than before and that means meetings. Too many meetings for your team is a productivity killer and that’s also something you’ll need to figure out how to cut down if it becomes an issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn to measure your success by your team’s success, not what you make yourself.
&lt;/h2&gt;

&lt;p&gt;When I started being a leader I was used to measuring myself as an engineer or as a “maker” (measuring by what I make). When I first started being a manager I used to feel like I haven’t accomplished anything in my day and this was difficult.&lt;/p&gt;

&lt;h2&gt;
  
  
  You’ll be exposed to a lot more conflicts
&lt;/h2&gt;

&lt;p&gt;You’ll see a lot of conflicts between people, sometimes over very small issues. A lot of your time will go towards handling them. This may not be the case for every manager but it was the hardest part of the job for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hard Choices
&lt;/h2&gt;

&lt;p&gt;You’re gonna have to let some people go, you’re gonna have to step in to break up conflicts, and sometimes you’ll have people who are trying their best but still hurting teammates. This isn’t an easy path and it’s full of people issues and conflicts. Even if you have a great hiring process, it won’t be perfect and you’ll hire the wrong person sometimes. There’s no right or wrong answer in almost any situation, you need to have peers and mentors you can trust and ask for guidance. Don’t make hard decisions alone, and definitely don’t make them when you’re not calm.&lt;/p&gt;

&lt;h2&gt;
  
  
  You’ll need to do things that many people don’t want or aren’t willing to do
&lt;/h2&gt;

&lt;p&gt;Improving code standards, bug report structures, doing some things manually until they’re automated, improving communication between your team and other departments, and so on. Your goal is to keep the team focused and removing obstacles from their way, and those obstacles are often easy/boring tasks that no one wants to do.&lt;/p&gt;

&lt;h1&gt;
  
  
  What to strive to do
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1:1 meetings
&lt;/h2&gt;

&lt;p&gt;Regularly sit down with your team mates, connect with them, understand their goals and what they want to achieve and learn, understand what’s troubling them. Put this on a schedule, it’s extremely important to do this regularly. Write down the main points that go on in your 1:1 meetings, it’s important for everything you need to do on this job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mentorship
&lt;/h2&gt;

&lt;p&gt;You need to help your teammates get better, sometimes in what they want to get better at, and sometimes in things you believe they need to learn for the sake of their team. This can be accomplished by pairing them with someone more experienced in an area they want to get better at, getting them access to websites like PluralSight, etc. Sometimes by mentoring them yourself. You have to give them goals, ask them to experiment with new things, help them write articles, give talks, and so on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scheduling
&lt;/h2&gt;

&lt;p&gt;You’ll probably be in a lot of meetings and people need an easy way to sit down and talk with you. Have an easy way for them to book some of your time, Google Calendar, &lt;a href="https://freebusy.io/"&gt;Freebusy.io&lt;/a&gt;, &lt;a href="https://calendly.com/"&gt;Calendly&lt;/a&gt;, etc. are all great tools for this.&lt;/p&gt;

&lt;p&gt;You also need to learn to switch between your Maker and Manager modes, it’s good to regularly have this scheduled either some days of the week or some hours of certain days. You need time to hop on in and review some work (pull requests, designs, etc.) and contribute to them too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping your “Maker” skills polished
&lt;/h2&gt;

&lt;p&gt;You won’t have as much time for “Making” as you used to before, and you might get rusty. Take the time to keep yourself up to date with what your team is doing, and of new developments in your field. You need to be able to understand at a general level what your team does so you can help them prioritize.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep a list of tasks
&lt;/h2&gt;

&lt;p&gt;Don’t forget anything. Write things down. Anything anyone asks of you, jot it down on some app and slap a due date on it (I use &lt;a href="https://todoist.com"&gt;Todoist&lt;/a&gt;). This list is also great for delegating. Got a complex task coming up that you’d like a teammate to have experience in? Delegate it. Got a lot of meetings today and your teammate asked for access to something or to try out a new tool, write down the task, don’t forget them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hiring
&lt;/h2&gt;

&lt;p&gt;The first thing you need to look at when hiring is whether these candidates will fit in with your team or not. Sit down with your teammates and peers and decide what’s a must-have and what’s a nice-to-have for your workplace. Do you care about about writing well in your organization? Are you ok with people learning it on the job? Or is it a must-have to even join?&lt;/p&gt;

&lt;p&gt;Avoid brain teaser interview questions, avoid theoretical scenarios and whiteboard questions that people will never have to do on the job, and make sure your hiring process involves the most real scenarios as possible. Get the candidate to work with your team on an actual task, and compensate them for it. &lt;a href="https://blog.usejournal.com/rethinking-how-we-interview-in-microsofts-developer-division-8f404cfd075a"&gt;Microsoft’s new process&lt;/a&gt; is my favorite way to hire, I highly recommend reading it. Not all organizations have the resources to do all this stuff and that’s understandable but try your hardest to accomplish these two things when hiring:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get the candidate to do some real work.&lt;/li&gt;
&lt;li&gt;Have your teammates and the candidate interact when doing real work.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Hire juniors
&lt;/h3&gt;

&lt;p&gt;Too often organizations only look for people who already know most of what they need and don’t hire enough juniors. Juniors are great to have in any organization because they want to learn and there’s a lot of them. A lot of the time after juniors grow and gain more skills within your organization, they’ll start to get better offers from other places so some think this isn’t a good investment, but I believe this is something organizations must try their hardest to do, especially in developing countries, because those juniors need to grow and they need help and training, and because you’ll have more manpower to deal with the many small bugs that are often overlooked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let your team grow / delegate
&lt;/h2&gt;

&lt;p&gt;As a manager, you need to delegate. Jumping into Maker mode to get something done is something you were probably used to before but you now need to let your team be responsible for most of the “Making”.&lt;/p&gt;

&lt;p&gt;Learning by doing is the best way to learn. If there’s an area you’re an expert in as a Maker, don’t immediately jump in to Maker mode and try to do it, let your teammates experience it and grow. Write them a short guide of what needs to be done and things to look out for and trust them to get it done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Encourage knowledge sharing
&lt;/h2&gt;

&lt;p&gt;Ensure your teammates have an accessible place to find and share lessons learned, no matter how small they are (Something like &lt;a href="https://stackoverflow.com/teams"&gt;StackOverflow for teams&lt;/a&gt;, etc.). Ensure that nobody on your team is afraid or embarrassed to ask questions, regularly encourage your team to ask anything they can think of, and make sure your team is composed of people who are ok with that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Always be open for feedback and change
&lt;/h2&gt;

&lt;p&gt;I like to have a GitHub repo in my organization for everything related to how I do management, how 1:1 meetings are run, what I strive to do as a manager, what teammates can expect from me, what I expect from them, and so on. I put it in a GitHub repo because engineers are generally used to working with GitHub and are used to changing things with pull requests, so I wanted to be like that. Make a pull request to change how your manager does something 😛&lt;/p&gt;

&lt;h2&gt;
  
  
  Get your team to be able to function without you
&lt;/h2&gt;

&lt;p&gt;This isn’t to say that you’re supposed to be useless or make yourself obsolete but that you should not be a blocker for your team. Imagine yourself going on a vacation for a month, what would go wrong? What tasks are you a blocker for? Will the person leading in your stead know what to do? Have you left them the necessary information? Does the team know what to work on? Do they know what to do or who to talk to if some disaster happens? Try going on short vacations every once in a while and see what goes wrong. At the beginning you’ll most likely find that a lot of the things you handled as a “Maker” aren’t being done anymore and you need to both make the team aware of them and delegate them.&lt;/p&gt;

&lt;h1&gt;
  
  
  Great resources
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Books
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://leadingsnowflakes.com/"&gt;Leading Snowflakes&lt;/a&gt; — Full of practical advice on day-to-day things you need to do as a manger.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.amazon.com/dp/B06XP3GJ7F/ref=dp-kindle-redirect?_encoding=UTF8&amp;amp;btkr=1"&gt;The Manager’s Path&lt;/a&gt; — A guide for tech managers going through increasing levels of management complexity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.amazon.com/dp/B015VACHOK/ref=dp-kindle-redirect?_encoding=UTF8&amp;amp;btkr=1"&gt;High Output Management&lt;/a&gt; — Great book on running and scaling a company.&lt;/p&gt;

&lt;h2&gt;
  
  
  Websites
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://managerreadme.com/"&gt;Manager Readme&lt;/a&gt; — A community of sharing your “manager guides” with other managers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://thewatercooler.io/"&gt;The Watercooler&lt;/a&gt; — A community for leaders looking to become better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Articles
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://rework.withgoogle.com/blog/the-evolution-of-project-oxygen/"&gt;Google's 10 year research on what makes a good manager&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.usejournal.com/rethinking-how-we-interview-in-microsofts-developer-division-8f404cfd075a"&gt;Rethinking how we interview in Microsoft’s Developer Division&lt;/a&gt; — Great article on hiring processes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.pragmaticengineer.com/on-writing-well/"&gt;Undervalued Software Engineering Skills: Writing Well&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Newsletters
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://softwareleadweekly.com/"&gt;Software Lead Weekly&lt;/a&gt; — A weekly email for busy people who care about people, culture and leadership.&lt;/p&gt;

&lt;p&gt;I’m not much of a video/audio person so I don’t have great resources for those mediums, if you have recommendations for more resources, share them below!&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>engineering</category>
      <category>management</category>
    </item>
    <item>
      <title>I just moved back to development from being Head of Engineering, Ask Me Anything!</title>
      <dc:creator>Omar Bahareth</dc:creator>
      <pubDate>Tue, 09 Apr 2019 11:23:34 +0000</pubDate>
      <link>https://dev.to/obahareth/i-just-moved-back-to-development-from-being-head-of-engineering-ask-me-anything-2a10</link>
      <guid>https://dev.to/obahareth/i-just-moved-back-to-development-from-being-head-of-engineering-ask-me-anything-2a10</guid>
      <description>

</description>
      <category>ama</category>
    </item>
    <item>
      <title>My Mac Setup</title>
      <dc:creator>Omar Bahareth</dc:creator>
      <pubDate>Sun, 30 Dec 2018 11:10:48 +0000</pubDate>
      <link>https://dev.to/obahareth/my-mac-setup-1jon</link>
      <guid>https://dev.to/obahareth/my-mac-setup-1jon</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6b0anwi514ofyujhd64.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6b0anwi514ofyujhd64.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post is my setup by the end of 2018, I keep &lt;a href="https://github.com/obahareth/my-mac-os" rel="noopener noreferrer"&gt;my-mac-os repository on GitHub&lt;/a&gt; up to date with my latest setup.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardware
&lt;/h2&gt;

&lt;p&gt;Computer: 15" Late 2016 Macbook Pro with Touch bar.&lt;/p&gt;

&lt;p&gt;Trackpad: &lt;a href="https://www.amazon.com/Apple-Magic-Trackpad-2-MJ2R2LL/dp/B016QO5YWC/ref=sr_1_3?ie=UTF8&amp;amp;qid=1538413611&amp;amp;sr=8-3&amp;amp;keywords=apple+magic+trackpad" rel="noopener noreferrer"&gt;Apple Magic Trackpad 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Monitor: &lt;a href="https://www.amazon.com/BenQ-GW2765HT-27-Inch-2560x1440-Monitor/dp/B00KYCSRSG/ref=sr_1_5?ie=UTF8&amp;amp;qid=1538413375&amp;amp;sr=8-5&amp;amp;keywords=benq+27+monitor" rel="noopener noreferrer"&gt;BenQ GW2765HT 27-Inch&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Keyboards:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://ergodox-ez.com/products/ergodox-ez-shine?variant=40172754947" rel="noopener noreferrer"&gt;ErgoDox EZ Shine&lt;/a&gt; for work and programming.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/Microsoft-Universal-Foldable-Keyboard-Android/dp/B00UBGU4PY" rel="noopener noreferrer"&gt;Microsoft Universal Foldable Keyboard&lt;/a&gt; for typing on the go.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/Filco-Majestouch-2-Keyboard-FKBN104M-EFB2/dp/B004Z0XR2O/ref=sr_1_1?s=electronics&amp;amp;ie=UTF8&amp;amp;qid=1538413535&amp;amp;sr=1-1&amp;amp;keywords=filco+majestouch+ninja+2" rel="noopener noreferrer"&gt;Filco Majestouch Ninja 2&lt;/a&gt; for gaming.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Applications
&lt;/h2&gt;

&lt;p&gt;I'm slowly transitioning to cross-platform software because I want to be able to use the majority of my apps on any OS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Productivity
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://www.alfredapp.com" rel="noopener noreferrer"&gt;Alfred&lt;/a&gt; - Launcher
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Alfred is both my launcher, clipboard history manager, and entry point to lots of &lt;a href="https://wiki.omar.engineer/alfred/workflows" rel="noopener noreferrer"&gt;awesome workflows&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZOKAVEo0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/zzF5Bkk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZOKAVEo0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/zzF5Bkk.png" alt="img" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It has a great &lt;a href="http://www.alfredforum.com/" rel="noopener noreferrer"&gt;community&lt;/a&gt; and &lt;a href="https://github.com/learn-anything/alfred-workflows#readme" rel="noopener noreferrer"&gt;amazing workflows&lt;/a&gt; that you can use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a small example of me using it to quickly run/evaluate Ruby code:&lt;/p&gt;

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

&lt;h4&gt;
  
  
  &lt;a href="https://todoist.com/" rel="noopener noreferrer"&gt;Todoist&lt;/a&gt; - Simple and quick task manager
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Todoist helps me plan out everything I want to do either for the short term or long term (I'm not sure if I follow GTD).&lt;/li&gt;
&lt;li&gt;It also has global quick add with a hotkey. Together with lists, priorities, powerful search and a lot more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t9R-Qhmb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/QicIQmD.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t9R-Qhmb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/QicIQmD.png" alt="img" width="800" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://trello.com" rel="noopener noreferrer"&gt;Trello&lt;/a&gt; - Project management tool
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Simple project management tool with tons of great integrations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://pipefy.com" rel="noopener noreferrer"&gt;Pipefy&lt;/a&gt; - Automation on boards and forms
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;When I need something like Trello but with a lot more automation/power (e.g. having a public form, sending emails when things move to a certain column, triggering other actions, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://1password.com" rel="noopener noreferrer"&gt;1Password&lt;/a&gt; - Password manager
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Generate all of my passwords with it and keep everything in a secured and encrypted vault kept secure by a master password and two factor authentication.&lt;/li&gt;
&lt;li&gt;No longer need to remember passwords and I now have a unique password for every website that I am signed up on whilst &lt;a href="https://support.1password.com/one-time-passwords/" rel="noopener noreferrer"&gt;activating two factor authentication&lt;/a&gt; wherever possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://mindnode.com" rel="noopener noreferrer"&gt;MindNode&lt;/a&gt; - Interactive Mind Mapping
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;I write A LOT, too much actually, MindNode helps me write short summaries of my thoughts that I "connect the dots" (or lines) between.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://pdfexpert.com/" rel="noopener noreferrer"&gt;PDF Expert&lt;/a&gt; - PDF reader/editor
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Super fast PDF viewer.&lt;/li&gt;
&lt;li&gt;Best experience for editing PDFs or filling PDF forms.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://contexts.co" rel="noopener noreferrer"&gt;Contexts&lt;/a&gt; - Window switcher
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Allows me to fuzzy search through all the currently active windows that I have.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Makes jumping to the right window I need effortless. I often may have many VS Code instances with different projects running and this lets me switch to the project I need in seconds.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://www.macbartender.com/" rel="noopener noreferrer"&gt;Bartender&lt;/a&gt; - Menu bar organizer
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Allows you to customize and hide the contents of your menu bar and improve the aesthetics of your OS. Here is how mine looks:
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oXHliT47--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/GKjbmZJ.png" alt="img" width="800" height="27"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="http://magnet.crowdcafe.com/" rel="noopener noreferrer"&gt;Magnet&lt;/a&gt; - Organize your workspace
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Lets me use keyboard shortcuts (or more rarely, drag-drop to edges) to tile windows on my screen.&lt;/li&gt;
&lt;li&gt;Keyboard shortcuts to move windows across monitors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://eternalstorms.at/yoink/mac/" rel="noopener noreferrer"&gt;Yoink&lt;/a&gt; - Simplify and Improve Drag and Drop on your Mac
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;A small/quick utility I use to drop files from multiple locations/apps into to take an action on them later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://www.obdev.at/products/littlesnitch/index.html" rel="noopener noreferrer"&gt;Little Snitch&lt;/a&gt; - Control incoming/outgoing network traffic
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Amazing networking tool that gives you a clear picture of what connections are incoming to your computer and what are outgoing.&lt;/li&gt;
&lt;li&gt;Takes a bit of time to set it up correctly and is quite an insightful experience first turning it on and having it notify every couple of seconds that some app is trying to send data to some server and whether you want to allow that.&lt;/li&gt;
&lt;li&gt;This is essential if you want to take control of what information gets sent out from your computer and what connections have right to connect to your data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Code
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://github.com/Microsoft/vscode" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt; - Code editor
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;My favorite editor that I use to write code in. I use &lt;a href="https://wiki.omar.engineer/vscode/extensions" rel="noopener noreferrer"&gt;many extensions&lt;/a&gt; for it.&lt;/li&gt;
&lt;li&gt;I use the &lt;a href="https://marketplace.visualstudio.com/items?itemName=arcticicestudio.nord-visual-studio-code" rel="noopener noreferrer"&gt;Nord&lt;/a&gt; theme, &lt;a href="https://marketplace.visualstudio.com/items?itemName=PKief.material-icon-theme" rel="noopener noreferrer"&gt;Material Icon Theme&lt;/a&gt; and the &lt;a href="https://github.com/tonsky/FiraCode" rel="noopener noreferrer"&gt;Fira Code&lt;/a&gt; font.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is how it looks:&lt;/p&gt;

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

&lt;h4&gt;
  
  
  &lt;a href="https://neovim.io/" rel="noopener noreferrer"&gt;Neovim&lt;/a&gt; - Powerful text editor
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Opening, editing, modifying, and searching through huge files.&lt;/li&gt;
&lt;li&gt;Go-to commandline text editor.&lt;/li&gt;
&lt;li&gt;I use &lt;a href="https://github.com/junegunn/vim-plug" rel="noopener noreferrer"&gt;vim-plug&lt;/a&gt; to manage plugins.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://hyper.is/" rel="noopener noreferrer"&gt;Hyper&lt;/a&gt; - Terminal Emulator
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;I use Fish as my shell together with &lt;a href="https://github.com/jorgebucaran/fisher" rel="noopener noreferrer"&gt;Fisher&lt;/a&gt; to install &lt;a href="https://wiki.omar.engineer/fish/plugins" rel="noopener noreferrer"&gt;Fish plugins I use&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;You can find my dotfiles &lt;a href="https://github.com/obahareth/dotfiles" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I use the &lt;a href="https://github.com/rafaelrinaldi/pure" rel="noopener noreferrer"&gt;Pure prompt&lt;/a&gt; and the &lt;a href="https://github.com/arcticicestudio/nord-hyper" rel="noopener noreferrer"&gt;Nord theme&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h4&gt;
  
  
  &lt;a href="https://kapeli.com/dash" rel="noopener noreferrer"&gt;Dash&lt;/a&gt; - API Documentation Browser
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Allows you to download any docset that you might want to use, search for any method, class or anything that you need very quickly, comes with the amazing &lt;a href="https://www.alfredapp.com/blog/productivity/dash-quicker-api-documentation-search/" rel="noopener noreferrer"&gt;Alfred workflow&lt;/a&gt; to simplify the process of searching for the right things.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://www.git-tower.com" rel="noopener noreferrer"&gt;GitKraken&lt;/a&gt; - Git client
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Great cross-platform Git client that integrates well with GitHub, BitBucket, GitLab, and supports GitFlow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://www.kaleidoscopeapp.com/" rel="noopener noreferrer"&gt;Kaleidoscope&lt;/a&gt; - Powerful diff tool
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Really great, precise, and beautiful diffing.&lt;/li&gt;
&lt;li&gt;Great at viewing/merging conflicts.&lt;/li&gt;
&lt;li&gt;Can do directory and image diffs as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://github.com/manosim/gitify" rel="noopener noreferrer"&gt;Gitify&lt;/a&gt; - GitHub issue &amp;amp; pull request notifiations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Neat little app that sits in the menu bar and lights up when I have GitHub notifications.&lt;/li&gt;
&lt;li&gt;Can view all notifications and mark them as read.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Social
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://www.codeux.com/textual/" rel="noopener noreferrer"&gt;Textual&lt;/a&gt; - IRC Client
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;The best macOS IRC client I've found.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://github.com/ramboxapp/community-edition" rel="noopener noreferrer"&gt;Rambox&lt;/a&gt; - Open Source Multi-messaging App
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;A single app I use for chatting with people on:

&lt;ul&gt;
&lt;li&gt;Telegram.&lt;/li&gt;
&lt;li&gt;WhatsApp.&lt;/li&gt;
&lt;li&gt;Messenger.&lt;/li&gt;
&lt;li&gt;Discord.&lt;/li&gt;
&lt;li&gt;Slack (sometimes).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://sparkmailapp.com" rel="noopener noreferrer"&gt;Spark&lt;/a&gt; - Email client
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Love how it smartly categorizes emails by categories.&lt;/li&gt;
&lt;li&gt;I approach all of my email tasks in GTD style. Keeping my email Inbox close to 0 at all times.&lt;/li&gt;
&lt;li&gt;I love being able to easily take batch actions on emails.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Writing
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;a href="http://dayoneapp.com/" rel="noopener noreferrer"&gt;Day One&lt;/a&gt; - Digital journal
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Day One is my digital life journal.&lt;/li&gt;
&lt;li&gt;If you don't journal, I suggest you to start, it is a very powerful mind cleanser and acts as a wonderful history record of your life.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://bywordapp.com/" rel="noopener noreferrer"&gt;Byword&lt;/a&gt; - Distraction-free Markdown Editor
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;A great minimal distraction-free markdown editor, I use it when I want absolute focus.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="http://happenapps.com/" rel="noopener noreferrer"&gt;Quiver&lt;/a&gt; - The Programmer's Notebook
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Powerful cell-based editor that operates based on "cells" (e.g. markdown cell, code cell, LaTeX cell, etc.) for mixing and matching different kinds of formats.&lt;/li&gt;
&lt;li&gt;Plain JSON data format.&lt;/li&gt;
&lt;li&gt;Cloud syncing.&lt;/li&gt;
&lt;li&gt;Charts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://evernote.com" rel="noopener noreferrer"&gt;Evernote&lt;/a&gt; - Large writing, scans, web clips, and annotations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;I do my huge writing here.&lt;/li&gt;
&lt;li&gt;If I want to access a document from anywhere, I usually store it here.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Music
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://play.google.com/music" rel="noopener noreferrer"&gt;Google Play Music&lt;/a&gt; - Music Streaming
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;My goto music streaming solution.&lt;/li&gt;
&lt;li&gt;Easiest for me to setup from where I live (Saudi Arabia).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://music.youtube.com/" rel="noopener noreferrer"&gt;YouTube Music&lt;/a&gt; - Music Streaming 2nd solution
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Google Play Music will most likely be merged with this, so it might be best to get used to it.&lt;/li&gt;
&lt;li&gt;I use it on the rare occasion that I don't find something on Google Play Music.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Images
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://photos.google.com/" rel="noopener noreferrer"&gt;Google Photos&lt;/a&gt; - Cloud storage for photos
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Easiest way to backup my photos and access them from multiple places.&lt;/li&gt;
&lt;li&gt;Machine learning, auto detects images.&lt;/li&gt;
&lt;li&gt;😖 Kind of scary.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Utilities
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://xscopeapp.com/" rel="noopener noreferrer"&gt;xScope&lt;/a&gt; - Measure. Inspect. Test.
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;A powerful set of tools for Mac OS X that are ideal for measuring, aligning and inspecting on-screen graphics and layouts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://bjango.com/mac/istatmenus/" rel="noopener noreferrer"&gt;iStat Menus&lt;/a&gt; - Mac system monitoring from menu bar
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Great system monitoring tools which I use to quickly see my CPU/network/memory usage.&lt;/li&gt;
&lt;li&gt;Has a great calendar popup that shows when you click date/time in the menubar. This is something I was really used to from Windows/Linux that I'm glad to have back again.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://bahoom.com/hyperdock" rel="noopener noreferrer"&gt;HyperDock&lt;/a&gt; - Window Previews for Dock
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Window previews (thumbnails) when hovering over applications on the dock.&lt;/li&gt;
&lt;li&gt;Calendar previews when hovering over the Calendar.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://paw.cloud/" rel="noopener noreferrer"&gt;Paw&lt;/a&gt; - Great API tool
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Full-featured HTTP client that lets you test and describe the APIs you build or consume.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Browsers
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://www.google.com/chrome/" rel="noopener noreferrer"&gt;Google Chrome&lt;/a&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;My main web browser because it easily syncs my extensions across machines.&lt;/li&gt;
&lt;li&gt;I use &lt;a href="https://wiki.omar.engineer/google-chrome/extensions" rel="noopener noreferrer"&gt;these extensions&lt;/a&gt; and &lt;a href="https://wiki.omar.engineer/google-chrome/theme" rel="noopener noreferrer"&gt;this theme&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://www.apple.com/lae/safari/" rel="noopener noreferrer"&gt;Safari&lt;/a&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Incredibly fast/lightweight web browser.&lt;/li&gt;
&lt;li&gt;Since I don't have extensions on it, I use it for when my Chrome extensions break sites.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Command Line Apps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/wting/autojump" rel="noopener noreferrer"&gt;autojump&lt;/a&gt; - A cd command that learns, easily navigate directories from the command line.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/asdf-vm/asdf" rel="noopener noreferrer"&gt;asdf&lt;/a&gt; - One version manager for all my programming languages.

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/asdf-vm/asdf-elixir" rel="noopener noreferrer"&gt;elixir&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kennyp/asdf-golang" rel="noopener noreferrer"&gt;golang&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/skotchpine/asdf-java" rel="noopener noreferrer"&gt;java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/asdf-vm/asdf-nodejs" rel="noopener noreferrer"&gt;nodejs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/smashedtoatoms/asdf-redis" rel="noopener noreferrer"&gt;redis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/asdf-vm/asdf-ruby" rel="noopener noreferrer"&gt;ruby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/code-lever/asdf-rust" rel="noopener noreferrer"&gt;rust&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/junegunn/fzf" rel="noopener noreferrer"&gt;fzf&lt;/a&gt; - Command-line fuzzy finder.&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/athityakumar/colorls" rel="noopener noreferrer"&gt;colorls&lt;/a&gt; - Beautify &lt;code&gt;ls&lt;/code&gt; command with color and font-awesome icons.&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/sharkdp/bat" rel="noopener noreferrer"&gt;bat&lt;/a&gt; - Cat clone with wings.&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/git/git" rel="noopener noreferrer"&gt;git&lt;/a&gt; - Version control.&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://curl.haxx.se/docs/manpage.html" rel="noopener noreferrer"&gt;curl&lt;/a&gt; - Transfer data from or to a server.&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/hishamhm/htop" rel="noopener noreferrer"&gt;htop&lt;/a&gt; - Interactive text-mode process viewer for Unix systems.&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/jakubroztocil/httpie" rel="noopener noreferrer"&gt;httpie&lt;/a&gt; - HTTP client.&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/curl/curl" rel="noopener noreferrer"&gt;curl&lt;/a&gt; - Transfer data, supports various protocols.&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/gleitz/howdoi" rel="noopener noreferrer"&gt;howdoi&lt;/a&gt; - Instant coding answers.&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/asciinema/asciinema" rel="noopener noreferrer"&gt;asciinema&lt;/a&gt; - Terminal session recorder.&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/tldr-pages/tldr" rel="noopener noreferrer"&gt;tldr&lt;/a&gt; - Simplified and community-driven man pages.&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/zeit/now-cli" rel="noopener noreferrer"&gt;now&lt;/a&gt; - Real time global deployments served over HTTP/2.&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/yarnpkg/yarn" rel="noopener noreferrer"&gt;yarn&lt;/a&gt; - Fast, reliable, and secure dependency management.&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/github/hub" rel="noopener noreferrer"&gt;hub&lt;/a&gt; - GitHub wrapper.&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/BurntSushi/xsv" rel="noopener noreferrer"&gt;xsv&lt;/a&gt; - Fast CSV command line toolkit written in Rust.&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>mac</category>
      <category>productivity</category>
      <category>setup</category>
      <category>development</category>
    </item>
    <item>
      <title>Lessons Learned From a Year of Fighting With Webpack and Babel</title>
      <dc:creator>Omar Bahareth</dc:creator>
      <pubDate>Tue, 25 Dec 2018 21:08:08 +0000</pubDate>
      <link>https://dev.to/obahareth/lessons-learned-from-a-year-of-fighting-with-webpack-andbabel-1knc</link>
      <guid>https://dev.to/obahareth/lessons-learned-from-a-year-of-fighting-with-webpack-andbabel-1knc</guid>
      <description>&lt;p&gt;I've been using React for more than a year now and a lot of the time things broke, slowed down our build speeds, and didn't work in the browsers I was transpiling for. I decided to share the lessons I've learned in the hopes they might help others out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be very careful with importing large libraries
&lt;/h2&gt;

&lt;p&gt;Trying to compile large libraries (like react-plotly or PDF libraries) can take your Webpack compile from seconds to 10 minutes+. If a package is slowing down your compile, consider using a CDN version. We simply used script tags, but there are Webpack plugins that can help with that too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/webpack-cdn-plugin"&gt;webpack-cdn-plugin&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/dynamic-cdn-webpack-plugin"&gt;dynamic-cdn-webpack-plugin&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try to find a webpack plugin for your dependencies
&lt;/h2&gt;

&lt;p&gt;Just importing packages like moment.js or lodash brings in a lot of bloat that you probably don't need. Try to import what you need only, or better yet find a webpack plugin that removes the unused things from your bundle, because &lt;a href="https://github.com/react-bootstrap/react-bootstrap/issues/2683"&gt;selective imports don't always work&lt;/a&gt;. As one example, there's &lt;a href="https://github.com/iamakulov/moment-locales-webpack-plugin"&gt;a webpack plugin&lt;/a&gt; that removes a lot of the unnecessary bloat added by Moment.js.&lt;/p&gt;

&lt;p&gt;Google actually has a &lt;a href="https://github.com/GoogleChromeLabs/webpack-libs-optimizations"&gt;nice repository&lt;/a&gt; listing some common problematic dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspect your bundle with Webpack bundle analyzer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gGdoLm3G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/uw90hpp9pwc1xq132nl0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gGdoLm3G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/uw90hpp9pwc1xq132nl0.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/webpack-contrib/webpack-bundle-analyzer"&gt;Webpack Bundle Analyzer&lt;/a&gt; is extremely helpful to see what exactly is going into your bundle. In the screenshot above, you'll notice that moment.js has lots of localization files that your app probably doesn't need. Webpack Bundle Analyzer can help you easily spot these issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add es-check to your CI pipeline early on
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/dollarshaveclub/es-check"&gt;es-check&lt;/a&gt; will help you find out which ES version your bundle is using, it's super useful to find out if you're somehow suddenly not producing ES5 anymore. Even if you're using Babel and browserslist, you might be importing a node module that's not even meant to be used in browsers, or even a package that's not being distributed as ES5. Add es-check to your continuous integration pipeline early on and it should help you find out if your bundle ever stops working with ES5, and that'll help you find which package is the culprit so you can then transpile it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transpiling a node_module
&lt;/h2&gt;

&lt;p&gt;We had imported a very simple package called &lt;a href="https://github.com/sindresorhus/hex-rgb"&gt;hex-rgb&lt;/a&gt; that's not even meant for browsers and this tiny package made our bundle not ES5-compatible anymore. Such packages should go through Babel and be transpiled.&lt;/p&gt;

&lt;p&gt;In your webpack config, your babel loader's exclude field probably looks like this: &lt;code&gt;/node_modules/&lt;/code&gt; . We need to make a regex that excludes node_modules except the specific ones that should be transpiled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Exclude all node modules except hex-rgb and another-package&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;node_modules&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="o"&gt;/&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="nx"&gt;hex&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;rgb&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nx"&gt;another&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kr"&gt;package&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And once again, this might not be a good solution for large packages as it can drastically slow your build time and you might want to switch to a CDN version instead.&lt;/p&gt;

&lt;p&gt;Follow &lt;a href="https://github.com/babel/babel-loader/issues/171"&gt;this issue&lt;/a&gt; from the babel-loader repo to stay up to date on how to handle cases like this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt; &lt;em&gt;(01 Feb 2018)&lt;/em&gt;&lt;br&gt;
I published a package called &lt;a href="https://github.com/obahareth/are-you-es5"&gt;are-you-es5&lt;/a&gt; to help out more with this. It gives you easy to read output, and even builds the regex you need for transpiling those specific modules, check it out!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9B4Rj-tv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2g93tl5ilquhwni8fmtw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9B4Rj-tv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2g93tl5ilquhwni8fmtw.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Use Browserslist to specify your target browsers
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/browserslist/browserslist"&gt;Browserslist&lt;/a&gt; lets you specify which browsers to transpile for.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; 1%
ie &amp;gt;= 8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This simple configuration targets browsers with usage more than 1% global usage, and IE versions 8 and above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use babel.config.js over .babelrc (for Babel ≥ 7.0)
&lt;/h2&gt;

&lt;p&gt;Favor using &lt;code&gt;babel.config.js&lt;/code&gt; to configure Babel over &lt;code&gt;.babelrc&lt;/code&gt;. If you want to transpile node_modules (which is now becoming a very common case with webapps), then you should use &lt;code&gt;babel.config.js&lt;/code&gt;.&lt;br&gt;
&lt;code&gt;.babelrc&lt;/code&gt; can be overridden by another &lt;code&gt;.babelrc&lt;/code&gt; belonging to a node_module that you're transpiling and that can lead to all sorts of weird issues.&lt;/p&gt;
&lt;h2&gt;
  
  
  Make your webpack-dev-server logging output friendlier
&lt;/h2&gt;

&lt;p&gt;Change your &lt;a href="https://webpack.js.org/configuration/dev-server/"&gt;webpack-dev-server config&lt;/a&gt; to this&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;devServer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;noInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;minimal&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;ol&gt;
&lt;li&gt;Add &lt;a href="https://github.com/nuxt/webpackbar"&gt;WebpackBar&lt;/a&gt; to get much less-verbose, friendlier, and more concise output.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gdbWeyPq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/4kjmpngvc7gtkbb6kfk0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gdbWeyPq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/4kjmpngvc7gtkbb6kfk0.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note: The first configuration is meant to be combined with Webpack Bundle Analyzer, as it suppresses console output for things related to your bundle that Webpack Bundle Analyzer already shows. If you're not using Webpack Bundle Analyzer, don't apply the first step.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I hope to see
&lt;/h2&gt;

&lt;p&gt;We need a lot of improvements in our tooling around this process. Most webpack and Babel configs exclude the &lt;code&gt;node_modules&lt;/code&gt; folder (with good reason, compilation would take an insanely long time if they didn't!), but I personally feel this is an area where we need smarter tools. It would be quite convenient if everything "just worked" only by configuring Webpack, and Babel along with browserslist without having to dig through dependencies and the often unique problems they introduce to our build pipeline ourselves. The package.json file could be a good starting point towards this goal. There are a couple of things that if used could help tools determine if a package should be tranpiled or not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://docs.npmjs.com/files/package.json#browser"&gt;browser field&lt;/a&gt;, this should be used instead of the main field if your package targets browsers. Meaning that we should at least get a warning if we specify that we target browsers in our package.json and we import a package that doesn't have the browser field in its package.json.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://docs.npmjs.com/files/package.json#engines"&gt;engines field&lt;/a&gt;, This is usually used to specify the node versions your package targets, but I honestly think it should also be used to specify ES versions, since a lot of NPM's usage is targeting browsers nowadays.
If a package doesn't have any of these fields and we specified that we target browsers and ES5 then we should:

&lt;ul&gt;
&lt;li&gt;Get a warning when we import it.&lt;/li&gt;
&lt;li&gt;Have Webpack plugins that detect these packages and un-exclude them so they get transpiled automatically.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing Notes
&lt;/h2&gt;

&lt;p&gt;Webpack and Babel are awesome, this article isn't trying to say otherwise. We wouldn't be able to use a lot of things if it weren't for them, but the experience of using them together needs to get better. I faced many of these issues over the course of many months and understanding them/finding solutions was incredibly difficult (error messages don't exactly tell you what went wrong and searching for them doesn't always give relevant results), and I hope this article can act as the guide I had hoped to find back then.&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webpack</category>
      <category>babel</category>
    </item>
    <item>
      <title>A Quick Dive Into GraphQL, Part 1: Building a GraphQL API</title>
      <dc:creator>Omar Bahareth</dc:creator>
      <pubDate>Fri, 16 Nov 2018 12:27:52 +0000</pubDate>
      <link>https://dev.to/obahareth/a-quick-dive-into-graphql-part-1-building-a-graphql-api-2cnp</link>
      <guid>https://dev.to/obahareth/a-quick-dive-into-graphql-part-1-building-a-graphql-api-2cnp</guid>
      <description>&lt;h2&gt;
  
  
  What is &lt;a href="https://graphql.org/" rel="noopener noreferrer"&gt;GraphQL&lt;/a&gt;?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“A query language for your API.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Think of it as being able to send queries to an API endpoint to fetch exactly what you want, rather than the traditional REST way of having many endpoints per resource. The way that it personally clicked for me is thinking of it as&lt;/p&gt;

&lt;p&gt;Take a look at this example from the GraphQL website to get an idea of how it works.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6udeov2333yh7dkyem3c.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6udeov2333yh7dkyem3c.gif"&gt;&lt;/a&gt;&lt;em&gt;request (GraphQL Query) is at the top, response is at the bottom&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As an API provider, GraphQL lets you do this by letting you define a schema and how to fetch the resources in it.&lt;/p&gt;

&lt;p&gt;As an API consumer, all you have to do is build the query to fetch exactly what you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is using it?
&lt;/h2&gt;

&lt;p&gt;GraphQL just got &lt;a href="https://gql.foundation/" rel="noopener noreferrer"&gt;its own foundation&lt;/a&gt;, and you can see a full list showing &lt;a href="https://graphql.org/users/" rel="noopener noreferrer"&gt;who’s using it&lt;/a&gt; from their website. Below are some notable articles from companies who’ve used GraphQL where they explain why they used it and their experiences with it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/paypal-engineering/graphql-a-success-story-for-paypal-checkout-3482f724fb53" rel="noopener noreferrer"&gt;GraphQL: A success story for PayPal Checkout&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://githubengineering.com/the-github-graphql-api/" rel="noopener noreferrer"&gt;The GitHub GraphQL API&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use cases
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“Ask for what you need, get exactly that.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes you just want to get a person’s name, other times you want to get the person along with his name and date of birth.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Get many resources in a single request.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Other times you might want to get the person, their home town, along with their friends and their home towns too. With other solutions, you’d usually either have to make different routes for each case, or write a way to request specific parameters yourself.&lt;/p&gt;

&lt;p&gt;I see these two features as extremely useful for the frontend web development (particularly with frameworks like React, Vue, Angular, etc.) and mobile apps.&lt;/p&gt;

&lt;p&gt;Another more advanced feature that I’ll cover in a later post is stitching, which lets you combine multiple schemas (from different APIs) into one; meaning you can fetch resources from different APIs with a single request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consuming a GraphQL API
&lt;/h2&gt;

&lt;p&gt;There’s a GraphQL “IDE” that you can use to explore GraphQL APIs called GraphiQL. They have a &lt;a href="https://graphql.github.io/swapi-graphql/?query=%7B%0A%20%20%23%20The%20first%202%20films%2C%20after%20a%20given%20cursor%20value%0A%20%20allFilms(first%3A%202%2C%20after%3A%20%22YXJyYXljb25uZWN0aW9uOjA%3D%22)%20%7B%0A%20%20%20%20%0A%20%20%20%20%23%20An%20edge%20is%20an%20entry%20in%20a%20%22list%22%0A%20%20%20%20edges%20%7B%0A%20%20%20%20%20%20%0A%20%20%20%20%20%20%23%20Get%20a%20cursor%20value%20for%20each%20%22edge%22%0A%20%20%20%20%20%20%23%20Used%20for%20pagination%2C%20in%20combination%20with%20filters%20like%0A%20%20%20%20%20%20%23%20before%20and%20after.%0A%20%20%20%20%20%20cursor%0A%0A%20%20%20%20%20%20%23%20Each%20edge%20has%20a%20node%2C%20where%20the%20node%20usually%20has%0A%20%20%20%20%20%20%23%20the%20data%20we%27re%20interested%20in%0A%20%20%20%20%20%20node%20%7B%0A%20%20%20%20%20%20%20%20%23%20Film%20title%2C%20releae%20date%2C%20and%20director%0A%20%20%20%20%20%20%20%20title%0A%20%20%20%20%20%20%20%20releaseDate%0A%20%20%20%20%20%20%20%20director%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%23%20The%20first%202%20species%20that%20appeared%20in%20this%20film%0A%20%20%20%20%20%20%20%20speciesConnection(first%3A%202)%20%7B%0A%20%20%20%20%20%20%20%20%20%20edges%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20node%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20The%20name%20of%20the%20species%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20name%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%20%20%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D" rel="noopener noreferrer"&gt;demo playground here&lt;/a&gt; with Star Wars films info that I used to construct the next example. The real cool thing about GraphiQL is that you can just keep hitting CTRL + Space like you would in any IDE, and you’ll get intelligent autocomplete. If you’re currently within a block related to films, you’ll see film fields that you can query for. This really helps explore and try out GraphQL APIs quickly and easily. Here’s an example query that shows some of the power of GraphQL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;# The first 2 films, after a given cursor value&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;allFilms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;after&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YXJyYXljb25uZWN0aW9uOjA="&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="c"&gt;# An edge is an entry in a "list"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;edges&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;

      &lt;/span&gt;&lt;span class="c"&gt;# Get a cursor value for each "edge"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="c"&gt;# Used for pagination, in combination with filters like&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="c"&gt;# before and after.&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="w"&gt;

      &lt;/span&gt;&lt;span class="c"&gt;# Each edge has a node, where the node has&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="c"&gt;# the data we're interested in&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="c"&gt;# Film title, release date, and director&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;releaseDate&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;director&lt;/span&gt;&lt;span class="w"&gt;

        &lt;/span&gt;&lt;span class="c"&gt;# The first 2 species that appeared in this film&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;speciesConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;edges&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="c"&gt;# The name of the species&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query gets the first 2 films (after a specific film’s cursor value) with their title, release date, and director. It also gets the names of the first 2 species that appeared in this film. We just fetched multiple resources with a single request.&lt;/p&gt;

&lt;p&gt;Here’s what the result looks like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allFilms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"edges"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"cursor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YXJyYXljb25uZWN0aW9uOjE="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The Empire Strikes Back"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"releaseDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1980-05-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"director"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Irvin Kershner"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"speciesConnection"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"edges"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Human"&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Droid"&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"cursor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YXJyYXljb25uZWN0aW9uOjI="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Return of the Jedi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"releaseDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1983-05-25"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"director"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Richard Marquand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"speciesConnection"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"edges"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Human"&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Droid"&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Services and Tools
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.gatsbyjs.org/" rel="noopener noreferrer"&gt;Gatsby&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.gatsbyjs.org/" rel="noopener noreferrer"&gt;Gatsby&lt;/a&gt; is a new framework that’s rapidly rising in popularity (and very well documented), it’s static PWA (&lt;a href="https://developers.google.com/web/progressive-web-apps/" rel="noopener noreferrer"&gt;Progressive WebApp&lt;/a&gt;) generator that lets you build websites using &lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt;, and fetch data using GraphQL. Even though you’re deploying “static” files, you can build e-commerce solutions and CMS and just do a whole lot of stuff that you might not think is possible with a static website.&lt;/p&gt;

&lt;p&gt;Gatsby abstracts things away via GraphQL. You want to &lt;a href="https://www.gatsbyjs.org/packages/gatsby-source-filesystem/?=filesystem" rel="noopener noreferrer"&gt;fetch a file from your disk&lt;/a&gt;? You want to &lt;a href="https://www.gatsbyjs.org/packages/gatsby-source-trello/?=trello" rel="noopener noreferrer"&gt;fetch cards, boards, or lists from Trello&lt;/a&gt;? You can do all of that just by sending GraphQL queries; usually through existing plugins that have already built the GraphQL API for you. It even comes with the GraphiQL IDE mentioned above and you can try fetching live data instantly, before you write even a single line of code.&lt;/p&gt;

&lt;p&gt;I’ve personally had a very rapid and rich development experience with Gatsby; especially when interacting with the community, but I did struggle with one thing: localization. Localization can mean different things depending on who you ask. It could be just translating strings, different routes per locale, or a whole lot more. In my case I wanted to translate strings and style content differently (right to left, for Arabic) and while I always found a “Gatsby way” of doing things, this was on area where I had to fiddle around a lot but I did end up having something that worked well after some trial and error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://hasura.io/" rel="noopener noreferrer"&gt;Hasura&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hasura.io" rel="noopener noreferrer"&gt;Hasura&lt;/a&gt; instantly and “automagically” builds a GraphQL API out of your PostgreSQL database (you can quickly choose what is exposed through the API and who has access to it); which was of course a very logical development, since both SQL and GraphQL use schemas. Hasura even goes beyondjust making a GraphQl API for you, it provides subscriptions to database changes, live queries, triggers when database changes happen, and a whole lot more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.apollographql.com/" rel="noopener noreferrer"&gt;Apollo&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Apollo is probably one of the friendliest ways to deal with or adopt GraphQL. In the past, they used to offer a client (React, JS, iOS, and Android), engine (for tracing and error tracking), and a server that helped you incrementally adopt GraphQL (even tools to help translate your REST API to GraphQL). Their offerings have great DX and they have &lt;a href="https://github.com/apollographql/apollo-client-devtools" rel="noopener noreferrer"&gt;dev tools&lt;/a&gt; and plugins for most editors, and all ofthat is open source. It really feels like they’re covering the GraphQL experience from A to Z.&lt;/p&gt;

&lt;p&gt;They recently &lt;a href="https://blog.apollographql.com/introducing-the-apollo-graphql-platform-8ef34bb269e5" rel="noopener noreferrer"&gt;launched the Apollo Platform&lt;/a&gt; which combines their open source tools with cloud services&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I use it?
&lt;/h2&gt;

&lt;p&gt;I haven’t used GraphQL extensively yet, I’ve only used it in one of my projects, which is &lt;a href="https://awesome-mena-conferences-and-meetups.netlify.com/" rel="noopener noreferrer"&gt;a website&lt;/a&gt; (work in progress) for listing tech conferences and meetups in the MENA region, and it’s built using Gatsby.&lt;/p&gt;

&lt;p&gt;I also like to explore GraphQL APIs I come across using &lt;a href="https://insomnia.rest" rel="noopener noreferrer"&gt;Insomnia&lt;/a&gt;, because it has similar features to GraphiQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where do I get started?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Official &lt;a href="https://graphql.org/learn/" rel="noopener noreferrer"&gt;Introduction to GraphQL&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://graphql.org/code/#graphql-clients" rel="noopener noreferrer"&gt;Libraries for clients&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/chentsulin/awesome-graphql" rel="noopener noreferrer"&gt;Awesome GraphQL&lt;/a&gt; - This is a great list that should help you find anything you’re looking for.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing Notes
&lt;/h2&gt;

&lt;p&gt;The comparison between GraphQL and REST is always made so I’ll briefly add my opinion here. I see them both as different tools in your toolbox. As always choose the right tools for your needs and use cases, and most importantly choose something you and your team are familiar and comfortable with, and areable to stay productive in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stay tuned for the upcoming Part 2, “Building a GraphQL API”.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>rest</category>
      <category>webdev</category>
      <category>backend</category>
    </item>
    <item>
      <title>Indexing PDF For Searching Using Tika, Nokogiri, and Algolia</title>
      <dc:creator>Omar Bahareth</dc:creator>
      <pubDate>Sun, 02 Sep 2018 17:40:37 +0000</pubDate>
      <link>https://dev.to/obahareth/indexing-pdf-for-searching-using-tika-nokogiri-andalgolia-phc</link>
      <guid>https://dev.to/obahareth/indexing-pdf-for-searching-using-tika-nokogiri-andalgolia-phc</guid>
      <description>&lt;p&gt;&lt;em&gt;Mirror of this &lt;a href="https://stories.algolia.com/indexing-pdf-or-other-file-contents-for-searching-b2499c23568f" rel="noopener noreferrer"&gt;Medium post&lt;/a&gt;.&lt;/em&gt;  &lt;/p&gt;

&lt;h3&gt;
  
  
  Figuring out where to start from
&lt;/h3&gt;

&lt;p&gt;This is the first guide I’ve written so bear with me, and please provide feedback!  &lt;/p&gt;

&lt;p&gt;I was working on a project that required me to have some really powerful search capabilities that work for multiple languages, and especially searching through file contents (I initially started with PDF). I needed something working and I didn’t have a lot of time. Usually a quick Google search would turn up a tutorial or guide on how to tackle a task like this, but this time I didn’t have any luck, so I decided I’d share my experience after I had something working.  &lt;/p&gt;

&lt;p&gt;I went ahead and looked at &lt;a href="https://stackshare.io/" rel="noopener noreferrer"&gt;StackShare&lt;/a&gt; and found a category for &lt;a href="https://stackshare.io/search-as-a-service" rel="noopener noreferrer"&gt;Search as a Service&lt;/a&gt;, I had always heard amazing things about Elastic Search and I checked out most of the other tools available there and contacted their support teams asking for some guidance. I was surprised to get a response only two minutes later (literally) from Adam Surak, director of infrastructure at &lt;a href="https://algolia.com" rel="noopener noreferrer"&gt;Algolia&lt;/a&gt; (a search service used by Medium surprisingly, and even Twitch) and he helped me out quite a bit. He directed me towards &lt;a href="http://tika.apache.org" rel="noopener noreferrer"&gt;Apache Tika&lt;/a&gt; which as their page states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Apache Tika™ toolkit detects and extracts metadata and text from over a thousand different file types (such as PPT, XLS, and PDF). All of these file types can be parsed through a single interface, making Tika useful for search engine indexing, content analysis, translation, and much more.&lt;br&gt;
Adam also recommended that I split the PDF into paragraphs because it assures that the searched text isn’t too long and that the relevancy is high.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Extracting PDF Contents
&lt;/h3&gt;

&lt;p&gt;I was able to quickly and easily acquire Tika via &lt;a href="http://brew.sh" rel="noopener noreferrer"&gt;HomeBrew&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;


&lt;p&gt;I’m going to use a PDF version of Dracula (and then I cut out everything but the first 3 pages) that I acquired for free from PDFPlanet and I ran:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tika &lt;span class="nt"&gt;-r&lt;/span&gt; dracula-shortened.pdf &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; dracula.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;-r&lt;/code&gt; option is for pretty printing, and Tika outputs HTML for easy parsing (I used &lt;code&gt;&amp;gt;&lt;/code&gt; to pass the HTML output into a file called &lt;code&gt;dracula.html&lt;/code&gt;). Let’s look at the metadata that we got out of that:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"date"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"2017-01-05T18:08:24Z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"pdf:PDFVersion"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"1.7"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"xmp:CreatorTool"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Acrobat PDFMaker 5.0 for Word"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"Keywords"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"free, PDF, ebook, ebooks, Planet PDF, download, classic, classics"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"access_permission:modify_annotations"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"access_permission:can_print_degraded"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"subject"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Free Planet PDF eBooks -- an assortment of some of the most popular classics. Free!"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"dc:creator"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Bram Stoker"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"dcterms:created"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"2002-09-20T06:10:21Z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"Last-Modified"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"2017-01-05T18:08:24Z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"dcterms:modified"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"2017-01-05T18:08:24Z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"dc:format"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"application/pdf; version=1.7"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"Last-Save-Date"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"2017-01-05T18:08:24Z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"access_permission:fill_in_form"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"meta:save-date"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"2017-01-05T18:08:24Z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"pdf:encrypted"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"dc:title"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Dracula"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"modified"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"2017-01-05T18:08:24Z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"cp:subject"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Free Planet PDF eBooks -- an assortment of some of the most popular classics. Free!"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"Content-Length"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"48741"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"application/pdf"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"X-Parsed-By"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"org.apache.tika.parser.DefaultParser"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"X-Parsed-By"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"org.apache.tika.parser.pdf.PDFParser"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"creator"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Bram Stoker"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"meta:author"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Bram Stoker"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"dc:subject"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"free, PDF, ebook, ebooks, Planet PDF, download, classic, classics"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"meta:creation-date"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"2002-09-20T06:10:21Z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"created"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Fri Sep 20 09:10:21 AST 2002"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"access_permission:extract_for_accessibility"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"access_permission:assemble_document"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"xmpTPg:NPages"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"Creation-Date"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"2002-09-20T06:10:21Z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"resourceName"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Dracula_NT-shortened.pdf"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"access_permission:extract_content"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"access_permission:can_print"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"meta:keyword"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"free, PDF, ebook, ebooks, Planet PDF, download, classic, classics"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"Author"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Bram Stoker"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"producer"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Acrobat Distiller 5.0.5 (Windows)"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"access_permission:can_modify"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Dracula&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;There’s a lot of useful data there, and you can use Tika to get metadata, detect content language, and so many other powerful options, but in our case we are more interested in the body, here’s a snippet of what the body looks like (2nd page of the PDF):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"page"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;p/&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Dracula
   &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;2 of 684
   &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Chapter 1
   &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Jonathan Harker’s Journal
      3 May. Bistritz.—Left Munich at 8:35 P.M., on 1st
   &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;May, arriving at Vienna early next morning; should have
      arrived at 6:46, but train was an hour late. Buda-Pesth
      seems a wonderful place, from the glimpse which I got of
      it from the train and the little I could walk through the
      streets. I feared to go very far from the station, as we had
      arrived late and would start as near the correct time as
      possible.
   &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;The impression I had was that we were leaving the
      West and entering the East; the most western of splendid
      bridges over the Danube, which is here of noble width
      and depth, took us among the traditions of Turkish rule.
   &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;We left in pretty good time, and came after nightfall to
      Klausenburgh. Here I stopped for the night at the Hotel
      Royale. I had for dinner, or rather supper, a chicken done
      up some way with red pepper, which was very good but
      thirsty. (Mem. get recipe for Mina.) I asked the waiter,
      and he said it was called ‘paprika hendl,’ and that, as it was
      a national dish, I should be able to get it anywhere along
      the Carpathians. &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;p/&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"annotation"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://www.planetpdf.com/mainpage.asp?eb"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And here is the 2nd page in the PDF for comparison:  &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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmy0siuy1uhf2wwlsmn0i.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmy0siuy1uhf2wwlsmn0i.png" alt="dracula-2nd-page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the output we can see that each page is a &lt;code&gt;div&lt;/code&gt; having &lt;code&gt;page&lt;/code&gt; as its class, and then we have each paragraph as the inner text of a paragraph element &lt;code&gt;p&lt;/code&gt;  &lt;/p&gt;

&lt;p&gt;This is very nicely formatted really easy to parse.&lt;/p&gt;


&lt;h3&gt;
  
  
  Using Tika in Ruby
&lt;/h3&gt;

&lt;p&gt;For me, the next step was to use Tika in Ruby (because my stack relies on Ruby on Rails). Some quick searching lead me to Yomu and using it in Ruby became as easy as:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"yomu"&lt;/span&gt;
&lt;span class="n"&gt;yomu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Yomu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s2"&gt;"dracula-shortened.pdf"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;yomu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Parsing HTML Using Nokogiri
&lt;/h3&gt;

&lt;p&gt;I have used Nokogiri in the past to parse HTML and it’s pretty easy in this case.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"nokogiri"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"yomu"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;invalid_paragraph?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;disallowed_strings&lt;/span&gt; &lt;span class="o"&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="s2"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;disallowed_strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_pdf_paragraphs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;yomu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Yomu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;paragraphs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Nokogiri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yomu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

  &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.page'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'p'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;paragraph&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;paragraph_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;paragraph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inner_text&lt;/span&gt;

      &lt;span class="k"&gt;next&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;invalid_paragraph?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paragraph_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;paragraphs&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;text: &lt;/span&gt;&lt;span class="n"&gt;paragraph_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;page: &lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;paragraphs&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;paragraphs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_pdf_paragraphs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"dracula-shortened.pdf"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;I use a CSS selector to loop through anything with &lt;code&gt;page&lt;/code&gt; as its class, and then I loop through all the paragraph elements &lt;code&gt;p&lt;/code&gt; within that page, and I’m just incrementing the &lt;code&gt;page&lt;/code&gt; variable (I do this because knowing which page the text was found on is important for my use case). I had a bunch of empty string paragraphs, paragraphs there were just a single space character or a newline character, and a space followed by a new line, so I added a simple function &lt;code&gt;invalid_paragraph?&lt;/code&gt; to easily skip those pieces of text (I could’ve written a regular expression but it didn’t seem worth it in this case, especially since I wanted an easily readable tutorial).  &lt;/p&gt;

&lt;p&gt;Let’s take a look at &lt;code&gt;paragraphs&lt;/code&gt; (I cut out a good chunk of the PDFs before pasting this output so I don’t have a really gigantic example):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="ss"&gt;:text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Dracula &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Bram Stoker &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;:text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;This eBook is designed and published by Planet PDF. For more free &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;eBooks visit our Web site at http://www.planetpdf.com/."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;:text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Dracula &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;:text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"2 of 684 &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;:text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Chapter 1 &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;:text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Jonathan Harker’s Journal &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;3 May. Bistritz.—Left Munich at 8:35 P.M., on 1st &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;:text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"May, arriving at Vienna early next morning; should have &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;arrived at 6:46, but train was an hour late. Buda-Pesth &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;seems a wonderful place, from the glimpse which I got of &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;it from the train and the little I could walk through the &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;streets. I feared to go very far from the station, as we had &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;arrived late and would start as near the correct time as &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;possible. &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;:text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"The impression I had was that we were leaving the &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;West and entering the East; the most western of splendid &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;bridges over the Danube, which is here of noble width &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;and depth, took us among the traditions of Turkish rule. &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;:text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"We left in pretty good time, and came after nightfall to &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Klausenburgh. Here I stopped for the night at the Hotel &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Royale. I had for dinner, or rather supper, a chicken done &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;up some way with red pepper, which was very good but &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;thirsty. (Mem. get recipe for Mina.) I asked the waiter, &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;and he said it was called ‘paprika hendl,’ and that, as it was &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;a national dish, I should be able to get it anywhere along &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;the Carpathians. "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&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 there you have it. Thanks to Tika, we easily split the PDF into paragraphs, and thanks to Nokogiri we have parsed it with extreme ease.&lt;/p&gt;


&lt;h3&gt;
  
  
  Getting the PDF Contents Into Algolia
&lt;/h3&gt;

&lt;p&gt;So I decided to use Algolia after playing around with it, because it was extremely easy to use, configure, and it returned search results extremely fast. Getting my PDF content searchable was as easy as doing this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Algolia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;application_id: &lt;/span&gt;&lt;span class="s1"&gt;'xxxx'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;api_key: &lt;/span&gt;&lt;span class="s1"&gt;'xxxx'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Algolia&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"books"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_objects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paragraphs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_settings&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="s2"&gt;"searchableAttributes"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Closing Notes
&lt;/h3&gt;

&lt;p&gt;Now obviously you could add more to that example (maybe get the filename and PDF author/title in as a record too, or add book IDs linked to your system or whatever) but that’s a nice and simple example to get started from. I’d like to thank all the people who worked on any of the tools I’ve used because they’re awesome and incredible time savers.  &lt;/p&gt;

&lt;p&gt;Here’s the full code of the example as a GitHub Gist:  &lt;/p&gt;


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



</description>
      <category>ruby</category>
      <category>algolia</category>
      <category>search</category>
      <category>pdf</category>
    </item>
    <item>
      <title>The Case of The Jumping Dot</title>
      <dc:creator>Omar Bahareth</dc:creator>
      <pubDate>Tue, 19 Dec 2017 00:00:00 +0000</pubDate>
      <link>https://dev.to/obahareth/the-case-of-the-jumping-dot-4cf8</link>
      <guid>https://dev.to/obahareth/the-case-of-the-jumping-dot-4cf8</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jjxKSnNr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://omar.engineer/content/images/2018/03/1_W4dUNX0JP_ZI33EM09iz3Q-1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jjxKSnNr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://omar.engineer/content/images/2018/03/1_W4dUNX0JP_ZI33EM09iz3Q-1.gif" alt="The Case of The Jumping Dot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you enter the dot/full stop character when typing in English, it just works, and you don’t need to think about it dancing around your words and sentences. This isn’t the case in Arabic. Since the full stop character is technically considered a &lt;a href="https://en.wikipedia.org/wiki/Bi-directional_text"&gt;BiDi (bidirectional) character&lt;/a&gt;, weird stuff starts to happen. There’s no clear standard on how to render it correctly(as far as I know).&lt;/p&gt;

&lt;p&gt;Take a look at this GIF for example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PAtxHgTJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://omar.engineer/content/images/2018/03/1_W4dUNX0JP_ZI33EM09iz3Q.gif%23left" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PAtxHgTJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://omar.engineer/content/images/2018/03/1_W4dUNX0JP_ZI33EM09iz3Q.gif%23left" alt="The Case of The Jumping Dot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The dot suddenly went to the beginning of the line (on the right side instead of the left), and the blinking cursor isn’t even moving. Now this doesn’t just happen with that character, it happens with any BiDi character (e.g. exclamation mark) in the middle of RTL (Right To Left) text.&lt;/p&gt;

&lt;p&gt;There’s actually a solution to this for some characters, there’s &lt;a href="https://www.fileformat.info/info/unicode/char/06d4/index.htm"&gt;an Arabic full stop character&lt;/a&gt;, the only issue is that most people have no idea it exists or how to type it. I usually google it and copy it off of websites that show you Unicode characters, and it works pretty well as you can see in this example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7XbUr101--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://omar.engineer/content/images/2018/03/1_RaDySb6voidlLSLRuDQQlw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7XbUr101--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://omar.engineer/content/images/2018/03/1_RaDySb6voidlLSLRuDQQlw.gif" alt="The Case of The Jumping Dot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The way it’s rendered is a bit weird, and some fonts render it at a very strange vertical position, but it doesn’t jump around text and drive me crazy.&lt;/p&gt;

&lt;p&gt;Another solution is to “set the text direction” but most users don’t know how to do that, it doesn’t work well when you mix LTR (Left To Right) and RTL in the same line, and it isn’t even immediately obvious how to do that in a number of platforms/apps. So I propose changing the normal full stop character to the Arabic one when using Arabic input (and the same for all other applicable characters), on all desktop and mobile operating systems. Which by the way, is what already happens when you try to use type a &lt;a href="http://www.fileformat.info/info/unicode/char/61f/index.htm"&gt;question mark with Arabic input&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ve actually tweeted to Apple, Google, and Microsoft, as well as their CEOs with no response (yet).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Could we get the Arabic full stop character to be inputted when we try to input a full stop with Arabic input? The normal one gets rendered all over the place and most people don’t know the Arabic one exists or how to type it.&lt;a href="https://twitter.com/Apple?ref_src=twsrc%5Etfw"&gt;@Apple&lt;/a&gt; &lt;a href="https://twitter.com/Google?ref_src=twsrc%5Etfw"&gt;@Google&lt;/a&gt; &lt;a href="https://twitter.com/Microsoft?ref_src=twsrc%5Etfw"&gt;@Microsoft&lt;/a&gt; &lt;a href="https://t.co/ccpePNJ45G"&gt;https://t.co/ccpePNJ45G&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Omar Bahareth | Blockchain (@o_bahareth) &lt;a href="https://twitter.com/o_bahareth/status/939275224801054721?ref_src=twsrc%5Etfw"&gt;December 8, 2017&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://twitter.com/sundarpichai?ref_src=twsrc%5Etfw"&gt;@sundarpichai&lt;/a&gt; &lt;a href="https://twitter.com/tim_cook?ref_src=twsrc%5Etfw"&gt;@tim_cook&lt;/a&gt; &lt;a href="https://twitter.com/satyanadella?ref_src=twsrc%5Etfw"&gt;@satyanadella&lt;/a&gt; Could you guys get this looked at?&lt;/p&gt;

&lt;p&gt;— Omar Bahareth | Blockchain (@o_bahareth) &lt;a href="https://twitter.com/o_bahareth/status/939275739274326016?ref_src=twsrc%5Etfw"&gt;December 8, 2017&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I would think these kind of UX issues are more important than where the cheese is on the burger emoji.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I think we need to have a discussion about how Google's burger emoji is placing the cheese underneath the burger, while Apple puts it on top &lt;a href="https://t.co/PgXmCkY3Yc"&gt;pic.twitter.com/PgXmCkY3Yc&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Thomas Baekdal (@baekdal) &lt;a href="https://twitter.com/baekdal/status/924312294439444480?ref_src=twsrc%5Etfw"&gt;October 28, 2017&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
&lt;p&gt;Will drop everything else we are doing and address on Monday:) if folks can agree on the correct way to do this! &lt;a href="https://t.co/dXRuZnX1Ag"&gt;https://t.co/dXRuZnX1Ag&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Sundar Pichai (@sundarpichai) &lt;a href="https://twitter.com/sundarpichai/status/924487551372615680?ref_src=twsrc%5Etfw"&gt;October 29, 2017&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are many other difficulties that we face which I might show in other posts but I’ll mention just a few which I regularly face and am extremely irritated by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Selecting Arabic text, particularly when there’s English text within the selection. What’s rendered on-screen as selected isn’t actually what’s selected.&lt;/li&gt;
&lt;li&gt;Tiny font sizes. Most websites I visit have a font size of around 16px (Google search bar for example), this looks fine for English, but many Arabic characters are not legible for some reason. I don’t know if this is the fault of the font or if we need dynamic font sizes per language.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is my hope that this post will bring some of the issues many users face when typing Arabic on computers and phones to light, and even drive developers and font creators to tackle these issues. There are many apps and websites that have solved a lot of these issues, but we need this knowledge to be shared and turned into standards that operating systems and browsers handle, without every app and website having to repeat the work.&lt;/p&gt;

</description>
      <category>ux</category>
      <category>arabic</category>
      <category>unicode</category>
      <category>bidi</category>
    </item>
  </channel>
</rss>
