<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Lorenzo Zarantonello</title>
    <description>The latest articles on Forem by Lorenzo Zarantonello (@lorenzojkrl).</description>
    <link>https://forem.com/lorenzojkrl</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%2F684349%2F25291a22-d42a-45aa-9fbf-f94f05f114bb.jpeg</url>
      <title>Forem: Lorenzo Zarantonello</title>
      <link>https://forem.com/lorenzojkrl</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/lorenzojkrl"/>
    <language>en</language>
    <item>
      <title>Chrome extensions suggestions? Plain JS or template/framework?</title>
      <dc:creator>Lorenzo Zarantonello</dc:creator>
      <pubDate>Thu, 22 May 2025 08:31:06 +0000</pubDate>
      <link>https://forem.com/lorenzojkrl/chrome-extensions-suggestions-plain-js-or-templateframework-3ng6</link>
      <guid>https://forem.com/lorenzojkrl/chrome-extensions-suggestions-plain-js-or-templateframework-3ng6</guid>
      <description></description>
      <category>browser</category>
      <category>extensions</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Lovable vs Bolt — What's Best?</title>
      <dc:creator>Lorenzo Zarantonello</dc:creator>
      <pubDate>Wed, 14 May 2025 08:16:34 +0000</pubDate>
      <link>https://forem.com/playfulprogramming/lovable-vs-bolt-whats-best-2447</link>
      <guid>https://forem.com/playfulprogramming/lovable-vs-bolt-whats-best-2447</guid>
      <description>&lt;p&gt;I decided to compare Lovable and Bolt on the same task. &lt;/p&gt;

&lt;p&gt;For this test, I didn’t require any external connection e.g. no DB, APIs, etc. However, the app I wanted is more complex than a simple landing page. &lt;/p&gt;

&lt;p&gt;Here is my unfiltered point of view.&lt;/p&gt;

&lt;p&gt;As a bonus, we can get 200k more tokens if you &lt;a href="https://bolt.new/?rid=osurfp" rel="noopener noreferrer"&gt;sign up for Bolt with this referral link&lt;/a&gt;! Let's do it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Prompt
&lt;/h2&gt;

&lt;p&gt;A detailed input increases the chances of a good first outcome. This is hands down the first and most important take-away, IMO.&lt;/p&gt;

&lt;p&gt;Here is the initial prompt I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create a word search game with a 5x5 grid where words can be found 
horizontally, vertically and diagonally.

For inspiration, draw from classic word search puzzle designs with a modern, 
clean interface that's both engaging and user-friendly.

For this first version, implement:

- A 5x5 letter grid with randomly generated puzzles
- Words placed horizontally, vertically or diagonally
- Word selection by clicking/tapping and dragging
- A selected letter changes the background color to a darker shade
- A word list to find. By default, 
  the word list is hidden and it can be toggled to be visible
- Visual feedback when a word is found. All the letters 
  of that word change their background color to a green shade
- Responsive design that works on mobile and desktop
- Game completion detection and celebration

For the color palette, I'm thinking:
Primary: #8B5CF6 (purple)
Secondary: #F97316 (orange)
Accent: #0EA5E9 (blue)
Background: #F6F6F7 (light gray)
Text: #1A1F2C (dark)
Highlight: #D6BCFA (light purple)
While Lovale is a pretty straightforward tool, Bolt immediately offers more customization, like the following. Also, it offers the possibility to use AI to enhance the prompt.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;For the sake of a good comparison, I simply used the prompt above with both Lovable and Bolt (Bolt offers more customizations).&lt;/p&gt;

&lt;p&gt;While Lovable failed to create a working prototype, Bolt crushed it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lovable — I could partially play with the app. Clicking a letter would change its background color, but clicking again would not revert it to the original color (To be fair, I didn’t ask for it). Once all words were selected, nothing would happen.&lt;/li&gt;
&lt;li&gt;Bolt was simply working. I would need to drag the cursor to select the letters, and it would not work by clicking. So that is something that could be improved. However, the app works fine with one prompt!
UI &amp;amp; Design
See for yourself below.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  UI, dragging issues, and smooth implementation
&lt;/h3&gt;

&lt;p&gt;Lovable seems to follow the prompt quite well, and the UI is good for a first try.&lt;/p&gt;

&lt;p&gt;This was expected, as in a previous experiment with Lovable, the design turned out to be pretty good on the first try.&lt;/p&gt;

&lt;p&gt;However, it has a strange behaviour that, I guess, tries to emulate a dragging selection. When I click on a letter and move the cursor over other letters, it selects those as well.&lt;/p&gt;

&lt;p&gt;The dragging selection is not good.&lt;/p&gt;

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

&lt;p&gt;On the other side, Bolt shows a good UI.&lt;/p&gt;

&lt;p&gt;Even though it doesn’t quite follow some instructions, like the Background: #F6F6F7 (light gray), the UI looks pretty good and better organized than Lovable.&lt;/p&gt;

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

&lt;p&gt;So, at this point, Lovable doesn’t quite work, or at least it is not a ready prototype, while Bolt is almost done!&lt;/p&gt;

&lt;h3&gt;
  
  
  Chatting with AI
&lt;/h3&gt;

&lt;p&gt;It’s time to use the chat to fix the issues.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lovable chat was helpless. That’s it, not much to add because I quickly ran out of my five daily credits (more on pricing below).
It seems the chat pretends to fix the issue, while nothing changes.&lt;/li&gt;
&lt;li&gt;Bolt chat was helpless. I ran out of credits before I could get the app to select words by clicking one letter at a time.
While Bolt chat couldn’t help much, the starting point seems promising! Words are placed horizontally, vertically, and diagonally as requested in the prompt.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The dragging selection is smooth and doesn’t create issues like I experienced with Replit.&lt;/p&gt;

&lt;p&gt;Very good result for a quick MVP!&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;Both platforms offer a free plan.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lovable plans
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;5 free credits a day. Lovable offers 30 free credits a month and a maximum of 5 per day. One credit is one prompt.&lt;/li&gt;
&lt;li&gt;$25/month for 130 credits. The next plan is $25/month, with only monthly billing. That gives you 130 credits e.g. 100 credits, on top of the 30 credits you get with the free plan. So, that’s about 130 prompts/messages per month.&lt;/li&gt;
&lt;li&gt;Lovable doesn’t seem to offer a yearly plan.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bolt
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;150k free tokens. Bolt offers 150k free tokens, and you can get 200k more tokens if you sign up with this referral link. (If you sign up with the referral link, I will also get 200k tokens at no cost to you)
Having said that, the way tokens are consumed is hard to understand and compare with Lovable (More on usability below)&lt;/li&gt;
&lt;li&gt;$20/month with 10M tokens. Roughly, I consumed 150k tokens with the initial prompts and three follow-up prompts. Which means I should get about 250/300 prompts/month. However, this is a guess, at the moment.
Does a similar plan (430 credits) cost $100/month in Lovable?&lt;/li&gt;
&lt;li&gt;Bolt offers an $18/month yearly plan.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these things are my interpretation, and you should check for yourself before paying.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lovable vs Bolt
&lt;/h2&gt;

&lt;p&gt;It is also worth comparing the two from a usability point of view.&lt;/p&gt;

&lt;p&gt;Lovable seems much more intuitive, and it shows a greater attention to detail. For example:&lt;/p&gt;

&lt;h3&gt;
  
  
  Lovable
&lt;/h3&gt;

&lt;p&gt;Lovable has a “Dev Mode” that lets you toggle between the visual preview of your app to an IDE-like view. They even got bullied by Figma for using the term Dev Mode. Figma tells AI startup to stop using the term ‘Dev Mode’&lt;br&gt;
Very straightforward pricing plan. It’s easy to understand what you get. However, why not include a yearly plan?&lt;br&gt;
Lovable requires a GitHub repo to interact with code through an AI assistant. This might create some friction for non-developers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bolt
&lt;/h3&gt;

&lt;p&gt;It is fairly easy to toggle between the Preview and the Code view&lt;br&gt;
The pricing plan is a bit of a mystery. It is hard to understand what a token is worth. Conversely, it is reasonable to assume that different requests consume more or less “power” e.g. API call?&lt;br&gt;
Conclusion&lt;br&gt;
Style-wise, Lovable and Bolt produce good-looking applications.&lt;/p&gt;

&lt;p&gt;If you get your app working on the first prompt, Lovable is your tool! However, I found the chat quite unreliable for fixing issues or adding functionalities.&lt;/p&gt;

&lt;p&gt;The same can be said about Bolt! The chat didn’t quite improve the app, and consumed the tokens quite fast.&lt;/p&gt;

&lt;p&gt;However, I am happy with Bolt’s improvements since the last time I tried it out. Good work there!&lt;/p&gt;

&lt;p&gt;Also, surprisingly, Bolt is the only one offering a referral mechanism!&lt;br&gt;
If you enjoyed this article, let's show it by &lt;a href="https://bolt.new/?rid=osurfp" rel="noopener noreferrer"&gt;joining Bolt with this referral link&lt;/a&gt; to get us both 200k tokens!&lt;/p&gt;

</description>
      <category>vibecoding</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Strawberry AI Browser Will Blow Your Mind</title>
      <dc:creator>Lorenzo Zarantonello</dc:creator>
      <pubDate>Thu, 24 Apr 2025 12:54:36 +0000</pubDate>
      <link>https://forem.com/playfulprogramming/strawberry-ai-browser-will-blow-your-mind-17pk</link>
      <guid>https://forem.com/playfulprogramming/strawberry-ai-browser-will-blow-your-mind-17pk</guid>
      <description>&lt;h2&gt;
  
  
  What Is Strawberry?
&lt;/h2&gt;

&lt;p&gt;According to their website, “&lt;a href="https://strawberrybrowser.com/" rel="noopener noreferrer"&gt;Strawberry&lt;/a&gt; is a browser with built-in AI assistants that work alongside you on any website.”&lt;/p&gt;

&lt;p&gt;I am telling you, this is an understatement!&lt;/p&gt;

&lt;p&gt;This is not another regular product jumping on the AI bandwagon by plugging in a chatbot using OpenAI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://strawberrybrowser.com/" rel="noopener noreferrer"&gt;Strawberry is a browser&lt;/a&gt; like Chrome and Edge, but it comes with something called Companions.&lt;/p&gt;

&lt;p&gt;Companions are AI agents that can do tasks online autonomously while you do other things!&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s See A Real Example — Strawberry, Get Me Venues Data
&lt;/h2&gt;

&lt;p&gt;Here is a real case example.&lt;/p&gt;

&lt;p&gt;I had to get some social data (Email, Instagram, TikTok) for some restaurants I found on Google Maps.&lt;/p&gt;

&lt;p&gt;Now, at scale, you could use the Google Maps API but they don’t give you Instagram and TikTok data.&lt;/p&gt;

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

&lt;p&gt;Thanks to Strawberry, I could do it quickly and effectively!&lt;/p&gt;

&lt;p&gt;No need to mess with APIs, no need to find a way to match restaurant names and their social handles on some table.&lt;/p&gt;

&lt;h3&gt;
  
  
  The prompt
&lt;/h3&gt;

&lt;p&gt;Here is my prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;For each of these restaurants, find me an email, 
phone number, Instagram handle and TikTok profile if available, 
Return the data in a table format with the following columns: 
Restaurant name, email, phone, Instagram, TikTok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, I was using the companion named Lora, which is available on the left panel.&lt;/p&gt;

&lt;p&gt;Lora is the standard companion and it works crazy well, although I didn’t set up any&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;instructions,&lt;/li&gt;
&lt;li&gt;memories, or&lt;/li&gt;
&lt;li&gt;skills&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of these as context and what ChatGPT calls memory!&lt;/p&gt;

&lt;h3&gt;
  
  
  The research
&lt;/h3&gt;

&lt;p&gt;Look at the tabs!&lt;/p&gt;

&lt;p&gt;Strawberry autonomously opened those tabs to search for the info I asked.&lt;/p&gt;

&lt;p&gt;When I took the screenshot, it just started with Google Maps and then went on with more tabs to research social media info for each of the restaurants.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fphncj33jkwvuywkqyetr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fphncj33jkwvuywkqyetr.png" alt="The Strawberry AI Browser opens tabs as needed" width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On top of that, there is a natural dialogue with the companion on the left panel.&lt;/p&gt;

&lt;p&gt;I find it useful for unexpected situations, like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Based on the research results, 
I can now present the information in a table format. 
But first, 
let me run one more search batch to confirm Ave Mario's phone number 
which wasn't found in the first batch.
Now, I know there might be a problem with a venue called Ave Mario.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While Strawberry finishes the work, I can work in another tab as usual, which is pretty cool.&lt;/p&gt;

&lt;h3&gt;
  
  
  The result
&lt;/h3&gt;

&lt;p&gt;See for yourself!&lt;/p&gt;

&lt;p&gt;It created the table as I wanted, and it explained the exceptions and so on!&lt;/p&gt;

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

&lt;p&gt;This took maybe a minute or two.&lt;/p&gt;

&lt;p&gt;The entire process would take me maybe 30 minutes, so let’s say I saved 90% of the time it would normally take.&lt;/p&gt;

&lt;h3&gt;
  
  
  More Use Cases?
&lt;/h3&gt;

&lt;p&gt;Well, the potential is really limitless!&lt;/p&gt;

&lt;p&gt;These are a few things I came up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open my Instagram page and get the description of the first three posts in my feed. If there is a … more text, click on more to read the entire text&lt;/li&gt;
&lt;li&gt;Open [pretty much any social] and write the following post…&lt;/li&gt;
&lt;li&gt;Given this Reddit post, read the question, and for each of the first three replies, come up with an appropriate follow-up question or sentence — You can see where this is going for marketing purposes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Should You Care About Strawberry?
&lt;/h2&gt;

&lt;p&gt;Well, Strawberry will change the way people work and carry out tasks.&lt;/p&gt;

&lt;p&gt;Until now, developers and programmers could create software to do repetitive tasks.&lt;/p&gt;

&lt;p&gt;Now everyone can do it!&lt;/p&gt;

&lt;p&gt;A few things stood out:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;This is going to be huge for workflow automation — Any browser-based task — data entry, email drafting, research, etc — can be automated, saving hours.&lt;/li&gt;
&lt;li&gt;Contextual Learning — Strawberry’s AI Companions get smarter over time, learning my preferences and way of working.&lt;/li&gt;
&lt;li&gt;Approval-Based Actions — This is really good if you need to work with socials and automate some tasks on the go. Strawberry always asks for approval before executing actions.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;The AI Browser revolution is here, and Strawberry is leading the charge.&lt;/p&gt;

&lt;p&gt;By integrating intelligent assistants directly into a browser, Strawberry automates tasks and workflows, saving you time.&lt;/p&gt;

&lt;p&gt;In a recent update, it is even possible to update videos of how to carry out a task and the Companion will follow the video as instructions!&lt;/p&gt;

&lt;p&gt;This is pretty revolutionary and I will write more about it as I explore Strawberry.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>browser</category>
      <category>productivity</category>
      <category>automation</category>
    </item>
    <item>
      <title>Three Ways to Fight AI Crawlers</title>
      <dc:creator>Lorenzo Zarantonello</dc:creator>
      <pubDate>Thu, 03 Apr 2025 14:49:20 +0000</pubDate>
      <link>https://forem.com/playfulprogramming/three-ways-to-fight-ai-crawlers-1c2f</link>
      <guid>https://forem.com/playfulprogramming/three-ways-to-fight-ai-crawlers-1c2f</guid>
      <description>&lt;p&gt;Until recently, websites were pushing for web crawlers to index their content properly.&lt;/p&gt;

&lt;p&gt;Now, a new type of crawler, AI crawlers, is changing the game, with negative repercussions on open source content and increasingly on companies that rely on content.&lt;/p&gt;

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

&lt;p&gt;Here is &lt;a href="https://pub.towardsai.net/three-ways-to-fight-ai-crawlers-a8bcede654b7?sk=e19940bf8b1fbb4deb5433818d717828" rel="noopener noreferrer"&gt;how to fight back&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AI Crawlers Are Bad For The Internet As We Know It
&lt;/h2&gt;

&lt;p&gt;In short:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spikes in costs for the website owners&lt;/li&gt;
&lt;li&gt;Outages or performance issues for the users&lt;/li&gt;
&lt;li&gt;DDoS outages — In the worst cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eventually, you get to the case of the founder of &lt;a href="https://techpays.com/" rel="noopener noreferrer"&gt;TechPays.com&lt;/a&gt;, where he noticed an over 10x increase in data outbound and over 90% of the traffic was AI crawlers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Is This A Problem?
&lt;/h3&gt;

&lt;p&gt;Because the content is scrapped for free and will then be sold to you through OpenAI, Meta AI, and whatever.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Ways to Fight AI Crawlers
&lt;/h2&gt;

&lt;p&gt;So here are three ways to fight AI crawlers with their pros and cons.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using JavaScript&lt;/li&gt;
&lt;li&gt;Deploying AI Tarpits and Labyrinths&lt;/li&gt;
&lt;li&gt;Rate Limiting and Advanced Filtering&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Using JavaScript
&lt;/h3&gt;

&lt;p&gt;It seems AI crawlers struggle with JavaScript-heavy websites!&lt;/p&gt;

&lt;p&gt;AI crawlers like GPTBot (OpenAI), Claude (Anthropic), and PerplexityBot have difficulty processing, or don’t process at all, JavaScript-rendered content.&lt;/p&gt;

&lt;p&gt;While they fetch JavaScript files, they don’t execute the code, which results in useless content from the scraper's point of view.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fight AI Crawlers Deploying AI Tarpits and Labyrinths
&lt;/h2&gt;

&lt;p&gt;Tarpits are tools designed to trap AI crawlers in an endless maze of content, wasting their computational resources and time while protecting your actual content.&lt;/p&gt;

&lt;p&gt;These tools create dynamic networks of interconnected pages that lead nowhere, effectively preventing crawlers from accessing legitimate content.&lt;/p&gt;

&lt;h3&gt;
  
  
  Popular Tarpit Solutions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://zadzmo.org/code/nepenthes/" rel="noopener noreferrer"&gt;Nepenthes&lt;/a&gt; — Creates an “infinite maze” of static files with no exit links, effectively trapping AI crawlers and wasting their resources. This is brutal, and if you are searching for vengeance, this is the right tool for you!&lt;/li&gt;
&lt;li&gt;Cloudflare’s AI Labyrinth: Uses AI-generated content to slow down, confuse, and waste the resources of crawlers that don’t respect “no crawl” directives.
See how to use &lt;a href="https://blog.cloudflare.com/ai-labyrinth/#how-to-use-ai-labyrinth-to-stop-ai-crawlers" rel="noopener noreferrer"&gt;AI Labyrinth to stop AI crawlers&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://lib.rs/crates/iocaine" rel="noopener noreferrer"&gt;Iocaine&lt;/a&gt;: Uses a reverse proxy to trap crawlers in an “infinite maze of garbage” with the intent to poison their data collection. Iocaine is also based on Nepenthes but “Iocaine is purely about generating garbage.”&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  Fight AI Crawlers Using Rate Limiting and Advanced Filtering
&lt;/h2&gt;

&lt;p&gt;Setting up geographical filtering with challenges (like CAPTCHAs or JavaScript tests) for visitors from countries outside your target market can significantly reduce unwanted crawler traffic.&lt;/p&gt;

&lt;p&gt;A few examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The sysadmin of the Linux Fedora project had to block the entire country of Brazil to combat aggressive AI scrapers!&lt;/li&gt;
&lt;li&gt;The founder of TechPays.com also tried this before moving to stronger measures like turning on Cloudflare’s AI crawler block&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Find &lt;a href="https://pub.towardsai.net/three-ways-to-fight-ai-crawlers-a8bcede654b7?sk=e19940bf8b1fbb4deb5433818d717828" rel="noopener noreferrer"&gt;more techniques to deal with AI crawlers&lt;/a&gt;, but this is a pretty good starting point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Considerations
&lt;/h2&gt;

&lt;p&gt;Most likely, a good approach consists of a combination of several techniques like IP blocking and Cloudflare’s AI crawler blocking.&lt;/p&gt;

&lt;p&gt;Furthermore, tarpit technologies and advanced rate limiting seem to be more robust against aggressive crawlers.&lt;/p&gt;

&lt;p&gt;Obviously, you don’t want to block completely all AI crawlers because it could potentially hide your content from human visitors who rely on AI-powered searches to find your site.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
    </item>
    <item>
      <title>What Is React Compiler?</title>
      <dc:creator>Lorenzo Zarantonello</dc:creator>
      <pubDate>Wed, 23 Oct 2024 13:16:23 +0000</pubDate>
      <link>https://forem.com/playfulprogramming/what-is-react-compiler-kp4</link>
      <guid>https://forem.com/playfulprogramming/what-is-react-compiler-kp4</guid>
      <description>&lt;p&gt;React compiler is a new experimental compiler that promises to revolutionize the way React applications are optimized for performance.&lt;/p&gt;

&lt;p&gt;You can read and listen to this article in a podcast format at this &lt;a href="https://medium.com/javascript-in-plain-english/what-is-react-compiler-5855fd6a8630?sk=66717c6e410dc2fef68474047529925a" rel="noopener noreferrer"&gt;free Medium link&lt;/a&gt;.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  What Is A Compiler?
&lt;/h2&gt;

&lt;p&gt;In short, a compiler is a program that translates one programming language (the source language) into another (the target language).&lt;/p&gt;

&lt;p&gt;A compiler translate a source language into a target language&lt;/p&gt;

&lt;p&gt;For example, JSX and TSX are compiled into JavaScript (with Babel or others) for the browser to understand the code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why A New React Compiler?
&lt;/h3&gt;

&lt;p&gt;One of the main reasons I have heard is Performance Optimization.&lt;/p&gt;

&lt;p&gt;Sometimes, React applications suffer from unnecessary re-renders, leading to performance issues.&lt;br&gt;
Developers can optimize the code using memorization, callbacks, and the like. But often this translates into a cluttered component filled with memoization calls.&lt;/p&gt;
&lt;h3&gt;
  
  
  What is the React Compiler solution?
&lt;/h3&gt;

&lt;p&gt;Briefly speaking, React Compiler should automatically optimize your code, reducing the need for manual performance optimization.&lt;/p&gt;

&lt;p&gt;As a consequence, React Compiler should improve app responsiveness.&lt;/p&gt;

&lt;p&gt;Let’s dive into it.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Is React Compiler?
&lt;/h2&gt;

&lt;p&gt;React compiler is an experimental compiler already available in React 18. It is, and most likely will be, an optional tool even in React 19.&lt;/p&gt;

&lt;p&gt;Using React Compiler in React 18 requires some manual configuration but you can already try it.&lt;/p&gt;

&lt;p&gt;The React Compiler is designed to work within the ecosystem of React 19&lt;/p&gt;
&lt;h3&gt;
  
  
  Install React Compiler
&lt;/h3&gt;

&lt;p&gt;While React Compiler is used in production at Instagram, it is open-sourced as beta and available on React 17+.&lt;/p&gt;

&lt;p&gt;The compiler comes with a strongly recommended eslint plugin that shows the compiler analysis in the editor.&lt;/p&gt;

&lt;p&gt;You can use the eslint plugin even if you are not using React Compiler.&lt;/p&gt;
&lt;h3&gt;
  
  
  React Compiler Healthcheck
&lt;/h3&gt;

&lt;p&gt;Before installing React Compiler it is recommended to check compatibility.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx react-compiler-healthcheck@beta
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to get a result like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output of npx react-compiler-healthcheck@beta
Output of npx react-compiler-healthcheck@beta
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A higher number of compiled components is a good thing. That is the number of components that can be successfully optimized.&lt;/p&gt;

&lt;p&gt;My StrictMode is not enabled but having this enabled and followed means a higher chance that the Rules of React are followed.&lt;br&gt;
So we should probably keep it enabled.&lt;/p&gt;

&lt;p&gt;Finally, react-compiler-healthcheck checks for known libraries that are incompatible with the compiler. I have none, but MobX will give you problems. It won’t work.&lt;/p&gt;
&lt;h3&gt;
  
  
  Installing React Compiler
&lt;/h3&gt;

&lt;p&gt;Install React Compiler using npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -D babel-plugin-react-compiler@beta eslint-plugin-react-compiler@beta
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, if you’re using Yarn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add -D babel-plugin-react-compiler@beta eslint-plugin-react-compiler@beta
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should also add the eslint plugin to the config. In my case, it is a .eslintrc.js file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
  plugins: [
    'eslint-plugin-react-compiler',
  ],
  rules: {
    'react-compiler/react-compiler': "error",
  },
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once eslint plugin is set up correctly, you might get some warnings about the Rules of React. You can update them on the spot or later.&lt;/p&gt;

&lt;p&gt;The only difference is that in the second case, React skipped optimizing that component or hook.&lt;/p&gt;

&lt;p&gt;You can read and listen to this article in a podcast format at this &lt;a href="https://medium.com/javascript-in-plain-english/what-is-react-compiler-5855fd6a8630?sk=66717c6e410dc2fef68474047529925a" rel="noopener noreferrer"&gt;free Medium link&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>frontend</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Angular Is Your Best Shot To Land A Frontend Job</title>
      <dc:creator>Lorenzo Zarantonello</dc:creator>
      <pubDate>Thu, 17 Oct 2024 07:59:56 +0000</pubDate>
      <link>https://forem.com/playfulprogramming-angular/angular-is-your-best-shot-to-land-a-frontend-job-1eip</link>
      <guid>https://forem.com/playfulprogramming-angular/angular-is-your-best-shot-to-land-a-frontend-job-1eip</guid>
      <description>&lt;p&gt;According to the &lt;a href="https://survey.stackoverflow.co/2024/" rel="noopener noreferrer"&gt;2024 Stack Overflow Developer Survey&lt;/a&gt;, most people who learn to code learn React. &lt;/p&gt;

&lt;h2&gt;
  
  
  Learners Market
&lt;/h2&gt;

&lt;p&gt;Not too bad, many jobs position requires React.&lt;br&gt;
However, look at the following table.&lt;/p&gt;

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

&lt;p&gt;This table tells you how many people are learning a certain framework among the people who are learning to code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React — 36.6%&lt;/li&gt;
&lt;li&gt;Next.js — 17.9%&lt;/li&gt;
&lt;li&gt;Flask — 17.3%&lt;/li&gt;
&lt;li&gt;Django — 14%&lt;/li&gt;
&lt;li&gt;Vue — 11%&lt;/li&gt;
&lt;li&gt;Svelte — 10%&lt;/li&gt;
&lt;li&gt;Angular — 9%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do you see anything strange?&lt;br&gt;
Maybe not. &lt;/p&gt;

&lt;p&gt;Let's look at the job market.&lt;/p&gt;

&lt;h2&gt;
  
  
  Job market
&lt;/h2&gt;

&lt;p&gt;I did a quick search on LinkedIn.&lt;br&gt;
Clearly, it's not a bulletproof statistical analysis but maybe it can give you a hint.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnzapzzwgdd9khpzoktqv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnzapzzwgdd9khpzoktqv.png" alt="Jobs requiring a certain framework in the US, Germany, and India" width="800" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This table shows the number of jobs mentioning a certain framework in the US, Germany, and India (LinkedIn).&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;As I said, this is a very rough exercise.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Job positions requiring Angular are second to React and well ahead of any other frontend framework. &lt;/li&gt;
&lt;li&gt;Only 8% learn Angular, while 30% to 40% of job positions require Angular.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Given this asymmetry, Angular might be your best shot at landing a front-end job.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://levelup.gitconnected.com/angular-is-your-best-shot-to-land-a-frontend-job-in-2024-2025-7252c679d854?sk=1d31c1da1d896ec08d3263da29acba40" rel="noopener noreferrer"&gt;Free link to the full post&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>angular</category>
      <category>career</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Three Examples of How AI Expands Developers' Roles</title>
      <dc:creator>Lorenzo Zarantonello</dc:creator>
      <pubDate>Tue, 13 Aug 2024 11:56:00 +0000</pubDate>
      <link>https://forem.com/lorenzojkrl/three-examples-of-how-ai-expands-developers-roles-45fa</link>
      <guid>https://forem.com/lorenzojkrl/three-examples-of-how-ai-expands-developers-roles-45fa</guid>
      <description>&lt;p&gt;Here are three tools to expand developers' roles and productivity. You can start to use them right now.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Copilot - Generic Developer Assistant&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.atlassian.com/blog/announcements/ai-editing-atlassian-products" rel="noopener noreferrer"&gt;JIRA AI&lt;/a&gt; - Developers Expand Into Product Owners&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.startearly.ai/" rel="noopener noreferrer"&gt;EarlyAI&lt;/a&gt; - Your AI Test Engineer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  GitHub Copilot - Generic Developer Assistant
&lt;/h2&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%2Fbrzq7yxstohj7pg3ig4o.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%2Fbrzq7yxstohj7pg3ig4o.png" alt="GitHub Copilot" width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By now, every developer should know Copilot. So, I won't spend too many words about it.&lt;br&gt;
It's a generic coding assistant that can support your development activities.&lt;br&gt;
It's not perfect but here are a few use cases I like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Quick questions without leaving my IDE&lt;/li&gt;
&lt;li&gt;Create mock data to display something &lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  JIRA AI expands developers' roles into Product Owners
&lt;/h2&gt;

&lt;p&gt;Now, JIRA doesn't really shine among developers.  JIRA is a Frankenstein project management tool developed by Atlassian to "do a lot of things".&lt;/p&gt;

&lt;p&gt;Sometimes, I feel that JIRA gets undeserved criticism because the problems come from the inability to master and use the tool efficiently rather than from its flaws. &lt;/p&gt;

&lt;p&gt;Atlassian, the company behind JIRA, hopped on the GenAI train enabling AI editing across Atlassian products.&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%2Fhzbccvjg1t3xujbpo5sg.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%2Fhzbccvjg1t3xujbpo5sg.png" alt="Atlassian AI" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now this might seem like prompt engineering. But I was positively surprised when with this flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write a good technical prompt describing the problem &lt;/li&gt;
&lt;li&gt;JIRA AI broke down the problem into smaller actionable sub-tasks &lt;/li&gt;
&lt;li&gt;Each sub-task was correctly assigned to a newly created ticket&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In such a workflow, technical knowledge is very relevant, and that knowledge is too often missing in non-technical POs.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;a href="https://www.startearly.ai/" rel="noopener noreferrer"&gt;EarlyAI&lt;/a&gt; by Early provides an AI test engineer to every developer
&lt;/h2&gt;

&lt;p&gt;Having an AI test engineer by your side has never been easier. &lt;br&gt;
If Copilot is a generic coding assistant, EarlyAI is super focused on unit tests and closer to becoming an AI agent for the specific task of high-quality unit test generation. &lt;/p&gt;

&lt;p&gt;EarlyAI creates entire test suites in one click.&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%2Fxk80fqz6b0w7i2dy7y8d.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%2Fxk80fqz6b0w7i2dy7y8d.png" alt="EarlyAI writes unit tests in one click" width="800" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Honestly, I find the phrasing "30 green unit tests in less than a minute" an understatement. At the moment, I am migrating a Next.js project from using Pages Router to App Router. &lt;/p&gt;

&lt;p&gt;Since it was supposed to be "just a side project" I didn't write tests. &lt;/p&gt;

&lt;p&gt;Now, the project has grown quite a bit, and touching code and libraries without proper testing is guaranteed to cause some issues down the line. &lt;br&gt;
In this case, I am using EarlyAI to cover my blind spots while migrating the project.&lt;/p&gt;

&lt;p&gt;Here is an example of the generated test suite EarlyAI created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// addDocument.early.test.ts

import { db } from "../../firebase";
import { collection, doc, setDoc, getFirestore } from "firebase/firestore";
import { addDocument } from "../addData";

// Mocking the Firestore functions
jest.mock("firebase/firestore", () =&amp;gt; ({
  collection: jest.fn(),
  doc: jest.fn(),
  setDoc: jest.fn(),
  getFirestore: jest.fn(),
}));

describe("addDocument() addDocument method", () =&amp;gt; {
  beforeEach(() =&amp;gt; {
    jest.clearAllMocks();
  });

  describe("Happy Path", () =&amp;gt; {
    test("should add a document successfully to the specified collection", async () =&amp;gt; {
      // Arrange
      const collectionName = "events";
      const event = { name: "Test Event" };
      const mockResult = { id: "123" };
      (setDoc as jest.Mock).mockResolvedValue(mockResult);

      // Act
      const { result, error } = await addDocument(collectionName, event);

      // Assert
      expect(collection).toHaveBeenCalledWith(db, collectionName);
      expect(doc).toHaveBeenCalled();
      expect(setDoc).toHaveBeenCalled();
      expect(result).toBe(mockResult);
      expect(error).toBeNull();
    });
  });

  ...
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the successful test run.&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%2Fzwz7r4exkdj9ti2xkuls.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%2Fzwz7r4exkdj9ti2xkuls.png" alt="EarlyAI test suite" width="800" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Was it perfect the first time? No.&lt;br&gt;
Despite that, it probably saved me 1h or so for each test suite and got me to a good position in one click.&lt;br&gt;
I am sure the team will keep improving the product and will naturally add E2E tests with time. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;Given the current trajectory of developers' roles and responsibilities, we could extrapolate that every developer will be empowered and expected to do more. &lt;/p&gt;

&lt;p&gt;Any generic coding assistant, JIRA AI, and EarlyAI can expand individual developers' capabilities to become team leaders with a team of AI agents in different disciplines (Coding, Product, Testing).&lt;/p&gt;

&lt;p&gt;This is also true for indie hackers! The cost of creating software, hosting websites, and using DBs has never been cheaper. On top of that, more and more AI agents can support developers across different tasks. &lt;/p&gt;

&lt;p&gt;Overall, every individual developer will be supported by AI to create more complex and quality software, at unprecedented speed.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Next.js - App Router</title>
      <dc:creator>Lorenzo Zarantonello</dc:creator>
      <pubDate>Tue, 30 Jul 2024 13:40:00 +0000</pubDate>
      <link>https://forem.com/lorenzojkrl/nextjs-app-router-4l7e</link>
      <guid>https://forem.com/lorenzojkrl/nextjs-app-router-4l7e</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/this-is-learning/first-experience-with-nextjs-2a58"&gt;previous posts&lt;/a&gt;, I tried Next.js using the Pages router. In this post and the following, I am exploring the new App router.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating The Application
&lt;/h2&gt;

&lt;p&gt;To create a project with Next.js version 14, you simply need to start a command from the terminal by using&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The "latest" option will download the latest version of the framework. All project dependencies will be installed, and then you will be asked to enter the project's name.&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%2Fgozxdaouhyb2iqs2gnz9.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%2Fgozxdaouhyb2iqs2gnz9.png" alt="Installing Next.js 14" width="719" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I kept the default selection, meaning that we will use&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TypeScript &lt;/li&gt;
&lt;li&gt;Tailwind CSS &lt;/li&gt;
&lt;li&gt;ESLint &lt;/li&gt;
&lt;li&gt;The app Router&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that all project files will be available in the root folder because there won't be a &lt;code&gt;src&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;Once the installation is complete, navigate inside the project folder. In my case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd next14-app-router
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and start the server that will serve your Next.js app on &lt;code&gt;http://localhost:3000/&lt;/code&gt; with the command&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You can see this and other commands in the &lt;code&gt;script&lt;/code&gt; object of the &lt;code&gt;package.json&lt;/code&gt; file in the root folder of your application (in my case, inside the folder named next14-app-router).&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%2F8fl5iiltz5fm8jz4j72n.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%2F8fl5iiltz5fm8jz4j72n.png" alt="package.json in Next.js" width="784" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, the &lt;code&gt;dependencies&lt;/code&gt; object shows I am using version 14.0.1 of &lt;a href="https://www.npmjs.com/package/next" rel="noopener noreferrer"&gt;next&lt;/a&gt; which allows us to install Next.js. &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%2Ft6v4xbvc4jdj9denw3wf.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%2Ft6v4xbvc4jdj9denw3wf.png" alt="Next.js Demo App" width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that the project is running, it is worth it to get an idea of the files and folders that were created by default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Application folders and files
&lt;/h2&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%2Fhrhj0hu5z7umkocanqmn.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%2Fhrhj0hu5z7umkocanqmn.png" alt="Next.js default setup" width="800" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From top to bottom, we have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.next - Stores cache files to speed up the development and build process. It also contains server files to handle server-side-rendering (SSR), and other minified files to serve the app.&lt;/li&gt;
&lt;li&gt;app - Contains components, logic, and routes of your application. We will mostly work inside the app folder.&lt;/li&gt;
&lt;li&gt;public - Contains images you use in your app (e.g. static files)&lt;/li&gt;
&lt;li&gt;.eslintrc.json - Configuration file for lining&lt;/li&gt;
&lt;li&gt;.gitignore - Defines what should not be tracked by git &lt;/li&gt;
&lt;li&gt;next-env.d.ts -  TypeScript declaration to improve type-checking&lt;/li&gt;
&lt;li&gt;next.config.js - Configuration file for Next.js&lt;/li&gt;
&lt;li&gt;package.json &amp;amp; package-lock.json define your project dependencies&lt;/li&gt;
&lt;li&gt;postcss.config.js, tailwind.config.ts, tsconfig.json - Configuration files, respectively for CSS, Tailwind, and TypeScript compiler&lt;/li&gt;
&lt;li&gt;README.md - Can be used to explain your project or include relevant info&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The app folder
&lt;/h3&gt;

&lt;p&gt;Inside the app folder, you can see a favicon file that is used to add an icon to the tab of the browser in which the app is running.&lt;/p&gt;

&lt;h4&gt;
  
  
  global.css
&lt;/h4&gt;

&lt;p&gt;A &lt;code&gt;global.css&lt;/code&gt; file sets CSS rules that will be used throughout the Next.js project. You can add more CSS by using Tailwind where you need it or by using CSS Modules that will allow you to keep the CSS separate from the JavaScript/TypeScript code.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;global.css&lt;/code&gt; file is imported in &lt;code&gt;layout. tsx&lt;/code&gt; which is also called the RootLayout component. &lt;/p&gt;

&lt;h4&gt;
  
  
  layout.tsx
&lt;/h4&gt;

&lt;p&gt;In Next.js, &lt;strong&gt;a layout is a component that represents a part of the UI shared among multiple pages&lt;/strong&gt;. A layout is a good place to manage some global state, global style, or place components that should be displayed on every page (or route) within your Next.js application—for instance a navigation menu. &lt;/p&gt;

&lt;p&gt;In the following example, the text &lt;em&gt;I'll be there for you&lt;/em&gt; will be there for you on every page of your app because it is part of the RootLayout component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// layout.tsx

...
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &amp;lt;html lang="en"&amp;gt;
      &amp;lt;body className={inter.className}&amp;gt;
        &amp;lt;div&amp;gt;I'll be there for you&amp;lt;/div&amp;gt;
        {children} // pages passed as children components
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Layouts receive the individual pages as children components, through the "children" property. The children property is handled automatically when you navigate to a specific page, allowing the page to be injected directly into the layout.&lt;/p&gt;

&lt;p&gt;By default, a layout is applied to all pages in the "pages" directory of your project.&lt;/p&gt;

&lt;p&gt;Think of the Layout component as a wrapper for every other component in your Next.js app.&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%2Ftf14e9cdzm1qv6vuqvvp.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%2Ftf14e9cdzm1qv6vuqvvp.png" alt="Layout visualization" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For completeness, I should mention that &lt;code&gt;layout.tsx&lt;/code&gt; in RootLayout does a few more things.&lt;/p&gt;

&lt;p&gt;It imports the Inter next font and assigns it to the inter variable. Then it is passed as a class to the body element to be used as a default font in the app. &lt;br&gt;
In so doing, Next.js simplifies the way you get and use Google fonts in the app. Find more about &lt;a href="https://nextjs.org/docs/app/building-your-application/optimizing/fonts" rel="noopener noreferrer"&gt;font optimization&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It imports Metadata so you can easily set the metadata of your app. If you change the title of the metadata object you will change the title of the tab in which your app is running.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// layout.tsx

...
export const metadata: Metadata = {
  title: "Next14 App Router",
  description: "Generated by create next app",
};
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see these changes in the developer console under the Elements tab inside the head tag. See the bottom of the following image.&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%2F7jcfqe7g3r9ez00csldh.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%2F7jcfqe7g3r9ez00csldh.png" alt="Elements tab" width="634" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  page.tsx
&lt;/h4&gt;

&lt;p&gt;The file &lt;code&gt;page.tsx&lt;/code&gt; is a default page that contains the Home component.&lt;br&gt;
By default, the initial app will pass the Home component as a child of RootLayout.&lt;/p&gt;

&lt;p&gt;By changing Home to the following, you will have a new starting point for your app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// page.tsx

export default function Home() {
  return &amp;lt;main&amp;gt;Hello Next.js&amp;lt;/main&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see horizontal lines, just go to &lt;code&gt;global.css&lt;/code&gt; and remove the style assigned to the body class so it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// global.css

...
body {
  color: rgb(var(--foreground-rgb));
}Warning: Extra attributes from the server: data-new-gr-c-s-check-loaded,data-gr-ext-installed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, your app should show the sentences "&lt;em&gt;I'll be there for you"&lt;/em&gt; coming from RootLayout and "&lt;em&gt;Hello Next.js&lt;/em&gt;" from Home.&lt;/p&gt;

&lt;h3&gt;
  
  
  Warning
&lt;/h3&gt;

&lt;p&gt;You might notice a warning in your console. If the warning is "Warning: Extra attributes from the server: data-new-gr-c-s-check-loaded,data-gr-ext-installed" or something similar, it means one or more extensions are adding some code to your app while it is executed on the browser.&lt;/p&gt;

&lt;p&gt;It is good practice to disable extensions while developing but you can also set the suppressHydrationWarning attribute to true in the body tag in RootLayout.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;body className={inter.className} suppressHydrationWarning={true}&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read more on &lt;a href="https://stackoverflow.com/a/75339011/6281872" rel="noopener noreferrer"&gt;StackOverflow&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
    </item>
    <item>
      <title>OneEntry Headless CMS: How To Use It?</title>
      <dc:creator>Lorenzo Zarantonello</dc:creator>
      <pubDate>Thu, 27 Jun 2024 08:50:26 +0000</pubDate>
      <link>https://forem.com/lorenzojkrl/oneentry-headless-cms-how-to-use-it-icd</link>
      <guid>https://forem.com/lorenzojkrl/oneentry-headless-cms-how-to-use-it-icd</guid>
      <description>&lt;p&gt;In the CMS context, the term "headless" points to the absence of a front-end or presentation layer. Instead, a headless CMS works with an API (Application Programming Interface), which empowers users to render content on the front end of their choice.&lt;/p&gt;

&lt;p&gt;For example, let's say you want to use OneEntry headless CMS for your international e-commerce store. To get started, you would need to take the following steps&lt;/p&gt;

&lt;h2&gt;
  
  
  1) Set Up OneEntry Headless CMS &amp;amp; Create a New Project
&lt;/h2&gt;

&lt;p&gt;Start by creating a OneEntry account or signing into an existing one. After that, you'll be able to start a new project for your eCommerce store. This will entail selecting a name for it and picking the pricing plan that matches your goals best.&lt;/p&gt;

&lt;h2&gt;
  
  
  2) Add Content
&lt;/h2&gt;

&lt;p&gt;Before starting to work with the API, you will need to add content to your new project via the "Content Management" section. In this case, it would mean inputting such details as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product images&lt;/li&gt;
&lt;li&gt;Item descriptions&lt;/li&gt;
&lt;li&gt;Pricing information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What's more, since the e-commerce we are discussing is supposed to cater to an international audience, you would also need to set up localization for multiple languages and regions.&lt;/p&gt;

&lt;h2&gt;
  
  
  3) Set Up APIs or SDKs
&lt;/h2&gt;

&lt;p&gt;Now, your task is to establish communication between your front end and OneEntry CMS via APIs or the SDK.&lt;br&gt;
Add content manually, through the API or the SDK.&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%2Fs5odcgl20x87vreqlz06.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%2Fs5odcgl20x87vreqlz06.png" alt="Image description" width="800" height="147"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  API 
&lt;/h3&gt;

&lt;p&gt;If you decide to use the API, begin by generating authentication API keys. They will serve as the gateway to secure and authenticate your interactions with the headless CMS. &lt;/p&gt;

&lt;p&gt;You will also need to configure the &lt;a href="https://oneentry.cloud/instructions.html#API"&gt;API endpoints&lt;/a&gt; within OneEntry CMS, which will enable your e-commerce store to seamlessly fetch data.&lt;/p&gt;
&lt;h3&gt;
  
  
  SDK
&lt;/h3&gt;

&lt;p&gt;If you decide to use the SDK, just run the handy command:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;At this point, you simply need to import the defineOneEntry function and start using the methods you need as shown in the &lt;a href="https://oneentry.cloud/instructions.html#NPM"&gt;usage documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4) Build Your E-Commerce Store
&lt;/h2&gt;

&lt;p&gt;The next step is to select a front-end framework for your e-commerce store. This is also the time when you build all the essential e-commerce features like product pages, the shopping cart, and the checkout page.&lt;/p&gt;

&lt;p&gt;After that, you can use the API keys and endpoints to integrate OneEntry CMS data into your eCommerce front end. &lt;br&gt;
This integration allows for dynamic fetching and real-time display of product information.&lt;/p&gt;

&lt;p&gt;Similarly, if you opted for the SDK, you have a similar degree of flexibility by using multiple methods, for instance, getProductsPageById, getProductByIdfilterProduct, and so on.&lt;/p&gt;

&lt;h2&gt;
  
  
  5) Test Your E-Commerce Website &amp;amp; Deploy It
&lt;/h2&gt;

&lt;p&gt;Before your e-commerce site goes live, it is crucial that you thoroughly test it and ensure proper data retrieval from the headless CMS. Once you are sure everything works as it should, you are ready to deploy your store to the hosting platform of your choice.&lt;/p&gt;

&lt;p&gt;Finally, remember that the journey doesn't end post-deployment. Regularly audit content with the help of OneEntry CMS's user-friendly interface to keep product information and other relevant data up-to-date.&lt;/p&gt;

&lt;h2&gt;
  
  
  Main Benefits of OneEntry Headless CMS
&lt;/h2&gt;

&lt;p&gt;If you would like to learn more about the setup process, check out the step-by-step instructions provided on the &lt;a href="https://oneentry.cloud/instructions.html#NPM"&gt;OneEntry website&lt;/a&gt; and &lt;a href="https://www.youtube.com/@headlesscms-oneentry"&gt;YouTube channel&lt;/a&gt;! &lt;/p&gt;

&lt;p&gt;Yet, even without going further into detail, it is clear to see that one of this system's key benefits is its straightforward implementation.&lt;br&gt;
Some other advantages OneEntry boasts compared to other headless CMSs in the market are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User-friendly interface&lt;/li&gt;
&lt;li&gt;Easy-to-scale backend&lt;/li&gt;
&lt;li&gt;Fast API, along with clear documentation and responsive support&lt;/li&gt;
&lt;li&gt;PostgreSQL, MySQL, and MongoDB bases&lt;/li&gt;
&lt;li&gt;Intuitive content management logic&lt;/li&gt;
&lt;li&gt;Virtual storage on state-of-the-art servers&lt;/li&gt;
&lt;li&gt;Simple export of products and services from YML or CSV files&lt;/li&gt;
&lt;li&gt;Daily backups and advanced cybersecurity measures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, OneEntry wants to position itself as a one-stop solution for all your content management needs, regardless of how multifaceted they are.&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%2Fgxcq6om4b3nq1qeli9n1.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%2Fgxcq6om4b3nq1qeli9n1.png" alt="Image description" width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Take
&lt;/h2&gt;

&lt;p&gt;OneEntry Headless CMS offers several features that can help you boost your content management processes. It is versatile and the OneEntry team is adding even more features to make the product more compelling.&lt;br&gt;
After all, its use cases expand way beyond e-commerce platforms. OneEntry headless CMS could be used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Corporate websites&lt;/li&gt;
&lt;li&gt;Blogs and journals&lt;/li&gt;
&lt;li&gt;Mobile apps&lt;/li&gt;
&lt;li&gt;Educational platforms&lt;/li&gt;
&lt;li&gt;Digital exhibitions &amp;amp; interactive installations&lt;/li&gt;
&lt;li&gt;Omnichannel commercial platforms&lt;/li&gt;
&lt;li&gt;Portfolios&lt;/li&gt;
&lt;li&gt;Service offerings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will explore and write more about this solution, but in the meantime feel free to give it a try.&lt;/p&gt;

</description>
      <category>cms</category>
      <category>contentwriting</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Create A Simple Website with OneEntry Headless CMS</title>
      <dc:creator>Lorenzo Zarantonello</dc:creator>
      <pubDate>Thu, 09 May 2024 07:46:07 +0000</pubDate>
      <link>https://forem.com/lorenzojkrl/create-a-simple-website-with-oneentry-headless-cms-523g</link>
      <guid>https://forem.com/lorenzojkrl/create-a-simple-website-with-oneentry-headless-cms-523g</guid>
      <description>&lt;p&gt;Generally speaking, OneEntry Headless CMS offers a streamlined solution with user-friendly interfaces, and intuitive tools to simplify the website creation process. &lt;/p&gt;

&lt;p&gt;Any website created with OneEntry is supported across different platforms and devices allowing for a mobile-first approach for the ones who want it.&lt;/p&gt;

&lt;p&gt;Even though these characteristics make the creation of a multi-platform website very intuitive, the deployment can be trickier compared to traditional solutions like WordPress.&lt;/p&gt;

&lt;p&gt;Anyway, here is my experience creating a website using OneEntry Headless CMS. &lt;/p&gt;

&lt;h2&gt;
  
  
  Registration and Plan Selection
&lt;/h2&gt;

&lt;p&gt;First of all, register on the &lt;a href="https://oneentry.cloud/"&gt;OneEntry Headless CMS&lt;/a&gt; website.&lt;br&gt;
Head over to OneEntry and sign up for an account.&lt;br&gt;
Upon registration, you'll receive a 100% free promo named "Free Plan Discount."&lt;/p&gt;

&lt;p&gt;Thanks to this offer you can try OneEntry Headless CMS at no cost and without sharing any credit card details. In other words, it is possible to explore the platform's features without any financial commitment, which is definitely a positive aspect.&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%2F036v2108oynelbkzxml1.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%2F036v2108oynelbkzxml1.png" alt="OneEntry Free Plan" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once registered, you can add a project name and subdomain name. Finally, you can select a plan according to your needs.&lt;/p&gt;

&lt;p&gt;Whether you’re a business owner looking to establish an e-commerce website or a developer seeking advanced customization options, OneEntry Headless CMS offers a range of plans to accommodate diverse requirements.&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%2Fjijzm6eci8w8w9xpu4nl.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%2Fjijzm6eci8w8w9xpu4nl.png" alt="OneEntry Headless CMS plans" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve filled in the necessary details, sit back and relax while it creates your project. This automated process may take a few minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Access Your Admin Panel
&lt;/h2&gt;

&lt;p&gt;Once your project is up and running, it’s time to dive into the heart of OneEntry Headless CMS: The admin panel.&lt;/p&gt;

&lt;p&gt;Click on your project card and go to the ‘Access’ tab.&lt;/p&gt;

&lt;p&gt;Here you can see some info about your project. Click on the URL to access your admin panel. Here, you’ll be prompted to enter the login credentials sent to your registered email address during the registration process.&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%2F3525q2w90eo1y7ztqajx.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%2F3525q2w90eo1y7ztqajx.png" alt="Project URL on OneEntry&amp;lt;br&amp;gt;
" width="800" height="251"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s Build a Demo
&lt;/h2&gt;

&lt;p&gt;Now that you have access to the admin panel, it is time to work on the website.&lt;/p&gt;

&lt;p&gt;Let’s walk through the process of creating a dummy site with a nested menu and a contact form as an example.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create Pages and Content
&lt;/h3&gt;

&lt;p&gt;In the ‘Settings’ section, you’ll have the option to create pages for your website. Let’s start with the homepage. Enter a page name, such as ‘Home,’ and specify any additional attributes, such as header title and image.&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%2Fy0861tzunqsoazc0exa2.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%2Fy0861tzunqsoazc0exa2.png" alt="Editing Pages Content From Visual Editor" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Simply select the desired template, add the page title, and upload any necessary assets, such as logos or images. Thanks to the clear and intuitive interface, this is pretty simple and straightforward.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Implement the Contact Us Form
&lt;/h3&gt;

&lt;p&gt;No website is complete without a Contact Us form.&lt;/p&gt;

&lt;p&gt;In the following example, we begin by developing a form with fields for name, email, message, and a submit button.&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%2Fierh77pizngwshn5yuu4.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%2Fierh77pizngwshn5yuu4.png" alt="Creating A Form From Visual Editor&amp;lt;br&amp;gt;
" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the form is created, navigate to the ‘Managing Forms’ section to create a dedicated page for the contact form. Specify the page name, marker, and type (in this case, ‘form’), and then proceed to add the form to the page.&lt;/p&gt;

&lt;p&gt;OneEntry Headless CMS offers a built-in form management tool, that allows for validations and other configurations to ensure that the form works as intended.&lt;/p&gt;

&lt;p&gt;If you ever worked in web development, you know how form validation can be painful.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Set Up Nested Main Menu and Navigation
&lt;/h3&gt;

&lt;p&gt;With all pages and content in place, it’s time to organize your website’s navigation.&lt;/p&gt;

&lt;p&gt;We can start by creating a main menu through the ‘Main menu’ section, where you’ll have the option to create a primary navigation menu for the website.&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%2Fvnoaeoheirn2rht0s0h5.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%2Fvnoaeoheirn2rht0s0h5.png" alt="Creating A Nested Menu&amp;lt;br&amp;gt;
" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On this page, you can add menu items and sub-items to create a nested menu using the intuitive interface.&lt;/p&gt;

&lt;p&gt;Every page, form, and menu is also created to fit the wide variety of devices on the market so you will not need to care about mobile layout or responsiveness as they come by default.&lt;/p&gt;

&lt;p&gt;While other CMS like WordPress rely on a myriad of themes and plugins, OneEntry Headless CMS has built-in solutions for the most common cases like building your own menu and forms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Words
&lt;/h3&gt;

&lt;p&gt;OneEntry Headless CMS offers a user-friendly and simple-to-use platform for creating web and mobile applications with ease. From registration to customization, the platform streamlines the entire website creation process, allowing users to bring their vision to life while reducing technical complexity.&lt;/p&gt;

&lt;p&gt;I am pleased to see that the platform is regularly adding new features and pushing to simplify the whole development process even further.&lt;/p&gt;

&lt;p&gt;Unlike other CMS, you won’t need to rely on third-party tools or plugins to get your work done. Furthermore, in the long term, you will only rely on one company and won’t need to update multiple themes, and plugins from other vendors.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>cms</category>
    </item>
    <item>
      <title>OneEntry Headless CMS: What Is It?</title>
      <dc:creator>Lorenzo Zarantonello</dc:creator>
      <pubDate>Fri, 22 Dec 2023 11:44:57 +0000</pubDate>
      <link>https://forem.com/lorenzojkrl/oneentry-headless-cms-what-is-it-4004</link>
      <guid>https://forem.com/lorenzojkrl/oneentry-headless-cms-what-is-it-4004</guid>
      <description>&lt;p&gt;For quite a long time, traditional content management systems have been hampered by the challenges that come with re-sharing and reusing materials across platforms, developmental bottlenecks, and scalability issues.&lt;/p&gt;

&lt;p&gt;However, the advent of headless CMSs has significantly altered the industry and provided users with numerous innovative features and greater flexibility.&lt;/p&gt;

&lt;p&gt;In this article, we will take a look at OneEntry Headless CMS, a state-of-the-art headless CMS solution, and explain how it can be used compared to its traditional counterparts, from integration to implementation.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  What Are Headless CMSs &amp;amp; How Do They Differ from Traditional Ones?
&lt;/h2&gt;

&lt;p&gt;Before we delve into the specifics of OneEntry's solution, let's review what a headless CMS is.&lt;/p&gt;

&lt;p&gt;The primary goal of a headless CMS is to provide a flexible and decoupled approach to managing and delivering content across various digital channels. Unlike traditional CMSs, which typically combine content creation and presentation in a monolithic system, their headless alternatives separate these two layers.&lt;/p&gt;

&lt;p&gt;Some other key differences between headless and conventional CMSs that you need to be aware of include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Front-end dependence&lt;/strong&gt;. A headless CMS enables developers to use any front-end framework, providing greater flexibility for building websites, mobile apps, or other digital platforms. In the traditional CMS approach, on the other hand, the front end is often integrated into the content management system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scaling possibilities&lt;/strong&gt;. Headless CMS systems are often more scalable as they can handle diverse front-end technologies and are not tied to a specific presentation layer. This makes them suitable for large and complex projects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adaptability to innovations&lt;/strong&gt;. Headless CMS solutions are more adaptable to changing technologies and trends since they are not bound to a specific front-end architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yet, perhaps the most fundamental distinction between these two CMS types lies in their operational principles. &lt;/p&gt;

&lt;p&gt;In a headless CMS, content is delivered via APIs, which allows it to be used in different contexts. In contrast, conventional CMSs typically deliver content in a specific, pre-defined way. &lt;/p&gt;

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

</description>
      <category>cms</category>
      <category>contentwriting</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>2 - GraphQL Core Concepts: Schema, Resolvers, Query, Apollo</title>
      <dc:creator>Lorenzo Zarantonello</dc:creator>
      <pubDate>Thu, 23 Nov 2023 16:39:52 +0000</pubDate>
      <link>https://forem.com/lorenzojkrl/2-graphql-core-concepts-4gkd</link>
      <guid>https://forem.com/lorenzojkrl/2-graphql-core-concepts-4gkd</guid>
      <description>&lt;p&gt;In this post, we will talk about the core concepts of GraphQL:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Schema,&lt;/li&gt;
&lt;li&gt;Resolvers,&lt;/li&gt;
&lt;li&gt;Query&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the next post, we will dive deeper into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mutations, &lt;/li&gt;
&lt;li&gt;Subscriptions. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As said earlier, unlike REST APIs, GraphQL can use a single endpoint to fetch data from a GraphQL API. &lt;/p&gt;

&lt;p&gt;In this sentence, you see the &lt;a href="https://graphql.org/learn/" rel="noopener noreferrer"&gt;dual nature of GraphQL&lt;/a&gt;: "&lt;em&gt;GraphQL is a query language for your API, and a server-side runtime for executing queries&lt;/em&gt;".&lt;/p&gt;

&lt;p&gt;This is important to know because some terminology can be confusing e.g. a client queries the server is simply sending a request in a specific query format. The Query type defines the format from the GraphQL server.&lt;/p&gt;

&lt;p&gt;In this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;lowercase query defines an HTTP POST request.&lt;/li&gt;
&lt;li&gt;Query with capital "C" indicates the Query type object in the GraphQL server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a side note, all GraphQL requests to the server are HTTP POST.&lt;/p&gt;

&lt;p&gt;Let's see some examples to clarify the core concepts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Schema
&lt;/h2&gt;

&lt;p&gt;The schema is the rulebook that explains what is possible to retrieve from a GraphQL API. Formally, "&lt;em&gt;the schema represents data objects with object types&lt;/em&gt;", &lt;a href="https://graphql.com/learn/schema/#inspecting-the-schema" rel="noopener noreferrer"&gt;graphql.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A graphQL schema is defined in a file in your backend. &lt;/p&gt;

&lt;p&gt;For the sake of simplicity, I initialized a simple backend with &lt;code&gt;npm init&lt;/code&gt; and I defined a schema in a file called index.js.&lt;/p&gt;

&lt;p&gt;Here is a schema defining a Fruit.&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="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;index.js&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Fruit&lt;/span&gt;&lt;span class="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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;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 is just an example. But it is enough to understand that a graphQL schema roughly resembles a TypeScript type (to me at least).&lt;/p&gt;

&lt;p&gt;To be more precise, the object type Fruit, determines that fruits can have four fields: id, name, quantity, and price. &lt;/p&gt;

&lt;p&gt;Each field has a type (scalar type).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;id has type ID, which is a "special" String because GraphQl makes sure it is unique.&lt;/li&gt;
&lt;li&gt;name is of type String,&lt;/li&gt;
&lt;li&gt;quantity is of type integer,&lt;/li&gt;
&lt;li&gt;price is of type integer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In GraphQL the type of a field (e.g. scalar type) can be one of the following: String, Int, Float, Boolean, and ID.&lt;/p&gt;

&lt;p&gt;All fields are required. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;!&lt;/code&gt; symbol indicates that the field is mandatory. In more formal terms, the field should never return null. Note that an empty list &lt;code&gt;[]&lt;/code&gt; is empty and not null.&lt;/p&gt;

&lt;p&gt;Read more about &lt;a href="https://graphql.com/learn/scalars-objects-lists/" rel="noopener noreferrer"&gt;types (object, scalar, and lists)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;"&lt;em&gt;To let us query anything at all, a schema requires first and foremost the Query type. [...] the Query type acts as the front door to the service&lt;/em&gt;", &lt;a href="https://graphql.com/learn/schema/#using-the-query-type" rel="noopener noreferrer"&gt;graphQL&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Query type is a special object type (like Fruit).&lt;br&gt;
So, the first Query we add is&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="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;index.js&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Fruit&lt;/span&gt;&lt;span class="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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;allFruits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fruit&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing with Apollo Server
&lt;/h3&gt;

&lt;p&gt;Since we are defining these things server-side, we can quickly test them by installing the Apollo server, as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @apollo/server graphql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the installation is completed, change &lt;code&gt;index.js&lt;/code&gt; to require ApolloServer. We are also including some dummy data from &lt;a href="https://graphql.com/learn/schema/#using-the-query-type" rel="noopener noreferrer"&gt;graphql.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At the bottom, we are instantiating a new ApolloServer to which we pass the schema.&lt;/p&gt;

&lt;p&gt;Finally, we start the server with startStandaloneServer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApolloServer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@apollo/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;startStandaloneServer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@apollo/server/standalone&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fruits&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;F2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blueberry&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;price&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;quantity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;F3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pear&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;price&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;quantity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;36&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;F1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;banana&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;price&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;quantity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;84&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;typeDefs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
  type Fruit {
    id: ID!
    name: String!
    quantity: Int!
    price: Int!
  }

  type Query {
    allFruits: [Fruit!]!
  }
`&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApolloServer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;typeDefs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nf"&gt;startStandaloneServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server available at &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now, if you run &lt;code&gt;node index.js&lt;/code&gt; you will start the GraphQL server in your terminal. You should see something like "Server available at &lt;a href="http://localhost:4000/" rel="noopener noreferrer"&gt;http://localhost:4000/&lt;/a&gt;".&lt;/p&gt;

&lt;p&gt;Click on &lt;a href="http://localhost:4000/" rel="noopener noreferrer"&gt;http://localhost:4000/&lt;/a&gt; to open Apollo Sandbox where you can test queries etc.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Resolver: Connecting client queries to schema
&lt;/h2&gt;

&lt;p&gt;The Operation area on Apollo Sandbox allows us to try queries (query the GraphQL server) as if we would send requests from a client. &lt;/p&gt;

&lt;p&gt;As an example, the following query (HTTP request) should return all fruit names, but it will fail.&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="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ExampleQuery&lt;/span&gt;&lt;span class="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;allFruits&lt;/span&gt;&lt;span class="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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the schema we defined the object type Query as follows:&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;allFruits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fruit&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but how do we get from the fruits array to allFruits?&lt;/p&gt;

&lt;p&gt;We use resolvers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resolver Example 1
&lt;/h3&gt;

&lt;p&gt;Here is one resolver:&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="err"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;resolvers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;allFruits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="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="err"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So our &lt;code&gt;index.js&lt;/code&gt; becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
let fruits = [...]

const typeDefs = `
  type Fruit { ... }

  type Query {
    allFruits: [Fruit!]!
  }
`
const resolvers = {
  Query: {
    allFruits: () =&amp;gt; fruits,
  },
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
});
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why all of this?&lt;br&gt;
Let's look at it from the client perspective. The client sends a request (a query):&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="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ExampleQuery&lt;/span&gt;&lt;span class="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;allFruits&lt;/span&gt;&lt;span class="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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;The request wants the "name" Field for all objects in allFruits&lt;/li&gt;
&lt;li&gt;The Query type in the schema acts as the front door and allows for queries that want &lt;code&gt;allFruits&lt;/code&gt;. It also defines that queries to &lt;code&gt;allFruits&lt;/code&gt; should return a non-null array of Fruit e.g. &lt;code&gt;[Fruit!]!&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The resolver picks up the request and returns the fruits array &lt;code&gt;allFruits: () =&amp;gt; fruits&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Resolver Example 2
&lt;/h3&gt;

&lt;p&gt;Let's make another example assuming we want to get the number of fruit objects in the fruits array.&lt;/p&gt;

&lt;p&gt;With a REST API, we would need to fetch the whole array and once we receive it, we would do something like &lt;code&gt;fruits.length&lt;/code&gt; in the client device.&lt;/p&gt;

&lt;p&gt;With GraphQL we can define another field in the schema type Query. The number of fruits is going to be an integer so the schema type Query becomes:&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;allFruits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fruit&lt;/span&gt;&lt;span class="p"&gt;!]!,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;fruitsCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;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;Now we created a new "door" called &lt;code&gt;fruitsCount&lt;/code&gt; that will return an integer, e.g. the number of fruit objects in the fruits array.&lt;/p&gt;

&lt;p&gt;Where do we get this number? We create a resolver for the &lt;code&gt;fruitsCount&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;So that when a client pings the GraphQL API with a &lt;code&gt;fruitsCount&lt;/code&gt; query, we return the number of fruit objects in the fruits array.&lt;/p&gt;

&lt;p&gt;Let's update the resolver as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolvers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fruitsCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;allFruits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now, if a client uses the following query, it will only get an object containing a number:&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="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ExampleQuery&lt;/span&gt;&lt;span class="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;fruitsCount&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Resolver Example 3
&lt;/h3&gt;

&lt;p&gt;Let's assume we want to find a fruit by its name.&lt;/p&gt;

&lt;p&gt;We start by updating the type Query (the "door") in the schema:&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;fruitsCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;allFruits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fruit&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;findFruit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Fruit&lt;/span&gt;&lt;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;We then update the resolver to find and return a specific fruit&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolvers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fruitsCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;allFruits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;findFruit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;fruit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fruit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The findFruit resolver is a bit different but you can &lt;a href="https://the-guild.dev/graphql/tools/docs/resolvers#resolver-function-signature" rel="noopener noreferrer"&gt;read more about resolvers here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A client could query the GraphQL API as follows to obtain the total number of fruit objects and some specific data on the "pear" object.&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="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ExampleQuery&lt;/span&gt;&lt;span class="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;fruitsCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
  &lt;/span&gt;&lt;span class="n"&gt;findFruit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pear"&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="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;The JSON response would be:&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;"fruitsCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"findFruit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"F3"&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;"pear"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="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;
  
  
  Query
&lt;/h2&gt;

&lt;p&gt;GraphQL uses queries to retrieve data from a GraphQL API. Each query builds on Types and Fields. In the following example (&lt;a href="https://graphql.com/learn/the-query/#running-a-query" rel="noopener noreferrer"&gt;from grapql.com&lt;/a&gt;) we run a Query to get fruits (Type) and prices (Field):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We want to get data of type "fruits"&lt;/li&gt;
&lt;li&gt;For each fruit we get, we want the price field&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;"&lt;em&gt;This query syntax begins with the query keyword, followed by an operation name that describes the request's purpose—like GetFruitPrices.&lt;/em&gt;"&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="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GetFruitPrices&lt;/span&gt;&lt;span class="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;fruits&lt;/span&gt;&lt;span class="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;price&lt;/span&gt;&lt;span class="p"&gt;,&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response is:&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;"fruits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="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;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;44&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;"banana"&lt;/span&gt;&lt;span class="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;"price"&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="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;"blueberry"&lt;/span&gt;&lt;span class="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;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;79&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;"pear"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But how do we know what we can retrieve from a GraphQL API?&lt;br&gt;
In the Schema.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
