<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sapan Diwakar</title>
    <description>The latest articles on DEV Community by Sapan Diwakar (@diwakarsapan).</description>
    <link>https://dev.to/diwakarsapan</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%2F726138%2Fa3fe6405-aa04-4830-a1cb-d0b9f6301d29.png</url>
      <title>DEV Community: Sapan Diwakar</title>
      <link>https://dev.to/diwakarsapan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/diwakarsapan"/>
    <language>en</language>
    <item>
      <title>Find and Fix N+1 Queries Using AppSignal for a Phoenix App in Elixir</title>
      <dc:creator>Sapan Diwakar</dc:creator>
      <pubDate>Tue, 05 Nov 2024 10:00:45 +0000</pubDate>
      <link>https://dev.to/appsignal/find-and-fix-n1-queries-using-appsignal-for-a-phoenix-app-in-elixir-3npj</link>
      <guid>https://dev.to/appsignal/find-and-fix-n1-queries-using-appsignal-for-a-phoenix-app-in-elixir-3npj</guid>
      <description>&lt;p&gt;N+1 queries are a frequent issue in complex applications built with Elixir and Phoenix. These queries can silently degrade application performance, often going unnoticed until they've compounded into a significant problem.&lt;/p&gt;

&lt;p&gt;They can substantially increase web page load times, as each additional query adds overhead to a database, consuming more time and resources. That's why it's crucial to detect and resolve N+1 queries to optimize production systems. It's not just about maintaining a seamless user experience; it's also about ensuring you maintain your Elixir application's scalability and efficiency.&lt;/p&gt;

&lt;p&gt;Using AppSignal, you can identify N+1 queries in Phoenix applications, alongside a comprehensive suite of tools to monitor, diagnose, and rectify performance bottlenecks in Elixir projects.&lt;/p&gt;

&lt;p&gt;Let's explore how N+1 queries happen, their impact on performance, and some strategies to detect and fix them in your Elixir application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding And Fixing N+1 Queries in Phoenix for Elixir
&lt;/h2&gt;

&lt;p&gt;N+1 queries occur when an application retrieves a single database record, followed by an additional query for each related record.&lt;br&gt;
In Phoenix applications, N+1 queries often emerge in the context of complex relationships and Ecto associations.&lt;/p&gt;

&lt;p&gt;Even small mistakes in Ecto queries can lead to cascading N+1 issues, resulting in multiple round trips to a database, each fetching only a small amount of data.&lt;/p&gt;

&lt;p&gt;Some common examples include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using &lt;code&gt;Repo.all&lt;/code&gt; without a proper &lt;code&gt;preload&lt;/code&gt; clause (this will not fetch associated records in the same query).&lt;/li&gt;
&lt;li&gt;In GraphQL, when using Absinthe with Elixir, N+1 query issues can arise when resolving fields that require data from associated records.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://blog.appsignal.com/2023/06/06/absinthe-for-large-elixir-applications.html#avoiding-n1-queries-in-elixir" rel="noopener noreferrer"&gt;Avoiding N+1 Queries for Absinthe&lt;/a&gt; for an overview of this topic.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  A Simple Example Using Ecto
&lt;/h3&gt;

&lt;p&gt;For instance, consider a blog application where each post has many comments. A naive Ecto query might retrieve all posts and then run a separate query for each post's comments. This results in one query for the posts (&lt;code&gt;N&lt;/code&gt;) and one query for each post's comments (&lt;code&gt;+1&lt;/code&gt;), which is highly inefficient.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Fetching posts without preloading comments&lt;/span&gt;
&lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# This will cause N+1 queries, as for each post, comments are fetched separately&lt;/span&gt;
&lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="no"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;where:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above might sound like a contrived example — experienced developers are quite unlikely to do this. But it is meant to serve as a simplified scenario of how N+1 queries can creep into systems: any query inside a loop is a suspect. N+1 queries are usually hidden in plain sight, for example, behind a context function like &lt;code&gt;Blog.list_comments(post_id: post.id)&lt;/code&gt; inside the loop.&lt;/p&gt;

&lt;p&gt;Next, let's discuss a complex scenario.&lt;/p&gt;

&lt;h3&gt;
  
  
  Complex Situations Leading to N+1 Queries
&lt;/h3&gt;

&lt;p&gt;Imagine a blog application displaying a feed of all recent posts and the count of comments (or likes) for each post.&lt;/p&gt;

&lt;p&gt;A sample implementation could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Load posts from subscribed authors&lt;/span&gt;
&lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Load comments count per post&lt;/span&gt;
&lt;span class="n"&gt;for&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;comments_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="no"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;where:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;select:&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"post.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;post:&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;comments_count:&lt;/span&gt; &lt;span class="n"&gt;comments_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the &lt;code&gt;comments_count&lt;/code&gt; query causes an N+1 problem because we fetch each individual post's count. One might argue that we can just add &lt;code&gt;comments&lt;/code&gt; to the preloads and be done with it. While that solves the N+1 query, it loads too much unnecessary data (we don't need all the comments yet, only the total number of comments) and might actually hurt loading times.&lt;/p&gt;

&lt;p&gt;A better solution is to &lt;code&gt;join&lt;/code&gt; the tables and use &lt;code&gt;select&lt;/code&gt; to load the comments count within the &lt;code&gt;posts&lt;/code&gt; query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;posts_with_counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;left_join:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="no"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;on:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;group_by:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;select:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;comments_count:&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;for&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;posts_with_counts&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"post.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;post:&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;comments_count:&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comments_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To avoid and detect N+1 queries, developers must be vigilant when writing Ecto queries and templates.&lt;br&gt;
Using &lt;code&gt;preload&lt;/code&gt; correctly to fetch all necessary records in a single database query is essential.&lt;/p&gt;
&lt;h2&gt;
  
  
  Detecting N+1 Queries By Their Impact
&lt;/h2&gt;

&lt;p&gt;Understanding the data access patterns of your application can help you anticipate and prevent N+1 issues before they arise.&lt;br&gt;
One way to identify N+1 queries is to observe and analyze common bottlenecks in your app, including:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Increased Load Times&lt;/strong&gt;: Each query takes time to execute, and when hundreds or thousands of them run sequentially, the cumulative effect leads to noticeable delays in content rendering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database Strain&lt;/strong&gt;: Databases are designed to handle multiple queries efficiently, but an influx of unnecessary queries can strain the system, leading to slower response times for all users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server Resource Drain&lt;/strong&gt;: A server's resources are finite, and handling a multitude of queries consumes more CPU and memory, potentially affecting other operations and leading to resource exhaustion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability Issues&lt;/strong&gt;: As a user base grows, inefficient N+1 queries can prevent an application from scaling smoothly, requiring more hardware resources to handle the same workload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Implications&lt;/strong&gt;: Increased server load and database usage can lead to higher operational costs, especially in cloud-based environments where resources are metered.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;However, it is difficult to spot small differences in performance and then pinpoint the parts of your application that have caused such degradation.&lt;/p&gt;

&lt;p&gt;The next sections will show how AppSignal can help you detect and fix N+1 queries, ensuring your application runs smoothly and efficiently.&lt;/p&gt;
&lt;h2&gt;
  
  
  Detecting N+1 Queries in Phoenix for Elixir Using AppSignal
&lt;/h2&gt;

&lt;p&gt;Before detecting N+1 queries, we need to set up AppSignal in your Phoenix application. &lt;a href="https://appsignal.com/users/sign_up" rel="noopener noreferrer"&gt;You can sign up for a free 30-day trial of AppSignal&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;AppSignal provides an Elixir package that integrates seamlessly with Phoenix applications. The &lt;a href="https://docs.appsignal.com/elixir/installation.html" rel="noopener noreferrer"&gt;AppSignal for Elixir setup process&lt;/a&gt; involves adding the Elixir package to your project's dependencies and configuring it with your AppSignal Push API key. Once installed, AppSignal starts monitoring your application's performance, including database query patterns.&lt;/p&gt;
&lt;h2&gt;
  
  
  Analyzing Elixir Data with AppSignal
&lt;/h2&gt;

&lt;p&gt;AppSignal's instrumentation for Phoenix and Ecto is designed to provide detailed insights into your application's database interactions. It automatically tracks database query times, helping you spot inefficiencies. With AppSignal's instrumentation in place, detecting N+1 queries becomes a matter of analyzing the collected data.&lt;/p&gt;
&lt;h3&gt;
  
  
  Use Slow Queries to Find N+1 Queries
&lt;/h3&gt;

&lt;p&gt;AppSignal's &lt;em&gt;Slow Queries&lt;/em&gt; dashboard breaks down web requests, showing the slowest queries and potential bottlenecks. A series of similar queries within a single web request is a strong indicator of an N+1 query problem.&lt;/p&gt;

&lt;p&gt;For example, if you see repeated queries fetching comments for different posts while a blog page is being rendered, you've likely encountered an N+1 issue. AppSignal provides context, including specific database calls, making it easier to pinpoint the exact location in your code where the issue originated.&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%2F8m3uevzhjuyq6bw4bo3m.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%2F8m3uevzhjuyq6bw4bo3m.png" alt="Screenshot of AppSignal Slow Queries Page" width="800" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see that the first query had almost 95% &lt;a href="https://docs.appsignal.com/appsignal/terminology.html#impact" rel="noopener noreferrer"&gt;impact&lt;/a&gt; on all our queries executed through Ecto.&lt;br&gt;
In a real-world app, this number might be lower, as there can be hundreds of unique queries. However, any outlier that's highlighted here is a great candidate for analysis.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Note:&lt;/strong&gt; The "impact" of a query on an application is based on its usage compared to other queries.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another metric to consider here is the high throughput of &lt;code&gt;720&lt;/code&gt;.&lt;br&gt;
Throughput is the total number of queries that were executed in a specified time window.&lt;/p&gt;

&lt;p&gt;Let's check out the full details of the query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;c0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"post_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"inserted_at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"post_id"&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"comments"&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;c0&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"post_id"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;c0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"post_id"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the query is targeting a single post, it is quite likely an N+1 query.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trace N+1 Queries with the Event Timeline
&lt;/h3&gt;

&lt;p&gt;AppSignal's &lt;em&gt;Event Timeline&lt;/em&gt; is also particularly useful for finding and tracing N+1 queries.&lt;br&gt;
It visualizes a sequence of database calls during a web request, allowing you to identify the hallmark successive database queries that characterize N+1 issues.&lt;/p&gt;

&lt;p&gt;Here's an example event timeline for a page that has an N+1 query issue:&lt;br&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%2Flgl9ppfqr3hacrbywjew.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%2Flgl9ppfqr3hacrbywjew.png" alt="Screenshot of AppSignal Performance Sample Event Timeline" width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see that many quick queries are being performed.&lt;br&gt;
Hovering over &lt;code&gt;query.ecto&lt;/code&gt; also shows the exact query that was performed to help us pinpoint which query is the culprit.&lt;/p&gt;

&lt;p&gt;Another good indication of a possible issue is when the &lt;code&gt;ecto&lt;/code&gt; time of an event is abnormally high.&lt;br&gt;
For example, in this case, we can see that Ecto was the major time-hog:&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%2F4f3iyxbbmfpasntledu5.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%2F4f3iyxbbmfpasntledu5.png" alt="Screenshot of AppSignal Performance Sample" width="800" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Addressing Detected N+1 Queries
&lt;/h2&gt;

&lt;p&gt;Once you've identified an N+1 query with AppSignal, the next step is to address it.&lt;br&gt;
This typically involves optimizing your Ecto queries to preload associated records.&lt;/p&gt;

&lt;p&gt;AppSignal's detailed metrics allow you to compare performance before and after the changes, giving you concrete feedback on the impact of your optimizations.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Whether you're a seasoned developer or new to Phoenix, understanding the nuances of N+1 queries and leveraging AppSignal's capabilities will empower you to build faster, more reliable applications.&lt;/p&gt;

&lt;p&gt;Remember, the key to maintaining a performant application is not just fixing issues as they arise, but continuously monitoring and improving your codebase with the help of powerful tools like AppSignal.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, &lt;a href="https://dev.to/elixir-alchemy"&gt;subscribe to our Elixir Alchemy newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>phoenix</category>
    </item>
    <item>
      <title>Scaling Your Phoenix App in Elixir with FLAME</title>
      <dc:creator>Sapan Diwakar</dc:creator>
      <pubDate>Wed, 18 Sep 2024 09:18:38 +0000</pubDate>
      <link>https://dev.to/appsignal/scaling-your-phoenix-app-in-elixir-with-flame-244o</link>
      <guid>https://dev.to/appsignal/scaling-your-phoenix-app-in-elixir-with-flame-244o</guid>
      <description>&lt;p&gt;When you build an app, you'll often find that certain tasks do not require user interaction and are better performed in the background. Elixir provides excellent primitives, such as &lt;code&gt;Task.async&lt;/code&gt;, to offload these tasks from the main user pipeline. Additionally, libraries like Oban offer more control over background tasks when needed.&lt;/p&gt;

&lt;p&gt;There's also &lt;code&gt;FLAME&lt;/code&gt;, which the core Phoenix team is developing to offer a scalable solution for offloading intensive tasks to remote machines.&lt;/p&gt;

&lt;p&gt;We will compare these methods and see how FLAME stands out.&lt;/p&gt;

&lt;p&gt;But first, let's delve into how FLAME works and how it can help you scale your Phoenix application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding FLAME for Phoenix
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;FLAME&lt;/code&gt; stands for &lt;em&gt;&lt;strong&gt;F&lt;/strong&gt;leeting &lt;strong&gt;L&lt;/strong&gt;ambda &lt;strong&gt;A&lt;/strong&gt;pplication for &lt;strong&gt;M&lt;/strong&gt;odular &lt;strong&gt;E&lt;/strong&gt;xecution&lt;/em&gt;. It is similar to &lt;code&gt;Task.async&lt;/code&gt;, allowing you to run any block of code, but with the added benefit of executing it on a separate node. Depending on your configuration, FLAME can automatically manage infrastructure, spawning or scaling down nodes as needed.&lt;/p&gt;

&lt;p&gt;To illustrate how FLAME works, let's consider an example where we need to find the SHA-256 hash of a file stored on Amazon S3. Calculating the hash for a large file can prove slow due to file size or network latency. By offloading this task to a background worker with FLAME, we can improve the performance and responsiveness of our main application.&lt;/p&gt;

&lt;p&gt;Here’s how you can do it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="no"&gt;FLAME&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;BackgroundRunner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="no"&gt;ExAws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;S3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;download_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:memory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;ExAws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stream!&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hash_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:sha256&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="ss"&gt;:crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hash_update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hash_final&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode16&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is similar to &lt;code&gt;Task.async&lt;/code&gt; and &lt;code&gt;Task.await&lt;/code&gt;, with the key difference being that it runs on a separate node. When the checksum is ready, it returns the result to the main node. We'll discuss configuration options and available functions later in the post.&lt;/p&gt;

&lt;p&gt;So, how is this different from any background job processing framework (e.g., Oban)?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Built-in Scaling&lt;/strong&gt;: FLAME has built-in support for scaling.
It can automatically spawn new nodes when new tasks come in and scale down to zero (configurable) when no tasks are in the queue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal Boilerplate&lt;/strong&gt;: Unlike other frameworks, FLAME does not require extensive boilerplate code. You simply wrap your task inside &lt;code&gt;FLAME.call&lt;/code&gt; or &lt;code&gt;FLAME.cast&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Awaiting Results&lt;/strong&gt;: In most job frameworks (including the free version of Oban), you cannot await the result of background tasks. FLAME, however, supports this feature, allowing you to wait for a task's completion and retrieve the result.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  FLAME in Action
&lt;/h2&gt;

&lt;p&gt;In the previous section, we saw how easy it was to run a piece of code on a separate node using FLAME. Let's pull back a little and see how to integrate FLAME inside an existing Phoenix app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install FLAME
&lt;/h3&gt;

&lt;p&gt;Add the dependency in &lt;code&gt;mix.exs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:flame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.1.12"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Start a FLAME Pool
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;FLAME.Pool&lt;/code&gt; is the main &lt;code&gt;GenServer&lt;/code&gt; provided by FLAME that manages the scaling of nodes inside your app. It also schedules tasks and delivers results to each task's respective nodes.&lt;/p&gt;

&lt;p&gt;Add &lt;code&gt;FLAME.Pool&lt;/code&gt; to your application's supervision tree by updating the &lt;code&gt;application.ex&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Application&lt;/span&gt;

  &lt;span class="nv"&gt;@half_hour_millis&lt;/span&gt; &lt;span class="mi"&gt;1_800_000&lt;/span&gt;

  &lt;span class="nv"&gt;@impl&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;children&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="c1"&gt;# ...&lt;/span&gt;
        &lt;span class="c1"&gt;# The BackgroundRunner pool controlled by FLAME&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;FLAME&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;BackgroundRunner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;min:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;max:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;max_concurrency:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;idle_shutdown_after:&lt;/span&gt; &lt;span class="nv"&gt;@half_hour_millis&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;strategy:&lt;/span&gt; &lt;span class="ss"&gt;:one_for_one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Supervisor&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="no"&gt;Supervisor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;name&lt;/code&gt; option defines the pool name to be used in calls to FLAME later in the application. This allows you to define several different pools with different configurations depending on the tasks that you want to achieve.&lt;/p&gt;

&lt;p&gt;For example, for nodes that handle hash computation, you might want different concurrency settings than the nodes that handle more complex tasks, like video encoding. &lt;a href="https://hexdocs.pm/flame/FLAME.Pool.html#start_link/1" rel="noopener noreferrer"&gt;Check out all the available options for the pool&lt;/a&gt;. The most important ones that you will commonly use are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;:min&lt;/code&gt; - The minimum number of nodes to start. You can configure this to zero to support pools that spawn a node only when there is a task, and then scale down when there are no pending tasks. This is especially useful in pre-production environments to save infrastructure costs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:max&lt;/code&gt; - The maximum number of nodes at a time.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:max_concurrency&lt;/code&gt; - The maximum number of concurrent executions on a node.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:timeout&lt;/code&gt; - Default timeout for functions. This can also be set during individual calls (using &lt;code&gt;FLAME.call&lt;/code&gt; with the &lt;code&gt;timeout&lt;/code&gt; option).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:idle_shutdown_after&lt;/code&gt; - The number of seconds after which an idle node is shut down (only if there are more than the &lt;code&gt;min&lt;/code&gt; number of nodes).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Configure FLAME Backend
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/flame/FLAME.Backend.html" rel="noopener noreferrer"&gt;FLAME &lt;code&gt;Backend&lt;/code&gt;&lt;/a&gt; is the component responsible for starting up new nodes, connecting them to parent nodes, and running functions. By default, FLAME ships with the &lt;a href="https://hexdocs.pm/flame/FLAME.FlyBackend.html" rel="noopener noreferrer"&gt;&lt;code&gt;FlyBackend&lt;/code&gt;&lt;/a&gt;. If you run your application on Fly, configuring FLAME is very simple. Just update your &lt;code&gt;config.exs&lt;/code&gt; or &lt;code&gt;runtime.exs&lt;/code&gt; if you use &lt;a href="https://hexdocs.pm/phoenix/releases.html" rel="noopener noreferrer"&gt;Elixir releases&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:flame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:backend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;FLAME&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;FlyBackend&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:flame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;FLAME&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;FlyBackend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;token:&lt;/span&gt; &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetch_env!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"FLY_API_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="ss"&gt;cpu_kind:&lt;/span&gt; &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"FLAME_CPU_KIND"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"shared"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="ss"&gt;memory_mb:&lt;/span&gt; &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"FLAME_MEMORY_MB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"1024"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_integer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To access the Fly API for spawning or destroying machines, you need a token from Fly. You can optionally configure the &lt;code&gt;cpu_kind&lt;/code&gt;, &lt;code&gt;memory&lt;/code&gt;, and &lt;code&gt;cpus&lt;/code&gt; for the spawned nodes. Check out the full list of supported options in the &lt;a href="https://hexdocs.pm/flame/FLAME.FlyBackend.html" rel="noopener noreferrer"&gt;&lt;code&gt;FlyBackend&lt;/code&gt; docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There is also a &lt;a href="https://hexdocs.pm/flame_k8s_backend/readme.html" rel="noopener noreferrer"&gt;&lt;code&gt;FLAMEK8sBackend&lt;/code&gt;&lt;/a&gt; available if you are running on Kubernetes. If you are not using Kubernetes, other platforms have no out-of-the-box support. However, you can refer to the &lt;a href="https://github.com/phoenixframework/flame/blob/main/lib/flame/fly_backend.ex" rel="noopener noreferrer"&gt;&lt;code&gt;FLAME.FlyBackend&lt;/code&gt; code&lt;/a&gt; and create a similar backend for your platform.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running Tasks with FLAME
&lt;/h3&gt;

&lt;p&gt;FLAME provides two main functions for running tasks on other nodes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;FLAME.call/1&lt;/code&gt;&lt;/strong&gt;: This function calls a task on a remote node and waits for the result. It's useful when you need to run tasks in the background but still require a result. &lt;strong&gt;Example&lt;/strong&gt;: A user requests a 10,000,000th Fibonacci number. You can run the computation on another machine to avoid blocking web server resources while the user waits for the result.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;FLAME.cast/1&lt;/code&gt;&lt;/strong&gt;: This function calls a task on a remote node without waiting for a result. It's useful when a result isn't needed immediately and you don't want to block the user pipeline. &lt;strong&gt;Example&lt;/strong&gt;: After a user uploads a file, you want to update its checksum in the records. This can be done in the background without making the user wait.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By default, the spawned processes on the remote node are linked to the parent process, preventing orphaned tasks on remote nodes if the parent process is killed or dies. However, for some background tasks (e.g., file checksum generation), it might be useful to continue running a task even if the parent process is terminated.&lt;/p&gt;

&lt;p&gt;Both &lt;code&gt;FLAME.call/2&lt;/code&gt; and &lt;code&gt;FLAME.cast/2&lt;/code&gt; support a &lt;code&gt;link&lt;/code&gt; option that can be set to &lt;code&gt;false&lt;/code&gt; to achieve this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploying FLAME
&lt;/h3&gt;

&lt;p&gt;You don't need anything special to deploy FLAME as long as you have configured a backend with all the required properties. FLAME starts your full application supervision tree on the remote node. Some parts of the application might not be necessary on a worker node. To control this, you can use &lt;a href="https://hexdocs.pm/flame/FLAME.Parent.html#get/0" rel="noopener noreferrer"&gt;&lt;code&gt;FLAME.Parent.get/0&lt;/code&gt;&lt;/a&gt; to determine if an application is running on the main node or a child node.&lt;br&gt;
Update &lt;code&gt;application.ex&lt;/code&gt; to add children to your supervision tree only if they are on the main node (i.e., if &lt;code&gt;FLAME.parent.get()&lt;/code&gt; is &lt;code&gt;nil&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Application&lt;/span&gt;

  &lt;span class="nv"&gt;@impl&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;flame_instance?&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;is_nil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;FLAME&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Parent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;children&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="c1"&gt;# ...&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;FLAME&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;BackgroundRunner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;min:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;max:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;max_concurrency:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;idle_shutdown_after:&lt;/span&gt; &lt;span class="nv"&gt;@half_hour_millis&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="n"&gt;!flame_instance?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Endpoint&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;strategy:&lt;/span&gt; &lt;span class="ss"&gt;:one_for_one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Supervisor&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="no"&gt;Supervisor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Technical Deep Dive into FLAME
&lt;/h2&gt;

&lt;p&gt;Internally, FLAME leverages Elixir's powerful &lt;a href="https://hexdocs.pm/elixir/1.16.3/Node.html#spawn_monitor/4" rel="noopener noreferrer"&gt;&lt;code&gt;Node.spawn_monitor/4&lt;/code&gt;&lt;/a&gt; function for spawning processes on remote nodes. This, coupled with BEAM's closure behavior, forms the core of FLAME's implementation.&lt;/p&gt;

&lt;p&gt;Let's delve into what occurs under the hood when you execute &lt;code&gt;FLAME.cast&lt;/code&gt; or &lt;code&gt;FLAME.call&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The request is dispatched to a pool of runners.&lt;/li&gt;
&lt;li&gt;If a runner is readily available (based on defined constraints like &lt;code&gt;min&lt;/code&gt;, &lt;code&gt;max&lt;/code&gt;, etc.), it spawns and monitors the process on the remote node.

&lt;ul&gt;
&lt;li&gt;Upon successful execution, the result is returned or discarded depending on the operation used.&lt;/li&gt;
&lt;li&gt;Additionally, it monitors the process according to the timeout configuration and terminates it if necessary.&lt;/li&gt;
&lt;li&gt;If the remote node crashes (e.g., due to memory exhaustion), the parent process is notified or terminated as per the configuration.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If a process cannot be accommodated on an existing remote node, FLAME tries to spawn a new node (using the configured backend) within the specified limits.

&lt;ul&gt;
&lt;li&gt;Once the node is spawned, it follows the same steps as mentioned in (2).&lt;/li&gt;
&lt;li&gt;If spawning a new node fails (e.g., because the maximum limit is reached), the task is queued until resources become available.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Regardless of specific user calls, FLAME autonomously manages idle nodes based on the configuration, shutting them down as needed using the configured backend.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Understanding Closures
&lt;/h3&gt;

&lt;p&gt;Closures play a pivotal role in FLAME's operation, and understanding them is essential in BEAM programming. A closure captures its enclosing environment, including variables from the outer scope. When a closure is defined, it retains references to the variables it "closes over", thus preserving the state of its enclosing process. This behavior is crucial for maintaining consistency, even in scenarios where a process crashes and restarts.&lt;/p&gt;

&lt;p&gt;However, when transmitting closures across different nodes in BEAM, the code on all nodes must match precisely. The Fly backend addresses this requirement well, as deployments on Fly rely on Docker images of the released code. This ensures uniformity between the parent node and the remote node, facilitating seamless execution of FLAME.&lt;/p&gt;

&lt;h3&gt;
  
  
  Leveraging Files as Processes
&lt;/h3&gt;

&lt;p&gt;Things get more interesting when we explore the concept of treating files as processes. In Elixir, opening a file spawns a new process. Writing to a file is akin to sending messages to the process handling the file descriptor. This behavior extends to functions like &lt;a href="https://hexdocs.pm/elixir/1.16.3/File.html#stream!/3" rel="noopener noreferrer"&gt;&lt;code&gt;File.stream!&lt;/code&gt;&lt;/a&gt; and all other file operations. Consequently, we can extend FLAME's capabilities to handle scenarios such as computing checksums of files stored on S3 or even supporting user-uploaded files directly on the web server.&lt;/p&gt;

&lt;p&gt;Here's a code sample:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stream!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# this runs on the web server&lt;/span&gt;
&lt;span class="no"&gt;FLAME&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;BackgroundRunner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;# this runs on the remote machine&lt;/span&gt;
  &lt;span class="n"&gt;stream&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hash_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:sha256&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="ss"&gt;:crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hash_update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hash_final&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode16&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this scenario, the file stream is initiated on the source machine, while the checksum computation is offloaded to the remote worker.&lt;br&gt;
This setup capitalizes on Elixir and BEAM's handling of closures and processes. However, it's worth noting that a more efficient approach would involve establishing a shared volume accessible from both nodes. A shared volume minimizes resource usage and enhances performance. This is in contrast to utilizing the stream from the main node, which still consumes resources to transmit data to the remote node.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparing FLAME With Other Approaches
&lt;/h2&gt;

&lt;p&gt;Let's now see how FLAME stacks up against some alternative approaches.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running Background Tasks on the Same Node
&lt;/h3&gt;

&lt;p&gt;Using &lt;code&gt;Task.async&lt;/code&gt; and similar functions, your tasks run on the same node as your web server. This approach works well for quick, lightweight tasks. However, during periods of heavy load, these background tasks can compete with your web server for resources, potentially slowing down your application's response time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Oban for Elixir to Get More Control
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/oban/Oban.html" rel="noopener noreferrer"&gt;Oban&lt;/a&gt; is a powerful library that allows you to manage background jobs with more granularity. It enables you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define dedicated Oban Workers.&lt;/li&gt;
&lt;li&gt;Configure job queues.&lt;/li&gt;
&lt;li&gt;Set up a job storage backend.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oban also supports running these tasks on dedicated nodes, which can help reduce the load on your main web server. However, setting up Oban involves significant boilerplate code. In its free version, it does not support automatic scaling of the physical nodes running the jobs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Achieving Infinite Scale with Serverless Functions
&lt;/h3&gt;

&lt;p&gt;For truly infinite scalability, you can use external serverless functions like AWS Lambda, Google Cloud Functions, or Azure Functions. These services allow you to run background tasks independently of your main application infrastructure. However, they come with their own set of challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Significant boilerplate and context switching for developers.&lt;/li&gt;
&lt;li&gt;Synchronization issues between third-party services and your application code.&lt;/li&gt;
&lt;li&gt;Potential latency and cold start problems.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  FLAME: A Summary
&lt;/h3&gt;

&lt;p&gt;As we have seen, &lt;code&gt;FLAME&lt;/code&gt; provides a robust way to handle complex background tasks at scale &lt;strong&gt;within&lt;/strong&gt; your Phoenix app. It aims to simplify the setup process and reduce boilerplate code, offering an efficient and scalable solution. All of this comes without the need to context switch — you can call FLAME code from within your app.&lt;/p&gt;

&lt;p&gt;By using FLAME, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easily offload tasks from the main user pipeline.&lt;/li&gt;
&lt;li&gt;Manage and scale background jobs without extensive configuration.&lt;/li&gt;
&lt;li&gt;Improve your application's performance and responsiveness during heavy loads.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post, we've explored how to scale your Phoenix application using FLAME. We've delved into how FLAME stands out by offloading tasks to remote nodes. It also offers built-in scaling with minimal boilerplate code. We've compared FLAME with other methods, too, like &lt;code&gt;Task.async&lt;/code&gt;, Oban, and external serverless functions, highlighting its unique advantages.&lt;/p&gt;

&lt;p&gt;Ultimately, FLAME helps you build more robust and scalable applications by leveraging Elixir's strengths in concurrency and distributed computing, all while keeping your development process straightforward and intuitive.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, &lt;a href="https://dev.to/elixir-alchemy"&gt;subscribe to our Elixir Alchemy newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>phoenix</category>
    </item>
    <item>
      <title>Deep Diving Into the Erlang Scheduler</title>
      <dc:creator>Sapan Diwakar</dc:creator>
      <pubDate>Tue, 07 May 2024 13:59:25 +0000</pubDate>
      <link>https://dev.to/appsignal/deep-diving-into-the-erlang-scheduler-579l</link>
      <guid>https://dev.to/appsignal/deep-diving-into-the-erlang-scheduler-579l</guid>
      <description>&lt;p&gt;Erlang is renowned for its remarkable fault tolerance and high concurrency. Erlang's scheduler efficiently handles many lightweight processes. The scheduler plays a crucial role in managing processes, concurrency, and system resources, efficiently coordinating these elements to help Erlang maintain fault tolerance and support high levels of concurrency in its applications.&lt;/p&gt;

&lt;p&gt;This post will dissect some of the scheduler's key components and shed light on how it works internally.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Processes in Erlang
&lt;/h2&gt;

&lt;p&gt;Erlang processes are lightweight, independent units of execution managed by the Erlang runtime system. They are created and scheduled by the Erlang virtual machine (BEAM), and each Erlang process has its own memory space. These should not be confused with operating system processes or threads.&lt;/p&gt;

&lt;p&gt;OS processes are entities managed by the OS and typically have more overhead in terms of memory and resources, as well as inter-process communication. While threads are lighter than OS processes, they are still heavier than a typical Erlang process and share a common memory space within a process. Communication is usually easier between threads of the same process but still requires synchronization mechanisms.&lt;/p&gt;

&lt;p&gt;On the other hand, the Erlang VM (BEAM) is capable of spawning several lightweight processes with independent memory space and can communicate easily using message passing. The scheduler inside the VM manages processes, allocates resources to processes, and context-switches between them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Erlang Scheduler Architecture — Preemptive Scheduling
&lt;/h2&gt;

&lt;p&gt;In a single-core setup, only one process can occupy the CPU core at any given time. To emulate a concurrent system, the scheduler employs a preemptive, priority-based scheduling mechanism (don't worry, we will explore what this means soon) that rapidly switches between available processes to create the illusion that all processes are executing simultaneously. The Erlang Virtual Machine (BEAM) schedules processes to run sequentially, with one process running for a duration, being suspended, and then allowing another process to take its turn.&lt;/p&gt;

&lt;p&gt;This process management strategy is characteristic of concurrency and does not entail true parallelism (which is not possible on a single-core system). Tasks appear to run concurrently due to fast context switching, although they actually execute sequentially on a single core.&lt;/p&gt;

&lt;p&gt;To identify processes for potential swapping, Erlang introduces the concept of "reductions". In Erlang, a reduction represents a unit of work performed by BEAM, encompassing fundamental operations such as function application, arithmetic calculations, or message passing. The scheduler keeps track of the reductions executed by each process, preempting a process when it reaches a certain reduction count, thereby allowing another process to run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reductions
&lt;/h3&gt;

&lt;p&gt;The idea of "reduction" in Erlang is inherited from its Prolog ancestry. In Prolog, every execution step is termed a goal-reduction, involving breaking down a logic problem into individual components and solving each part accordingly.&lt;/p&gt;

&lt;p&gt;To promote fairness among processes, Erlang's preemptive scheduling relies on reductions rather than time slices. If a process exhausts its allocated reductions, it can be preempted, even if its execution isn't complete. This approach prevents a single process from monopolizing the CPU for an extended period, fostering fairness among concurrent processes. By using reductions as the foundation for preemption, Erlang mitigates the risk of processes starving for CPU time. This design ensures that every process, irrespective of its workload, is periodically allowed to execute.&lt;/p&gt;

&lt;p&gt;Moreover, reductions are flexibly applied based on the type of operation a process is performing. For example, certain operations, such as I/O operations, may consume various reductions. This adaptability allows the scheduler to effectively handle different types of operations.&lt;/p&gt;

&lt;p&gt;In other languages, traditional blocking I/O operations can lead to inefficient resource utilization, as threads might be blocked while waiting for I/O to complete. Erlang's asynchronous and non-blocking I/O model allows processes to continue executing other tasks while waiting for I/O operations to complete. This minimizes the impact of blocking operations on overall system performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Priority
&lt;/h3&gt;

&lt;p&gt;Each process in Erlang can have a &lt;code&gt;priority&lt;/code&gt; value that decides how often the scheduler will execute that process. A process priority can be set using the &lt;code&gt;Process.flag/2&lt;/code&gt; (or &lt;a href="https://www.erlang.org/doc/man/erlang.html#process_flag-2"&gt;process_flag/2&lt;/a&gt; on Erlang) function call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:priority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:high&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:link&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;There are 4 priority levels: &lt;code&gt;low&lt;/code&gt;, &lt;code&gt;normal&lt;/code&gt;, &lt;code&gt;high&lt;/code&gt;, and &lt;code&gt;max&lt;/code&gt;. &lt;code&gt;max&lt;/code&gt; is reserved for internal use in Erlang and should not be used in application code. Processes on each priority level get a separate run queue and are scheduled in the normal round-robin fashion as described above.&lt;/p&gt;

&lt;p&gt;Except &lt;code&gt;low&lt;/code&gt; and &lt;code&gt;normal&lt;/code&gt;, Erlang executes the processes of each priority queue exclusively. This means that if there is a process in the &lt;code&gt;max&lt;/code&gt; priority queue, only &lt;code&gt;max&lt;/code&gt; priority processes will be executed, and all other processes will be blocked. Similarly, if there is a process in the &lt;code&gt;high&lt;/code&gt; priority queue, &lt;code&gt;low&lt;/code&gt; and &lt;code&gt;normal&lt;/code&gt; processes will not be executed until all processes from the &lt;code&gt;high&lt;/code&gt; priority queue are executed (or are non-runnable).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;low&lt;/code&gt; and &lt;code&gt;normal&lt;/code&gt; queues behave slightly differently — the processes inside both queues are interleaved such that &lt;code&gt;normal&lt;/code&gt; priority processes are executed more often than &lt;code&gt;low&lt;/code&gt; priority ones, but &lt;code&gt;normal&lt;/code&gt; processes &lt;strong&gt;do not&lt;/strong&gt; block the execution of &lt;code&gt;low&lt;/code&gt; priority processes.&lt;/p&gt;

&lt;p&gt;Due to the blocking behavior of &lt;code&gt;high&lt;/code&gt; priority processes, they should be used very rarely and only for short-lived tasks. Overusing &lt;code&gt;high&lt;/code&gt; priority processes is bound to lead to outages and affect the responsiveness of an application, as all other regular OTP processes run on &lt;code&gt;normal&lt;/code&gt; priority.&lt;/p&gt;

&lt;p&gt;Another point that's important here is that Erlang places no restrictions on communication between different priority levels of processes. So a high-priority process can wait for a message from a lower-priority process. This is allowed, but will effectively lower the priority of the high-priority process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running on Multiple Cores
&lt;/h2&gt;

&lt;p&gt;Up to this point, we've explored how the scheduler orchestrates processes within a single-core system. However, in a multi-core environment, where additional resources are available for parallel processing, the Erlang VM creates a dedicated "run queue" for each available core.&lt;br&gt;
This enables true parallelism, as multiple processes can run simultaneously (one on each available core). Within each run queue, all processes adhere to the preemptive scheduling mechanism we discussed earlier.&lt;/p&gt;

&lt;p&gt;Typically, Erlang processes share the same run queue as their parent process, and a work-stealing algorithm may come into play to ensure load balancing. For instance, if there are two cores in the system and one consistently handles busy processes while the other remains relatively idle, the Erlang schedulers on both cores engage in periodic communication. This communication facilitates the movement of processes from the heavily loaded core to the less busy one, ensuring a more equitable distribution of workloads across both cores.&lt;/p&gt;

&lt;p&gt;In the broader context, beyond Erlang's internal run queues, the operating system plays a role in managing the scheduling of threads onto OS-level cores. This implies that processes not only experience swapping within the Erlang run queue but also may undergo a complete context switch or be moved to a different core at the OS level.&lt;/p&gt;

&lt;p&gt;Note that, because of the concept of work-stealing inside the Erlang VM, it is usually beneficial to run a single Erlang application instance on multiple cores rather than running separate instances of the same application on different cores of the same machine. In a single instance, the schedulers dedicated to each core can better communicate between them to share the load equitably compared to a multi-node cluster where schedulers cannot share process load (even if all of that cluster's nodes are on the same physical machine).&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance and Optimization
&lt;/h2&gt;

&lt;p&gt;Erlang's scheduler takes out most of the complexities involved in building a concurrent system. It automatically frees the developer from having to think about things like lock contention, thread overhead, and load balancing by handling these issues out of the box with its preemptive scheduling algorithm.&lt;/p&gt;

&lt;p&gt;Erlang provides various scheduler-related parameters that can be tuned for optimal performance. Parameters like &lt;a href="https://www.erlang.org/doc/man/erl#+S"&gt;+S&lt;/a&gt; (scheduler count) and &lt;a href="https://www.erlang.org/doc/man/erl#+P"&gt;+P&lt;/a&gt; (maximum number of processes) allow you to configure the number of scheduler threads and processes.&lt;/p&gt;

&lt;p&gt;For example, you can start Erlang with &lt;code&gt;erl +S Schedulers:SchedulerOnline&lt;/code&gt; to control the number of scheduler threads. By default, Erlang uses the number of CPU cores to identify these values automatically. Note that while both &lt;code&gt;Scheduler&lt;/code&gt; and &lt;code&gt;SchedulerOnline&lt;/code&gt; accept values up to 1024, starting more schedulers than the number of CPU cores does not have any positive benefits for an app.&lt;/p&gt;

&lt;p&gt;Another possible step to fine-tune performance is to control the priorities of processes, as we've discussed. It is indeed possible to execute certain high-priority tasks in Erlang.&lt;/p&gt;

&lt;p&gt;Nevertheless, this comes with an inherent risk of potentially rendering a system unresponsive and increasing latency, as processes with a high priority may block all other normal/low-priority processes. Conversely, marking tasks identified as intentionally low priority can be advantageous to prioritize other processes above them.&lt;/p&gt;

&lt;p&gt;So be careful and use your judgment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post, we've seen that the Erlang scheduler stands as a cornerstone in Erlang's architecture, fostering fault tolerance, concurrency, and adaptability. Its preemptive and dynamic nature equips developers to build resilient, highly concurrent systems capable of handling failures and utilizing resources optimally. Understanding the intricacies of the Erlang scheduler can empower you to craft scalable and robust distributed applications.&lt;/p&gt;

&lt;p&gt;If you are interested in learning more about the scheduler, I recommend checking out the &lt;a href="https://blog.stenmans.org/theBeamBook/#CH-Scheduling"&gt;Scheduling chapter in The BEAM Book&lt;/a&gt; and &lt;a href="https://www.erlang.org/docs/23/efficiency_guide/processes"&gt;Processes from the Erlang Efficiency Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, &lt;a href="https://dev.to/elixir-alchemy"&gt;subscribe to our Elixir Alchemy newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.P.S. &lt;a href="https://www.appsignal.com/elixir/erlang-monitoring"&gt;AppSignal has an integration for Erlang — check it out&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>erlang</category>
    </item>
    <item>
      <title>A Deep Dive into Mutations with Absinthe</title>
      <dc:creator>Sapan Diwakar</dc:creator>
      <pubDate>Tue, 11 Jul 2023 14:43:31 +0000</pubDate>
      <link>https://dev.to/appsignal/a-deep-dive-into-mutations-with-absinthe-5ame</link>
      <guid>https://dev.to/appsignal/a-deep-dive-into-mutations-with-absinthe-5ame</guid>
      <description>&lt;p&gt;In the last two posts of this series, we saw how easy it is to integrate Absinthe into your app to provide a GraphQL API for querying data. We also shared some useful tips for building and maintaining large schemas and optimizing queries.&lt;/p&gt;

&lt;p&gt;This post will show how we can provide an API to create GraphQL mutations with Absinthe for Elixir.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  A Note on Queries Vs. Mutations in GraphQL
&lt;/h2&gt;

&lt;p&gt;GraphQL places a clear distinction between queries and mutations. Technically, we can implement any query to write data to the server. But &lt;a href="https://graphql.org/learn/queries/#mutations"&gt;GraphQL convention&lt;/a&gt; clearly separates them into different top-level &lt;code&gt;mutation&lt;/code&gt; types.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mutations in GraphQL
&lt;/h2&gt;

&lt;p&gt;First, let’s see what a typical mutation looks like:&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;mutation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CreatePost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PostCreateInput&lt;/span&gt;&lt;span class="p"&gt;!)&lt;/span&gt;&lt;span class="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;createPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="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;post&lt;/span&gt;&lt;span class="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="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;errors&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a mutation to create a new post. It accepts a variable named &lt;code&gt;post&lt;/code&gt; of type &lt;code&gt;PostCreateInput&lt;/code&gt; (which is required because &lt;code&gt;!&lt;/code&gt; follows the type name). This variable is passed into the &lt;code&gt;createPost&lt;/code&gt; field inside the top-level &lt;code&gt;mutation&lt;/code&gt; type.&lt;/p&gt;

&lt;p&gt;The result of that mutation is a &lt;code&gt;post&lt;/code&gt; (which can have further selections) and an &lt;code&gt;errors&lt;/code&gt; array. Note that we have named the mutation &lt;code&gt;CreatePost&lt;/code&gt; but that is just a client-side identifier. Here is what a sample response from this mutation looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"createPost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="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;"post"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="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;"1"&lt;/span&gt;&lt;span class="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;"errors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="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;
  
  
  Input Objects with Absinthe for Elixir
&lt;/h2&gt;

&lt;p&gt;Let’s see how we can implement this with Absinthe. The first thing we need is an &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Type.InputObject.html"&gt;&lt;code&gt;input_object&lt;/code&gt;&lt;/a&gt; that can be used as a variable for the mutation. Let’s create one now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;

  &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;:post_state_enum&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ss"&gt;:active&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ss"&gt;:draft&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;input_object&lt;/span&gt; &lt;span class="ss"&gt;:post_create_input&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;non_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:author_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;non_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;non_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:post_state_enum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default_value:&lt;/span&gt; &lt;span class="ss"&gt;:draft&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we use the &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Type.Enum.html"&gt;&lt;code&gt;enum&lt;/code&gt;&lt;/a&gt; macro to create an enum named &lt;code&gt;post_state_enum&lt;/code&gt; with two possible values — &lt;code&gt;draft&lt;/code&gt; and &lt;code&gt;active&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We then use the &lt;code&gt;input_object&lt;/code&gt; macro to define an input object named &lt;code&gt;post_create_input&lt;/code&gt; which has four fields. Note that the definition of fields changes slightly from what we would use in an output type. For example, for fields inside an input object, we can add a &lt;code&gt;default_value&lt;/code&gt; to a non-required field. Absinthe will fill the field in if the user doesn’t provide a value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining Mutations in Absinthe
&lt;/h2&gt;

&lt;p&gt;Next, let’s define our mutation type in the schema. All individual mutations (like &lt;code&gt;createPost&lt;/code&gt;) will be fields inside this mutation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;

  &lt;span class="n"&gt;mutation&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:create_post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;non_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post_mutation_result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;non_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post_create_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we use the &lt;code&gt;mutation..do..end&lt;/code&gt; block to define the base mutation type. And inside it, we define a field named &lt;code&gt;create_post&lt;/code&gt;. This takes a single argument named &lt;code&gt;post&lt;/code&gt; of the &lt;code&gt;post_create_input&lt;/code&gt; type defined above. The result of this field is of type &lt;code&gt;post_mutation_result&lt;/code&gt;. We haven’t defined it yet, so let’s do that now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="ss"&gt;:post_mutation_result&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;list_of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;non_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now on to the interesting part — actually performing the mutation operation. This is similar to what we already did for queries. We will define a resolver to handle it.&lt;/p&gt;



&lt;p&gt;If you remember from &lt;a href="https://blog.appsignal.com/2023/06/06/absinthe-for-large-elixir-applications.html"&gt;our previous post&lt;/a&gt;, a 3-arity resolver receives the parent object, the attributes, and an &lt;code&gt;Absinthe.Resolution&lt;/code&gt; struct. Let’s define that first to create the post:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resolvers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PostResolver&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;post:&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;_resolution&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&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="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;post:&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;changeset&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="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;errors:&lt;/span&gt; &lt;span class="n"&gt;translate_changeset_errors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;)}}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the resolver, we use the &lt;code&gt;post&lt;/code&gt; attributes to create a post. If that is successful, we respond with the post object (the result map should correspond to the mutation's result type — &lt;code&gt;post_mutation_result&lt;/code&gt;, in our case). On an error, we respond with the &lt;code&gt;errors&lt;/code&gt;. Note that &lt;a href="https://gist.github.com/sapandiwakar/fd1642a484826f2e02208d02c014fb55"&gt;&lt;code&gt;translate_changeset_errors&lt;/code&gt;&lt;/a&gt; is a custom helper function that translates &lt;code&gt;Ecto.Changeset&lt;/code&gt; errors into an array of strings.&lt;/p&gt;

&lt;p&gt;With the resolver in place, we can now plug it into our schema to create a post:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;

  &lt;span class="n"&gt;mutation&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:create_post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:post_mutation_result&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;non_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post_create_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resolvers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PostResolver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_post&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when we execute the above mutation against our schema, Absinthe will call the &lt;code&gt;create_post&lt;/code&gt; function. The return value from the resolver's &lt;code&gt;{:ok, value}&lt;/code&gt; tuple will then be used as the mutation's result and returned to the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authorization in Your Elixir App with GraphQL and Absinthe
&lt;/h2&gt;

&lt;p&gt;Up until now, we have been discussing everything as if we're using an open public API. But in most apps, this is not how things work. Often, certain APIs are only available to logged-in users, and this is even more important in the case of mutations. Let’s see how we can add authorization to our API.&lt;/p&gt;

&lt;p&gt;First, we will need to identify the users making requests. In the &lt;a href="https://blog.appsignal.com/2023/05/16/an-introduction-to-absinthe-for-elixir.html#setting-up-your-elixir-app-with-graphql-and-absinthe"&gt;first post of this series&lt;/a&gt;, we used &lt;code&gt;Absinthe.Plug&lt;/code&gt; to handle all the requests coming into the &lt;code&gt;/api&lt;/code&gt; endpoint using the &lt;code&gt;MyAppWeb.Schema&lt;/code&gt; schema. This doesn’t handle any user authorization for us.&lt;/p&gt;

&lt;p&gt;Absinthe provides a &lt;a href="https://hexdocs.pm/absinthe/context-and-authentication.html"&gt;context&lt;/a&gt; concept containing shared information that might be useful for all queries and mutations. This is passed to all resolver functions that accept an &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Resolution.html"&gt;&lt;code&gt;Absinthe.Resolution&lt;/code&gt;&lt;/a&gt; struct inside the &lt;code&gt;context&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;Let’s fix that first to identify users &lt;em&gt;before&lt;/em&gt; a request is forwarded to Absinthe.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Example Using Absinthe Context
&lt;/h2&gt;

&lt;p&gt;Update the router in your app to execute an additional plug before forwarding a request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Router&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:router&lt;/span&gt;

  &lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="ss"&gt;:api&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="ss"&gt;:accepts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"json"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="ss"&gt;:graphql&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Context&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;pipe_through&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:graphql&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;forward&lt;/span&gt; &lt;span class="s2"&gt;"/api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;schema:&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've added a new &lt;code&gt;MyAppWeb.Schema.Context&lt;/code&gt; plug to the requests. Let’s implement it to put a user in the Absinthe context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Context&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@behaviour&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;

  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Conn&lt;/span&gt;

  &lt;span class="c1"&gt;#...&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;context:&lt;/span&gt; &lt;span class="n"&gt;absinthe_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;absinthe_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;conn:&lt;/span&gt; &lt;span class="n"&gt;fetch_query_params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;put_user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; I have intentionally left out some app-specific parts of the plug. Here is the &lt;a href="https://gist.github.com/sapandiwakar/cd6b988ae8f7d59db579f228fad63210"&gt;full code of that module&lt;/a&gt; if you are interested.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;An interesting part of the code in the above code block is the use of &lt;code&gt;Absinthe.Plug.put_options/2&lt;/code&gt; to set values that &lt;code&gt;Absinthe.Plug&lt;/code&gt; will pass to Absinthe when executing the query/mutation. This is similar to how we use &lt;a href="https://hexdocs.pm/plug/Plug.Conn.html#assign/3"&gt;&lt;code&gt;Plug.Conn.assign&lt;/code&gt;&lt;/a&gt; to assign something on the conn.&lt;/p&gt;

&lt;p&gt;Internally, &lt;code&gt;Absinthe.Plug&lt;/code&gt; puts a private assign inside the &lt;code&gt;conn&lt;/code&gt; and passes it as the third parameter to &lt;a href="https://hexdocs.pm/absinthe/Absinthe.html#run/3"&gt;&lt;code&gt;Absinthe.run/3&lt;/code&gt;&lt;/a&gt; when executing the document. The &lt;code&gt;context&lt;/code&gt; option we use above is a special value that Absinthe will then pass to all resolvers inside the &lt;code&gt;Absinthe.Resolution&lt;/code&gt; struct.&lt;/p&gt;

&lt;p&gt;Now let’s update our &lt;code&gt;create_post/3&lt;/code&gt; resolver function to use this context and deny the request if the user isn’t present.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resolvers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PostResolver&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;_parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;post:&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resolution&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;context:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;user:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"unauthorized"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;post:&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resolution&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&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="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;post:&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;changeset&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="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;errors:&lt;/span&gt; &lt;span class="n"&gt;translate_changeset_errors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;)}}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a resolver function returns an error tuple, Absinthe will not resolve that field and adds an error to the response.&lt;/p&gt;

&lt;p&gt;This is just one example of how to use Absinthe context. There are many more advanced potential use cases. For example, you might want to automatically set a user’s id as the post author, instead of accepting an &lt;code&gt;author_id&lt;/code&gt; in the &lt;code&gt;post_create_input&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Middleware in Absinthe for Elixir
&lt;/h2&gt;

&lt;p&gt;We have used context to authorize a user during resolution. But if we have many fields to handle, this soon becomes too cumbersome. Absinthe provides middleware to handle such cases.&lt;/p&gt;

&lt;p&gt;For example, let's assume that several fields require a user to be present when performing an operation. Instead of updating the resolver function for all those fields, wouldn’t it be better if we could just write &lt;code&gt;middleware MyAppWeb.Schema.RequireUser&lt;/code&gt; in the field definition?&lt;/p&gt;

&lt;p&gt;We can do that using the &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Middleware.html#module-the-middleware-2-macro"&gt;&lt;code&gt;middleware/2&lt;/code&gt; macro&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;

  &lt;span class="n"&gt;mutation&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:create_post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:post_mutation_result&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;non_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post_create_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;middleware&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;RequireUser&lt;/span&gt;

      &lt;span class="n"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resolvers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PostResolver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_post&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The argument to &lt;code&gt;middleware&lt;/code&gt; is a module that implements the &lt;code&gt;Absinthe.Middleware&lt;/code&gt; behaviour. The module should start a &lt;code&gt;call/2&lt;/code&gt; function that receives an &lt;code&gt;Absinthe.Resolution&lt;/code&gt; struct as the first argument.&lt;/p&gt;

&lt;p&gt;The second argument is whatever is passed to the &lt;code&gt;middleware/2&lt;/code&gt; macro. We don’t pass anything above, so it's nil by default. The &lt;code&gt;call/2&lt;/code&gt; function should return an updated &lt;code&gt;Absinthe.Resolution&lt;/code&gt; struct.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implement &lt;code&gt;RequireUser&lt;/code&gt; Middleware
&lt;/h3&gt;

&lt;p&gt;Let’s implement the &lt;code&gt;RequireUser&lt;/code&gt; middleware now to stop requests if a user is not present:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;SpendraWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;RequireUser&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@behaviour&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Middleware&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(%&lt;/span&gt;&lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resolution&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;state:&lt;/span&gt; &lt;span class="ss"&gt;:resolved&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resolution&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_config&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;resolution&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(%&lt;/span&gt;&lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resolution&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;context:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;user:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resolution&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resolution&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;resolution&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"You must be logged in to access this API"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(%&lt;/span&gt;&lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resolution&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resolution&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_config&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;resolution&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The middle clause is the most important. Here, we receive a context with a &lt;code&gt;nil&lt;/code&gt; value for the user. We use &lt;code&gt;Absinthe.Resolution.put_result&lt;/code&gt; to mark that the resolution is now complete. It does two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Marks the &lt;code&gt;state&lt;/code&gt; of the resolution as &lt;code&gt;resolved&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Puts the specified value as the resolution result. Here we use an error tuple to signal that this is an erroneous response.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We also have a special clause at the beginning that checks for the resolution's existing state. If you are using multiple middlewares in your app, this is important. It avoids a middleware running on a resolution that has already been resolved.&lt;/p&gt;

&lt;p&gt;Finally, if a resolution's state is not already &lt;code&gt;resolved&lt;/code&gt; and we have a user in the context, we just return the original resolution struct without any modifications. This allows execution to proceed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chaining Middleware
&lt;/h3&gt;

&lt;p&gt;The great thing about middleware is that it can be chained. For example, we might have several user roles and only want authors to be able to create posts. We can use multiple middleware clauses that each perform a single task before the final resolution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:create_post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:post_mutation_result&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;middleware&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;RequireUser&lt;/span&gt;

  &lt;span class="n"&gt;middleware&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;RequireAuthor&lt;/span&gt;

  &lt;span class="n"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resolvers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PostResolver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_post&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In fact, &lt;code&gt;resolve&lt;/code&gt; itself is just a middleware which roughly translates to &lt;code&gt;middleware Absinthe.Resolution, unquote(function_ast)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Another important point about middlewares is that they are executed in the order they are defined in a field. So in the above example, first &lt;code&gt;RequireUser&lt;/code&gt; will be executed, followed by &lt;code&gt;RequireAuthor&lt;/code&gt; and, finally, the resolver.&lt;/p&gt;

&lt;p&gt;It is also possible to use middleware after a resolve call — for example, to handle resolution errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;In this post, we created GraphQL mutations with Absinthe. We also added a custom plug to put shared information in an Absinthe context and defined middleware to handle common tasks like authorization.&lt;/p&gt;

&lt;p&gt;In the next and final part of this series, we will discuss advanced GraphQL use cases like subscriptions (including how to implement and deliver subscriptions over a WebSocket connection).&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, &lt;a href="https://dev.to/elixir-alchemy"&gt;subscribe to our Elixir Alchemy newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>absinthe</category>
    </item>
    <item>
      <title>Absinthe for Large Elixir Applications</title>
      <dc:creator>Sapan Diwakar</dc:creator>
      <pubDate>Tue, 13 Jun 2023 10:32:52 +0000</pubDate>
      <link>https://dev.to/appsignal/absinthe-for-large-elixir-applications-4kj4</link>
      <guid>https://dev.to/appsignal/absinthe-for-large-elixir-applications-4kj4</guid>
      <description>&lt;p&gt;In our introduction to Absinthe, we covered the basics of Absinthe and GraphQL for an Elixir application.&lt;/p&gt;

&lt;p&gt;Now we'll dig deeper and see how we can customize Absinthe for larger-scale Elixir applications.&lt;/p&gt;

&lt;p&gt;Let's get going!&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining Resolvers for a Large Elixir App
&lt;/h2&gt;

&lt;p&gt;We've seen that creating a query and returning a result in Absinthe is really easy. But if you have big resolution logic, the schema can soon get very heavy. It's common practice to define resolver functions in a separate module for large apps using Absinthe.&lt;/p&gt;

&lt;p&gt;First, create a module that defines functions acting as field resolvers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/my_app_web/schema/resolvers/blog.ex&lt;/span&gt;
&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resolvers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(%{&lt;/span&gt;&lt;span class="ss"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;_resolution&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# complex resolution logic here&lt;/span&gt;
    &lt;span class="c1"&gt;# post = ...&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, update your schema to use this resolver:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;

  &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;non_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resolvers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This also allows you to unit-test that resolution logic separately from your GraphQL integration tests. We will discuss more about testing schemas later on in this series.&lt;/p&gt;

&lt;p&gt;The resolvers additionally help you to reduce code duplication at several places in your schema. For example, let's say you need to expose a field under a different name from what is defined on the struct.&lt;/p&gt;

&lt;p&gt;We can build the object like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:published_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:datetime&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;resolve&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_resolution&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inserted_at&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you find yourself doing that at several places in the schema, it might be better to create a resolver:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resolvers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@doc&lt;/span&gt; &lt;span class="sd"&gt;"""
  Returns a resolver function that returns the value at `field` from `parent`
  """&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_resolution&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then use it in your object like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:published_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;resolve:&lt;/span&gt; &lt;span class="no"&gt;Resolvers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:inserted_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Avoiding N+1 Queries in Elixir
&lt;/h2&gt;

&lt;p&gt;Let’s get back to our schema, but with a query that returns a list of posts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;

  &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;non_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="ss"&gt;resolve:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;Resolvers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_author&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;non_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list_of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
      &lt;span class="ss"&gt;resolve:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;Resolvers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_posts&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the full &lt;a href="https://gist.github.com/sapandiwakar/2585851aa844b9cbf4f7fb812f049ca5#file-blog-ex"&gt;&lt;code&gt;Resolvers.Blog&lt;/code&gt;&lt;/a&gt; if you are interested.&lt;/p&gt;

&lt;p&gt;Now, perform a query that includes the author of each post:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="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="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="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="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="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;When this query is executed, we first fetch all posts from the database. Then we fetch the author by their id for each post — that's not very efficient.&lt;/p&gt;

&lt;p&gt;But that’s an easy problem to solve. We can just preload the authors in &lt;code&gt;Resolvers.Blog.list_posts/2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, what if someone makes a query that doesn’t need an author? We will still be unnecessarily fetching all authors, defeating the whole purpose of having a composable query like this.&lt;/p&gt;

&lt;p&gt;One possible solution is to be smart when resolving the initial list of posts and load authors only when the user has selected the &lt;code&gt;author&lt;/code&gt; field in the query. If you remember, we get an &lt;code&gt;Absinthe.Resolution&lt;/code&gt; struct as the last argument to the resolver function.&lt;br&gt;
&lt;code&gt;resolution.definition.selections&lt;/code&gt; contains all the selected fields (&lt;code&gt;Absinthe.Blueprint.Document.Field&lt;/code&gt; structs).&lt;/p&gt;

&lt;p&gt;We can check if the &lt;code&gt;author&lt;/code&gt; was selected here, and preload it right there:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Resolvers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;list_posts&lt;/span&gt;&lt;span class="p"&gt;(%{},&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resolution&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_posts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;definition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selections&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;any?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That works, but if you have many fields (or several nested levels), it can soon become too cumbersome. This is where &lt;a href="https://hexdocs.pm/absinthe/dataloader.html"&gt;&lt;code&gt;dataloader&lt;/code&gt;&lt;/a&gt; can help.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dataloader to the Rescue!
&lt;/h2&gt;

&lt;p&gt;Dataloader provides an efficient way to load linked data by batching queries. It does this by first performing a full pass through the query resolution phase, collecting all the ids of objects to be loaded. Dataloader then loads them all in one go instead of making a request to the database for each of them separately.&lt;/p&gt;

&lt;p&gt;It’s a separate dependency, so first add it to your &lt;code&gt;mix.exs&lt;/code&gt; inside &lt;code&gt;deps&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You then need a one-time setup in your schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;

  &lt;span class="c1"&gt;# ... schema definition here ...&lt;/span&gt;

  &lt;span class="c1"&gt;# Add dataloader to the context&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="no"&gt;Dataloader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Dataloader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="c1"&gt;# ... add other sources here ...&lt;/span&gt;

    &lt;span class="no"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:loader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Add dataloader to the plugins&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Dataloader&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can add custom fields to the context through the &lt;code&gt;context&lt;/code&gt; method in the schema. The context's value is available to all steps in the GraphQL request cycle (e.g., inside resolvers or middleware) inside the &lt;code&gt;%Absinthe.Resolution{}&lt;/code&gt; struct. Context is also where we usually store user details if we authenticate users.&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href="https://hexdocs.pm/absinthe/context-and-authentication.html"&gt;Absinthe Context and Authentication&lt;/a&gt; guide to explore this further.&lt;/p&gt;

&lt;p&gt;In addition, we add &lt;code&gt;dataloader&lt;/code&gt; to &lt;code&gt;plugins&lt;/code&gt; using the &lt;code&gt;plugins/0&lt;/code&gt; callback on the schema. This allows &lt;code&gt;dataloader&lt;/code&gt; to hook into the resolution pipeline. If you want to learn more about plugins, read the &lt;a href="https://hexdocs.pm/absinthe/middleware-and-plugins.html"&gt;Writing Middleware and Plugins guide for Absinthe&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;DataLoader.add_source/3&lt;/code&gt; expects the name of the data source as its second argument and a module implementing the &lt;code&gt;Dataloader.Source&lt;/code&gt; protocol. Dataloader supports an &lt;a href="https://hexdocs.pm/dataloader/Dataloader.Ecto.html"&gt;Ecto-based data source&lt;/a&gt; out of the box, which is what we will need for our example.&lt;/p&gt;

&lt;p&gt;Let's update our Phoenix context (&lt;code&gt;Blog&lt;/code&gt; in our example) to return the Ecto data source from &lt;code&gt;data/0&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# my_app/blog.ex&lt;/span&gt;
&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Dataloader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;query:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queryable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_params&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;queryable&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It isn't really documented very well, but if you are feeling adventurous, most of the data-loading magic lies inside the &lt;a href="https://github.com/absinthe-graphql/dataloader/blob/master/lib/dataloader/ecto.ex"&gt;Ecto data source&lt;/a&gt;.&lt;br&gt;
It uses the &lt;code&gt;query/2&lt;/code&gt; function we passed to generate a base query when it needs to load some data.&lt;/p&gt;

&lt;p&gt;For example, if we try to load &lt;code&gt;Author&lt;/code&gt; with ids 1 through 5, it will make a single query like &lt;code&gt;from a in Author, where a.id in [1, 2, 3, 4, 5]&lt;/code&gt;, instead of making 5 different queries.&lt;/p&gt;

&lt;p&gt;This function is our opportunity to filter results and return a query that will finally be used to fetch the items.&lt;br&gt;
For now, we just return the queryable as it is, which means that we don’t need any special filtering.&lt;/p&gt;
&lt;h2&gt;
  
  
  Use Dataloader Inside the Absinthe Schema
&lt;/h2&gt;

&lt;p&gt;To use Dataloader inside our schema, we must now modify our object &lt;code&gt;post&lt;/code&gt; to use the &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Resolution.Helpers.html#dataloader/3"&gt;&lt;code&gt;dataloader&lt;/code&gt; helper&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resolution&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Helpers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;dataloader:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# ... other fields&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;non_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="ss"&gt;resolve:&lt;/span&gt; &lt;span class="n"&gt;dataloader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The first argument to &lt;code&gt;dataloader/2&lt;/code&gt; is the source name (as registered in the schema).&lt;/li&gt;
&lt;li&gt;The next argument is the name of the field in the parent object (&lt;code&gt;author&lt;/code&gt; field in parent object &lt;code&gt;post&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that the data source should be the one that will resolve the field. So if the post belongs to an author with type &lt;code&gt;MyApp.Accounts.User&lt;/code&gt;, you must use &lt;code&gt;dataloader(MyApp.Accounts, :author)&lt;/code&gt; as the resolver and support &lt;code&gt;data/0&lt;/code&gt; and &lt;code&gt;query/2&lt;/code&gt; inside the &lt;code&gt;Accounts&lt;/code&gt; context.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/sapandiwakar/067b283d14905f0c9dcafb378c783cd7"&gt;Here is the full code&lt;/a&gt; if you are interested. I know it's a lot to take in, so let's go through the execution of the query above.&lt;/p&gt;

&lt;p&gt;Absinthe first invokes our posts resolver (&lt;code&gt;Resolvers.Blog.list_posts/2&lt;/code&gt;), and returns the list of posts. Absinthe then checks for the fields it needs inside &lt;code&gt;post&lt;/code&gt; and encounters a selection for &lt;code&gt;author&lt;/code&gt;.&lt;br&gt;
This is where dataloader takes over:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It collects the &lt;code&gt;author_id&lt;/code&gt; for all posts that will be returned in our result. Let's say we need to load authors &lt;code&gt;[1, 2, 3, 4, 5]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It then calls &lt;code&gt;MyApp.Blog.query(Author, %{})&lt;/code&gt; to get the initial query. In our example, we are simply returning &lt;code&gt;Author&lt;/code&gt; (but in a real application, this could be filtered by business case — for example, if we need only authors that have an active account, we could return &lt;code&gt;where(queryable, [a], a.active)&lt;/code&gt;, instead of just returning &lt;code&gt;queryable&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Finally, it loads the required ids from the above query — &lt;code&gt;from a in Author, where a.id in [1, 2, 3, 4, 5]&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As you can see, we only performed a single query instead of 5 different ones.&lt;/p&gt;

&lt;p&gt;Nesting also works out of the box, so if each author has an &lt;code&gt;organization&lt;/code&gt; field and we select that in the query, Dataloader will load all organizations in one batch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Organizing Your Absinthe Schema with Imports
&lt;/h2&gt;

&lt;p&gt;As your schema starts growing, you will soon notice that putting all type definitions and query fields in the same file is not sensible. This is where &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Schema.Notation.html#import_types/2"&gt;&lt;code&gt;import_types&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Schema.Notation.html#import_fields/2"&gt;&lt;code&gt;import_fields&lt;/code&gt;&lt;/a&gt; come into play.&lt;/p&gt;

&lt;p&gt;The level to split at depends on the size of your API and your application, but it is a common practice to split by business context (the same as your Phoenix contexts).&lt;/p&gt;

&lt;p&gt;Here is a structure that works well.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a module that contains queries (and another for mutations) related to each model:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="c1"&gt;# lib/my_app_web/schema/types/blog/post/queries.ex&lt;/span&gt;
   &lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Queries&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Notation&lt;/span&gt;

     &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="ss"&gt;:post_queries&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;list_of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="ss"&gt;resolve:&lt;/span&gt; &lt;span class="no"&gt;Resolvers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
       &lt;span class="c1"&gt;# ... all queries related to post here&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create a module for types related to each model. Also import the query and mutation types here.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="c1"&gt;# lib/my_app_web/schema/types/blog/post.ex&lt;/span&gt;
   &lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Notation&lt;/span&gt;

     &lt;span class="n"&gt;import_types&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Queries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="n"&gt;import_types&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Mutations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

     &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;not_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="c1"&gt;# ...&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;

     &lt;span class="c1"&gt;# all types related to blog here&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create a module for types related to each context. This should only import the types from model-specific modules.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="c1"&gt;# lib/my_app_web/schema/types/blog.ex&lt;/span&gt;
   &lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Notation&lt;/span&gt;

     &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Types&lt;/span&gt;

     &lt;span class="n"&gt;import_types&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="c1"&gt;# ... import all types related to blog here&lt;/span&gt;

     &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="ss"&gt;:blog_queries&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="n"&gt;import_fields&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post_queries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="c1"&gt;# ... import all queries related to blog here&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;

     &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="ss"&gt;:blog_mutations&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="n"&gt;import_fields&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post_mutations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="c1"&gt;# ... import all queries related to blog here&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Finally, import the context-specific types to your schema.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="c1"&gt;# lib/my_app_web/schema.ex&lt;/span&gt;
   &lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;

     &lt;span class="n"&gt;import_types&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

     &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="n"&gt;import_fields&lt;/span&gt; &lt;span class="ss"&gt;:blog_queries&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;

     &lt;span class="n"&gt;mutation&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="n"&gt;import_fields&lt;/span&gt; &lt;span class="ss"&gt;:blog_mutations&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, your schema remains very clear, declaring only what it imports. All specific queries are further down the pipeline.&lt;/p&gt;

&lt;p&gt;This may seem like overkill for our small API example.&lt;br&gt;
But we have been using it in production for a large app with several contexts, and it’s been a boon to keep our schema manageable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;In this post, the second part of our series on Absinthe, we customized Absinthe for an Elixir application pushing a lot of data. We started by defining resolvers for a big Elixir application and covered how to avoid N+1 queries.&lt;/p&gt;

&lt;p&gt;Finally, we dived into Dataloader (which helps to load linked data) in some detail and explored how to organize our Absinthe schema.&lt;/p&gt;

&lt;p&gt;Next up, we'll look at creating mutations and subscriptions with Absinthe.&lt;/p&gt;

&lt;p&gt;Until then, happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, &lt;a href="https://dev.to/elixir-alchemy"&gt;subscribe to our Elixir Alchemy newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
    </item>
    <item>
      <title>An Introduction to Absinthe</title>
      <dc:creator>Sapan Diwakar</dc:creator>
      <pubDate>Tue, 23 May 2023 11:41:57 +0000</pubDate>
      <link>https://dev.to/appsignal/an-introduction-to-absinthe-4b49</link>
      <guid>https://dev.to/appsignal/an-introduction-to-absinthe-4b49</guid>
      <description>&lt;p&gt;Absinthe is a toolkit for building a GraphQL API with Elixir. It has a declarative syntax that fits really well with Elixir’s idiomatic style.&lt;/p&gt;

&lt;p&gt;In today’s post — the first of a series on Absinthe — we will explore how you can use Absinthe to create a GraphQL API.&lt;/p&gt;

&lt;p&gt;But before we jump into Absinthe, let’s take a brief look at GraphQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL
&lt;/h2&gt;

&lt;p&gt;GraphQL is a query language that allows declarative data fetching. A client can ask for exactly what they want, and only that data is returned.&lt;/p&gt;

&lt;p&gt;Instead of having multiple endpoints like a REST API, a GraphQL API usually provides a single endpoint that can perform different operations based on the request body.&lt;/p&gt;

&lt;h3&gt;
  
  
  GraphQL Schema
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Schema&lt;/code&gt; forms the core of a GraphQL API. In GraphQL, everything is strongly typed, and the schema contains information about the API's capabilities.&lt;/p&gt;

&lt;p&gt;Let's take an example of a blog application. The schema can contain a &lt;code&gt;Post&lt;/code&gt; type like this:&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;Post&lt;/span&gt;&lt;span class="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;title&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;body&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;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="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;Comment&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;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 above type specifies that a post will have an &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;body&lt;/code&gt;, &lt;code&gt;author&lt;/code&gt; (all non-null because of &lt;code&gt;!&lt;/code&gt; in the type), and an optional (nullable) list of &lt;code&gt;comments&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://graphql.org/learn/schema/"&gt;Schema&lt;/a&gt; to learn about advanced concepts like &lt;code&gt;input&lt;/code&gt;, &lt;code&gt;Enum&lt;/code&gt;, and &lt;code&gt;Interface&lt;/code&gt; in the type system.&lt;/p&gt;

&lt;h3&gt;
  
  
  GraphQL Query and Mutation
&lt;/h3&gt;

&lt;p&gt;A type system is at the heart of the GraphQL schema. GraphQL has two special types:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;code&gt;query&lt;/code&gt; type that serves as an entry point for all read operations on the API.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;mutation&lt;/code&gt; type that exposes an API to mutate data on the server.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each schema, therefore, has something like this:&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;schema&lt;/span&gt;&lt;span class="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="n"&gt;Query&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mutation&lt;/span&gt;&lt;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;Then the &lt;code&gt;Query&lt;/code&gt; and &lt;code&gt;Mutation&lt;/code&gt; types provide the real API on 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;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="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;Post&lt;/span&gt;&lt;span class="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;Mutation&lt;/span&gt;&lt;span class="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;createPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PostInput&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CreatePostResult&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;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 will get back to these types when we start creating our schema with Absinthe.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://graphql.org/learn/queries/"&gt;Read more about GraphQL's queries and mutations&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  GraphQL API
&lt;/h3&gt;

&lt;p&gt;Clients can read the schema to know exactly what an API provides. To perform queries (or mutations) on the API, you send a &lt;code&gt;document&lt;/code&gt; describing the operation to be performed. The server handles the rest and returns a result. Let’s check out an example:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="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="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="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="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt;&lt;span class="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;The response contains exactly what we've asked for:&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;"post"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"An Introduction to Absinthe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sapan"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"lastName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Diwakar"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows for a more efficient data exchange compared to a REST API. It's especially useful for rarely used complex fields in a result that takes time to compute.&lt;/p&gt;

&lt;p&gt;In a REST API, such cases are usually handled by providing different endpoints for fetching that field or having special attributes like &lt;code&gt;include=complex_field&lt;/code&gt; in the query param. On the other hand, a GraphQL API can offer native support by delaying the computation of that field unless it is explicitly asked for in the query.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Your Elixir App with GraphQL and Absinthe
&lt;/h2&gt;

&lt;p&gt;Let’s now turn to Absinthe and start building our API. The installation is simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add Absinthe, &lt;code&gt;Absinthe.Plug&lt;/code&gt;, and a JSON codec (like Jason) into your &lt;code&gt;mix.exs&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;deps&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="p"&gt;[&lt;/span&gt;
       &lt;span class="c1"&gt;# ...&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:absinthe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.7"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:absinthe_plug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.5"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:jason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.0"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="p"&gt;]&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add an entry in your router to forward requests to a specific path (e.g., &lt;code&gt;/api&lt;/code&gt;) to &lt;code&gt;Absinthe.Plug&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Router&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Router&lt;/span&gt;

     &lt;span class="c1"&gt;# ...&lt;/span&gt;

     &lt;span class="n"&gt;forward&lt;/span&gt; &lt;span class="s2"&gt;"/api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;schema:&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Absinthe.Plug&lt;/code&gt; will now handle all incoming requests to the &lt;code&gt;/api&lt;/code&gt; endpoint and forward them to &lt;code&gt;MyAppWeb.Schema&lt;/code&gt; (we will see how to write the schema below).&lt;/p&gt;

&lt;p&gt;The installation steps might vary for different apps, so follow the &lt;a href="https://hexdocs.pm/absinthe/installation.html"&gt;official Absinthe installation guide&lt;/a&gt; if you need more help.&lt;/p&gt;

&lt;h2&gt;
  
  
  Define the Absinthe Schema and Query
&lt;/h2&gt;

&lt;p&gt;Notice that we've passed &lt;code&gt;MyAppWeb.Schema&lt;/code&gt; as the schema to &lt;code&gt;Absinthe.Plug&lt;/code&gt;. This is the entry point of our GraphQL API. To build it, we will use &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Schema.html"&gt;&lt;code&gt;Absinthe.Schema&lt;/code&gt;&lt;/a&gt; behaviour which provides macros for writing schema. Let’s build the schema to support fetching a post by its id.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;

  &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;non_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;resolve&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&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="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_post!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a lot of things happening in the small snippet above. Let’s break it down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We first define a &lt;code&gt;query&lt;/code&gt; block inside our schema. This defines the special query type that we discussed in the GraphQL section.&lt;/li&gt;
&lt;li&gt;That &lt;code&gt;query&lt;/code&gt; type has only one field, named &lt;code&gt;post&lt;/code&gt;. This is the first argument to the &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Schema.Notation.html#field/4"&gt;&lt;code&gt;field&lt;/code&gt; macro&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The return type of the &lt;code&gt;post&lt;/code&gt; field is &lt;code&gt;post&lt;/code&gt; — this is the second argument to the macro. We will get back to that later on.&lt;/li&gt;
&lt;li&gt;This field also has an argument named &lt;code&gt;id&lt;/code&gt;, defined using the &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Schema.Notation.html#arg/3"&gt;&lt;code&gt;arg&lt;/code&gt; macro&lt;/a&gt;.The type of that argument is &lt;code&gt;non_null(:id)&lt;/code&gt;, which is the Absinthe way of saying &lt;code&gt;ID!&lt;/code&gt; — a required value of type &lt;code&gt;ID&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Finally, the &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Schema.Notation.html#resolve/1"&gt;&lt;code&gt;resolve&lt;/code&gt; macro&lt;/a&gt; defines how that field is resolved. It accepts a 2-arity or 3-arity function that receives the parent entity (not passed for the 2-arity function), arguments map, and an &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Resolution.html"&gt;Absinthe.Resolution&lt;/a&gt; struct. The function's return value should be &lt;code&gt;{:ok, value}&lt;/code&gt; or &lt;code&gt;{:error, reason}&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Define the Type In Absinthe
&lt;/h2&gt;

&lt;p&gt;In Absinthe, &lt;code&gt;object&lt;/code&gt; refers to any type that has sub-fields. In the above query, we saw the type &lt;code&gt;post&lt;/code&gt;. To create that type, we will use the &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Schema.Notation.html#object/3"&gt;&lt;code&gt;object&lt;/code&gt; macro&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;

  &lt;span class="nv"&gt;@desc&lt;/span&gt; &lt;span class="s2"&gt;"A post"&lt;/span&gt;
  &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;non_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;non_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;non_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;list_of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:comment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first argument to the object macro is the identifier of the type. This must be unique across the whole schema. Each object can have many fields. Each field can use the full power of the &lt;code&gt;field&lt;/code&gt; macro that we saved above when defining the query. So we can define nested fields that accept arguments and return other &lt;code&gt;object&lt;/code&gt;s.&lt;/p&gt;

&lt;p&gt;As we discussed earlier, the &lt;code&gt;query&lt;/code&gt; itself is an object, just a special one that serves as an entry point to the API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Scalar Types
&lt;/h3&gt;

&lt;p&gt;In addition to objects, you can also get &lt;code&gt;scalar&lt;/code&gt; types. A scalar is a special type with no sub-fields and serializes to native values in the result (e.g., to a string). A good example of a scalar is &lt;a href="https://hexdocs.pm/elixir/1.13/DateTime.html"&gt;Elixir’s &lt;code&gt;DateTime&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To support a &lt;code&gt;DateTime&lt;/code&gt; that we'll use in the schema, we need to use the &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Schema.Notation.html#scalar/3"&gt;&lt;code&gt;scalar&lt;/code&gt; macro&lt;/a&gt;. This tells Absinthe how to serialize and parse a &lt;code&gt;DateTime&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is an example from the &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Schema.Notation.html#scalar/3-examples"&gt;Absinthe docs&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;

  &lt;span class="n"&gt;scalar&lt;/span&gt; &lt;span class="ss"&gt;:isoz_datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;"UTC only ISO8601 date time"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;parse&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;Timex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"{ISO:Extended:Z}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;serialize&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;Timex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"{ISO:Extended:Z}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then use this scalar anywhere in our schema by using &lt;code&gt;:isoz_datetime&lt;/code&gt; as the type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Absinthe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;

  &lt;span class="nv"&gt;@desc&lt;/span&gt; &lt;span class="s2"&gt;"A post"&lt;/span&gt;
  &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;non_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:isoz_datetime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Absinthe already provides several &lt;a href="https://hexdocs.pm/absinthe/1.1.5/Absinthe.Type.BuiltIns.html"&gt;built-in scalars&lt;/a&gt; — &lt;code&gt;boolean&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;integer&lt;/code&gt;, and &lt;code&gt;string&lt;/code&gt; — as well as some &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Type.Custom.html"&gt;custom scalars&lt;/a&gt;: &lt;code&gt;datetime&lt;/code&gt;, &lt;code&gt;naive_datetime&lt;/code&gt;, &lt;code&gt;date&lt;/code&gt;, &lt;code&gt;time&lt;/code&gt;, and &lt;code&gt;decimal&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Type Modifiers and More
&lt;/h3&gt;

&lt;p&gt;We can also modify each type to mark some additional constraints or properties. For example, to mark a type as non-null, we use the &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Schema.Notation.html#non_null/1"&gt;&lt;code&gt;non_null/1&lt;/code&gt;&lt;/a&gt; macro. To define a list of a specific type, we can use &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Schema.Notation.html#list_of/1"&gt;&lt;code&gt;list_of/1&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Advanced types like &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Schema.Notation.html#union/3"&gt;union&lt;/a&gt; and &lt;a href="https://hexdocs.pm/absinthe/Absinthe.Schema.Notation.html#interface/3"&gt;interface&lt;/a&gt; are also supported.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;In this post, we covered the basics of GraphQL and Absinthe for an Elixir application. We discussed the use of GraphQL and Absinthe schema, and touched on types in Absinthe.&lt;/p&gt;

&lt;p&gt;In the next part of this series, we'll see how we can apply Absinthe and GraphQL to large Elixir applications.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, &lt;a href="https://dev.to/elixir-alchemy"&gt;subscribe to our Elixir Alchemy newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
    </item>
    <item>
      <title>Under the Hood of Ecto</title>
      <dc:creator>Sapan Diwakar</dc:creator>
      <pubDate>Tue, 28 Feb 2023 12:08:38 +0000</pubDate>
      <link>https://dev.to/appsignal/under-the-hood-of-ecto-n1a</link>
      <guid>https://dev.to/appsignal/under-the-hood-of-ecto-n1a</guid>
      <description>&lt;p&gt;Ecto is a toolkit for mapping database objects to Elixir structs and provides a unified interface to manipulate that data.&lt;/p&gt;

&lt;p&gt;In this post, we will dive into the internals of Ecto — its major components, their functions, and how they work. In doing so, we'll demystify some of the apparent magic behind Ecto.&lt;/p&gt;

&lt;p&gt;Let's get going!&lt;/p&gt;

&lt;h2&gt;
  
  
  Ecto's Modules
&lt;/h2&gt;

&lt;p&gt;Ecto is made up of four major modules — &lt;code&gt;Repo&lt;/code&gt;, &lt;code&gt;Query&lt;/code&gt;, &lt;code&gt;Schema&lt;/code&gt;, and &lt;code&gt;Changeset&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We'll look at each in turn. Let’s start with the &lt;code&gt;Repo&lt;/code&gt; module.&lt;/p&gt;

&lt;h2&gt;
  
  
  Repo Module
&lt;/h2&gt;

&lt;p&gt;If you use Ecto with a database (like most users out there), Repo is the heart of Ecto. It binds everything together and provides a centralized point of communication between a database and your application. Repo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;maintains connections&lt;/li&gt;
&lt;li&gt;executes queries against a database&lt;/li&gt;
&lt;li&gt;provides an API to write migrations that interact with the database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's get started with Repo. Simply call &lt;code&gt;use Ecto.Repo&lt;/code&gt; inside your Repo module. If you use &lt;code&gt;mix phx.new&lt;/code&gt; to generate your Elixir project, this is done automatically for you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/my_app/repo.ex&lt;/span&gt;
&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;otp_app:&lt;/span&gt; &lt;span class="ss"&gt;:my_app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;adapter:&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Adapters&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Postgres&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These few lines of code define the repo. Putting it under the Supervision tree inside &lt;code&gt;application.ex&lt;/code&gt; gives you access to a whole set of functions provided by Repo to interact with a database. Again, this is code that is generated for you when using Phoenix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Application&lt;/span&gt;

  &lt;span class="nv"&gt;@impl&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;children&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="c1"&gt;# Start the Ecto repository&lt;/span&gt;
      &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;# Other Children...&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# See https://hexdocs.pm/elixir/Supervisor.html&lt;/span&gt;
    &lt;span class="c1"&gt;# for other strategies and supported options&lt;/span&gt;
    &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;strategy:&lt;/span&gt; &lt;span class="ss"&gt;:one_for_one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Supervisor&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="no"&gt;Supervisor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the few lines of code above, you get the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access to the &lt;a href="https://hexdocs.pm/ecto/Ecto.Repo.html#summary" rel="noopener noreferrer"&gt;full &lt;code&gt;Ecto.Repo API&lt;/code&gt;&lt;/a&gt; included in &lt;code&gt;MyApp.Repo&lt;/code&gt;.
The most common use cases include fetching records with &lt;a href="https://hexdocs.pm/ecto/Ecto.Repo.html#c:all/2" rel="noopener noreferrer"&gt;&lt;code&gt;MyApp.Repo.all/2&lt;/code&gt;&lt;/a&gt;, inserting new records with &lt;a href="https://hexdocs.pm/ecto/Ecto.Repo.html#c:insert/2" rel="noopener noreferrer"&gt;&lt;code&gt;MyApp.Repo.insert/2&lt;/code&gt;&lt;/a&gt;, and updating records with &lt;a href="https://hexdocs.pm/ecto/Ecto.Repo.html#c:update/2" rel="noopener noreferrer"&gt;&lt;code&gt;MyApp.Repo.update/2&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://github.com/elixir-ecto/ecto/blob/bbe380e49cb52b1ad4114372cfd73126cf34f996/lib/ecto/repo/supervisor.ex#L1" rel="noopener noreferrer"&gt;Supervisor&lt;/a&gt; starts that keeps track of all the processes required to keep Ecto working.
The Supervision tree initializes the adapter (&lt;code&gt;Ecto.Adapters.Postgres&lt;/code&gt;, in this case), which is responsible for all communication with the database.
The Postgres adapter, in turn, starts a connection pool to your database using the &lt;a href="https://github.com/elixir-ecto/db_connection" rel="noopener noreferrer"&gt;&lt;code&gt;DBConnection&lt;/code&gt; library&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A query planner starts that's responsible for planning and normalizing a query and its parameters.
It also keeps a cache of all planned queries in an ETS table.
We will learn more about this when we get to the &lt;code&gt;Query&lt;/code&gt; module.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Monitor Queries Sent to Ecto from Your Elixir Application
&lt;/h3&gt;

&lt;p&gt;In addition, Ecto also automatically publishes telemetry events that can be monitored.&lt;br&gt;
For example, to monitor statistics for all the queries sent to Ecto, you can subscribe to the &lt;code&gt;[:my_app, :repo, :query]&lt;/code&gt; event with telemetry.&lt;/p&gt;

&lt;p&gt;Then, each time a query is performed, this event triggers some query metadata that includes the time spent executing the query, retrieving the data from the database, and more.&lt;/p&gt;

&lt;p&gt;For more details, see this &lt;a href="https://hexdocs.pm/ecto/Ecto.Repo.html#module-telemetry-events" rel="noopener noreferrer"&gt;full list of Ecto telemetry events&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are many options available to configure the Repo or the adapter as per your needs, but that's out of the scope of this post. Let's just take a very quick look at how you can monitor queries with AppSignal.&lt;/p&gt;
&lt;h3&gt;
  
  
  Instrumenting Ecto Queries with AppSignal in Your Elixir App
&lt;/h3&gt;

&lt;p&gt;AppSignal automatically instruments Ecto so you can get insights into Queries running in your Phoenix or Plug applications. Make sure the &lt;code&gt;:otp_app&lt;/code&gt; configuration option matches your app’s OTP app name, and you’re all set!&lt;/p&gt;

&lt;p&gt;Here's an example of how an Ecto query will look in AppSignal:&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%2Fnywh8ijxevpptl0cijse.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%2Fnywh8ijxevpptl0cijse.png" alt="Ecto query" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.appsignal.com/elixir/integrations/ecto.html" rel="noopener noreferrer"&gt;Read more in our Ecto docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.appsignal.com/elixir" rel="noopener noreferrer"&gt;Check out our AppSignal for Elixir page&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Query Module
&lt;/h2&gt;

&lt;p&gt;The Query module provides a unified API to write database-agnostic queries in Elixir.&lt;br&gt;
Note that building database queries with functions provided by the &lt;code&gt;Ecto.Query&lt;/code&gt; module does not result in the queries being executed.&lt;/p&gt;

&lt;p&gt;These functions return a query in the form of an &lt;code&gt;Ecto.Query&lt;/code&gt; struct.&lt;br&gt;
Nothing is actually sent to the database until the built &lt;code&gt;%Ecto.Query{}&lt;/code&gt; is passed to one of the functions provided by the Repo module.&lt;/p&gt;

&lt;p&gt;As an example, let’s see a simple query that selects all users above 18 years of age:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;
&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="ss"&gt;where:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="ss"&gt;select:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type this into an IEx console and you will see that it creates a struct like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;#Ecto.Query&amp;lt;from u0 in "users", where: u0.age &amp;gt; 18, select: u0.name&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also print it as a full map to see everything inside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;structs:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;%{&lt;/span&gt;
  &lt;span class="ss"&gt;__struct__:&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;aliases:&lt;/span&gt; &lt;span class="p"&gt;%{},&lt;/span&gt;
  &lt;span class="ss"&gt;assocs:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="ss"&gt;combinations:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="ss"&gt;distinct:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;from:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;
    &lt;span class="ss"&gt;__struct__:&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;FromExpr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;as:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;file:&lt;/span&gt; &lt;span class="s2"&gt;"iex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;hints:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="ss"&gt;line:&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;params:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="ss"&gt;prefix:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;source:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="ss"&gt;group_bys:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="ss"&gt;havings:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="ss"&gt;joins:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="ss"&gt;limit:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;lock:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;offset:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;order_bys:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="ss"&gt;prefix:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;preloads:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="ss"&gt;select:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;
    &lt;span class="ss"&gt;__struct__:&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;SelectExpr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;aliases:&lt;/span&gt; &lt;span class="p"&gt;%{},&lt;/span&gt;
    &lt;span class="ss"&gt;expr:&lt;/span&gt; &lt;span class="p"&gt;{{:&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="ss"&gt;:&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt; &lt;span class="ss"&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;span class="ss"&gt;fields:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;file:&lt;/span&gt; &lt;span class="s2"&gt;"iex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;line:&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;params:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="ss"&gt;subqueries:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="ss"&gt;take:&lt;/span&gt; &lt;span class="p"&gt;%{}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="ss"&gt;sources:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;updates:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="ss"&gt;wheres:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;%{&lt;/span&gt;
      &lt;span class="ss"&gt;__struct__:&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;BooleanExpr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;expr:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[{{:&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="p"&gt;[{&lt;/span&gt;&lt;span class="ss"&gt;:&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt; &lt;span class="ss"&gt;:age&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[]},&lt;/span&gt; &lt;span class="p"&gt;{:&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]}]},&lt;/span&gt;
      &lt;span class="ss"&gt;file:&lt;/span&gt; &lt;span class="s2"&gt;"iex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;line:&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;op:&lt;/span&gt; &lt;span class="ss"&gt;:and&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;params:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:age&lt;/span&gt;&lt;span class="p"&gt;}}],&lt;/span&gt;
      &lt;span class="ss"&gt;subqueries:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="ss"&gt;windows:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="ss"&gt;with_ctes:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much more interesting — it shows exactly how the simple query is represented internally in Ecto.&lt;br&gt;
&lt;code&gt;Ecto.Query.FromExpr&lt;/code&gt; contains details about the table we are querying (&lt;code&gt;users&lt;/code&gt;).&lt;/p&gt;
&lt;h3&gt;
  
  
  ASTs in the Query
&lt;/h3&gt;

&lt;p&gt;The other two expressions we see in the query are much more complex, but this is something that the adapters understand and convert to the query.&lt;br&gt;
If you look closely, they are &lt;code&gt;AST&lt;/code&gt;s.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you are interested in learning more about ASTs, check out &lt;a href="https://blog.appsignal.com/2021/09/07/an-introduction-to-metaprogramming-in-elixir.html" rel="noopener noreferrer"&gt;An Introduction to Metaprogramming in Elixir&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's see what the code looks like for this expression:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Macro&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;:&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[{{:&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="p"&gt;[{&lt;/span&gt;&lt;span class="ss"&gt;:&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt; &lt;span class="ss"&gt;:age&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[]},&lt;/span&gt; &lt;span class="p"&gt;{:&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]}]})&lt;/span&gt;
&lt;span class="s2"&gt;"&amp;amp;0.age() &amp;gt; ^0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is our condition in &lt;code&gt;where&lt;/code&gt;, just normalized into terms the adapters understand.&lt;/p&gt;

&lt;p&gt;The adapter does the final translation of the query to actual &lt;code&gt;SQL&lt;/code&gt; that the database understands. Note that while we usually write SQL here, the adapters don't need to work with &lt;code&gt;SQL&lt;/code&gt; databases only — some adapters work just as well with no-SQL databases. Query generation and all database communication are clearly separated from Ecto's core.&lt;/p&gt;

&lt;p&gt;If you want to explore this further, try building out some complex queries with joins, subqueries, windows, etc., and see how they are represented internally — it is a great way to learn how abstractions are made inside Ecto.&lt;/p&gt;

&lt;p&gt;Finally, this query struct is converted to a SQL statement by the adapter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Adapters&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"SELECT u0.&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; FROM &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; AS u0 WHERE (u0.&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;age&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt; $1)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Back to Erlang's ETS Table
&lt;/h3&gt;

&lt;p&gt;Back in the section about the Repo module, we created an ETS table when we started the Repository in our application.&lt;/p&gt;

&lt;p&gt;Now that ETS table comes into play.&lt;br&gt;
When a query is executed multiple times, the query is only &lt;code&gt;prepared&lt;/code&gt; the first time.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; You can learn more about &lt;a href="https://www.postgresql.org/docs/current/sql-prepare.html" rel="noopener noreferrer"&gt;&lt;code&gt;PREPARE&lt;/code&gt; in the context of Postgres&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It is then cached inside that ETS table and fetched from there for all subsequent calls.&lt;br&gt;
To see the caching in action, check this out (notice the &lt;code&gt;:cached&lt;/code&gt; in result, which signals that this query has been cached):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# This puts the query in the cache&lt;/span&gt;

&lt;span class="c1"&gt;# Trying to prepare the query again gets a cached version&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Adapter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Queryable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prepare_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="ss"&gt;:cached&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;#Function&amp;lt;41.44551318/1 in Ecto.Query.Planner.query_with_cache/8&amp;gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;#Function&amp;lt;42.44551318/1 in Ecto.Query.Planner.query_with_cache/8&amp;gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;5122&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Postgrex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="ss"&gt;ref:&lt;/span&gt; &lt;span class="c1"&gt;#Reference&amp;lt;0.3269063957.2912157697.165169&amp;gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"ecto_5122"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;statement:&lt;/span&gt; &lt;span class="s2"&gt;"SELECT u0.&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; FROM &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; AS u0 WHERE (u0.&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;age&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt; $1)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;param_oids:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
     &lt;span class="ss"&gt;param_formats:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:binary&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
     &lt;span class="ss"&gt;param_types:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Postgrex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Extensions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Int8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
     &lt;span class="ss"&gt;columns:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
     &lt;span class="ss"&gt;result_oids:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1043&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
     &lt;span class="ss"&gt;result_formats:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:binary&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
     &lt;span class="ss"&gt;result_types:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Postgrex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Extensions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Raw&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
     &lt;span class="ss"&gt;types:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;Postgrex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;DefaultTypes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;#Reference&amp;lt;0.3269063957.2912288771.132368&amp;gt;},&lt;/span&gt;
     &lt;span class="ss"&gt;cache:&lt;/span&gt; &lt;span class="ss"&gt;:reference&lt;/span&gt;
   &lt;span class="p"&gt;}}},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that this doesn’t cache the result, only the prepared statements. Prepared statements give a large performance advantage, especially for complex queries. &lt;a href="https://www.postgresql.org/docs/current/sql-prepare.html" rel="noopener noreferrer"&gt;From the Postgres docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Prepared statements potentially have the largest performance advantage when a single session is being used to execute a large number of similar statements.&lt;/p&gt;

&lt;p&gt;The performance difference will be particularly significant if the statements are complex to plan or rewrite, e.g., if the query involves a join of many tables or requires the application of several rules.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next important module in Ecto is the &lt;code&gt;Schema&lt;/code&gt;. Let's take a look at it now.&lt;/p&gt;

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

&lt;p&gt;You can use Ecto without schemas and it works just as well (as we saw above when we referenced the table names directly).&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Schema&lt;/code&gt; module is responsible for defining and mapping a record's attributes (fields and associations) from a database table to an Elixir struct.&lt;/p&gt;

&lt;p&gt;To create a schema, we write &lt;code&gt;use Ecto.Schema&lt;/code&gt; at the top of our module and use the &lt;code&gt;schema&lt;/code&gt; DSL.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Organization&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;

  &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="s2"&gt;"organizations"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;

  &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
    &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:organization&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Organization&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://github.com/elixir-ecto/ecto/blob/v3.9.4/lib/ecto/schema.ex#L473" rel="noopener noreferrer"&gt;&lt;code&gt;use&lt;/code&gt; statement&lt;/a&gt; includes several utility functions and macros inside the module and sets some default module attributes required for Ecto to gather data from the Schema.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://github.com/elixir-ecto/ecto/blob/v3.9.4/lib/ecto/schema.ex#L541" rel="noopener noreferrer"&gt;&lt;code&gt;schema&lt;/code&gt; macro&lt;/a&gt; then updates some of those attributes to mark that this is a persisted schema (there is also another &lt;code&gt;embedded_schema&lt;/code&gt; macro to deal with non-persisted schemas) and sets some other defaults, like the primary key.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://github.com/elixir-ecto/ecto/blob/v3.9.4/lib/ecto/schema.ex#L729" rel="noopener noreferrer"&gt;&lt;code&gt;field&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/elixir-ecto/ecto/blob/v3.9.4/lib/ecto/schema.ex#L1274" rel="noopener noreferrer"&gt;&lt;code&gt;belongs_to&lt;/code&gt;&lt;/a&gt; inside the schema block then put those fields in the module attributes (for type validation), and add the fields to the struct defined by the module.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;Ecto.Schema&lt;/code&gt; behavior exposes some methods inside the schema to fetch field details. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;62&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__schema__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="s2"&gt;"users"&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;63&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__schema__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:fields&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:organization_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__schema__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:primary_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__schema__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:associations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:organization&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;66&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__schema__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:association&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:organization&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Association&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;BelongsTo&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;field:&lt;/span&gt; &lt;span class="ss"&gt;:organization&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;owner:&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;related:&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Organization&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;owner_key:&lt;/span&gt; &lt;span class="ss"&gt;:organization_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;related_key:&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;queryable:&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Organization&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;on_cast:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;on_replace:&lt;/span&gt; &lt;span class="ss"&gt;:raise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;where:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="ss"&gt;defaults:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="ss"&gt;cardinality:&lt;/span&gt; &lt;span class="ss"&gt;:one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;relationship:&lt;/span&gt; &lt;span class="ss"&gt;:parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;unique:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;ordered:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;__schema__&lt;/code&gt; function is also the entry-point for other parts of Ecto to reflect on more details about the defined schema and perform operations on it.&lt;/p&gt;

&lt;p&gt;For example, when used as the source of a query, the repo will use schema to validate the conditions in the &lt;code&gt;where&lt;/code&gt; clause, and cast the data returned from the database to Elixir structs.&lt;br&gt;
This results in much better feedback when there's something wrong.&lt;/p&gt;
&lt;h2&gt;
  
  
  A Schema Module In Action on an Elixir App
&lt;/h2&gt;

&lt;p&gt;Let's try executing a query that has a typo to see the benefits of using a schema in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;select:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;where:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ages&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Executing this with &lt;code&gt;Repo.all&lt;/code&gt; will throw a generic &lt;code&gt;Postgrex.Error&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;**&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Postgrex.Error&lt;span class="o"&gt;)&lt;/span&gt; ERROR 42703 &lt;span class="o"&gt;(&lt;/span&gt;undefined_column&lt;span class="o"&gt;)&lt;/span&gt; column u0.ages does not exist

    query: SELECT u0.&lt;span class="s2"&gt;"id"&lt;/span&gt; FROM &lt;span class="s2"&gt;"users"&lt;/span&gt; AS u0 WHERE &lt;span class="o"&gt;(&lt;/span&gt;u0.&lt;span class="s2"&gt;"ages"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 19&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's try the same query, but this time with a schema.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;select:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;where:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ages&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As expected, this also throws an error, but it now includes the line number where it happened and has a more specific Exception type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;**&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Ecto.QueryError&lt;span class="o"&gt;)&lt;/span&gt; lib/my_app/accounts.ex:109: field &lt;span class="sb"&gt;`&lt;/span&gt;ages&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;where&lt;span class="sb"&gt;`&lt;/span&gt; does not exist &lt;span class="k"&gt;in &lt;/span&gt;schema MyApp.Accounts.User &lt;span class="k"&gt;in &lt;/span&gt;query:

from u0 &lt;span class="k"&gt;in &lt;/span&gt;MyApp.Accounts.User,
  where: u0.ages &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 19,
  &lt;span class="k"&gt;select&lt;/span&gt;: u0.id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works because the query planner in Ecto can look at the schema's metadata and figure out that this field doesn't exist on the schema even before hitting the database.&lt;/p&gt;

&lt;p&gt;Ecto also does type conversions behind the scenes when using the schema. For example, it allows us to run this query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2"&lt;/span&gt;
&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;where:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the other hand, if you are not using a schema, a similar query will raise an exception:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;where:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;select:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;DBConnection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;EncodeError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="no"&gt;Postgrex&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;integer&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9223372036854775808&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;9223372036854775807&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="no"&gt;Please&lt;/span&gt; &lt;span class="n"&gt;make&lt;/span&gt; &lt;span class="n"&gt;sure&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;passing&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;definition&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;convert&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;accordingly&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Changeset Module
&lt;/h2&gt;

&lt;p&gt;The final module that we will look at today is &lt;code&gt;Changeset&lt;/code&gt;.&lt;br&gt;
It provides an interface for validating and transforming data before it is written into a database.&lt;/p&gt;

&lt;p&gt;Similarly to &lt;code&gt;Query&lt;/code&gt;, Changeset provides a structured way to represent changes to data. It is most commonly used with Ecto schemas, but &lt;a href="https://hexdocs.pm/ecto/Ecto.Changeset.html#module-schemaless-changesets" rel="noopener noreferrer"&gt;schemaless changesets&lt;/a&gt; are also possible when you don’t need a full-fledged schema.&lt;/p&gt;

&lt;p&gt;Ecto.Changeset provides a &lt;a href="https://hexdocs.pm/ecto/Ecto.Changeset.html#summary" rel="noopener noreferrer"&gt;comprehensive API&lt;/a&gt; to work with data.&lt;/p&gt;
&lt;h3&gt;
  
  
  The &lt;code&gt;cast/4&lt;/code&gt; Changeset
&lt;/h3&gt;

&lt;p&gt;Let's start by looking at the most commonly used changeset, &lt;a href="https://hexdocs.pm/ecto/Ecto.Changeset.html#cast/4" rel="noopener noreferrer"&gt;&lt;code&gt;cast/4&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"some name"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(%{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"organization_id"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"foo"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:organization_id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
     &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;validate_required&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We pass initial data (the &lt;code&gt;MyApp.User&lt;/code&gt; struct in this case) to &lt;code&gt;cast&lt;/code&gt; followed by some parameters and the list of allowed fields.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cast&lt;/code&gt; figures out the type of each allowed field by looking at the schema metadata. &lt;code&gt;cast&lt;/code&gt; then typecasts a parameter value to an allowed value, or adds an error to the changeset.&lt;/p&gt;

&lt;p&gt;For example, here we can see that we had a value of &lt;code&gt;1&lt;/code&gt; (&lt;code&gt;String&lt;/code&gt;) as the &lt;code&gt;organization_id&lt;/code&gt;.&lt;br&gt;
But from the schema, &lt;code&gt;cast&lt;/code&gt; can figure out that &lt;code&gt;organization_id&lt;/code&gt; is of type &lt;code&gt;int&lt;/code&gt; and cast the value to an integer before putting the change inside the Changeset.&lt;/p&gt;
&lt;h3&gt;
  
  
  The &lt;code&gt;validate_required/3&lt;/code&gt; Changeset
&lt;/h3&gt;

&lt;p&gt;The second call in the pipeline, &lt;a href="https://hexdocs.pm/ecto/Ecto.Changeset.html#validate_required/3" rel="noopener noreferrer"&gt;&lt;code&gt;validate_required/3&lt;/code&gt;&lt;/a&gt;, requires the field to be present (as the name suggests).&lt;br&gt;
By default, it trims any strings/binaries before running validations and assumes an empty string to be blank.&lt;/p&gt;

&lt;p&gt;Here's what is printed out when you inspect the &lt;code&gt;Changeset&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#Ecto.Changeset&amp;lt;&lt;/span&gt;
  action: nil,
  changes: %&lt;span class="o"&gt;{&lt;/span&gt;organization_id: 1&lt;span class="o"&gt;}&lt;/span&gt;,
  errors: &lt;span class="o"&gt;[&lt;/span&gt;name: &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"can't be blank"&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;validation: :required]&lt;span class="o"&gt;}]&lt;/span&gt;,
  data: &lt;span class="c"&gt;#MyApp.User&amp;lt;&amp;gt;,&lt;/span&gt;
  valid?: &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This small summary already contains most of the information we need about the changeset.&lt;br&gt;
It shows what changes were made to the initial data (&lt;code&gt;organization_id&lt;/code&gt; was set to &lt;code&gt;1&lt;/code&gt;), and that the changeset is invalid. It lists all the errors.&lt;/p&gt;

&lt;p&gt;Let’s go one step further and inspect the full map.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;structs:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;%{&lt;/span&gt;
  &lt;span class="ss"&gt;__struct__:&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Changeset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;action:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;changes:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;organization_id:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="ss"&gt;constraints:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="ss"&gt;data:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;
    &lt;span class="ss"&gt;__meta__:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;
      &lt;span class="ss"&gt;__struct__:&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;context:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;prefix:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;schema:&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;source:&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;state:&lt;/span&gt; &lt;span class="ss"&gt;:built&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="ss"&gt;__struct__:&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;id:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"some name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;organization:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;
      &lt;span class="ss"&gt;__cardinality__:&lt;/span&gt; &lt;span class="ss"&gt;:one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;__field__:&lt;/span&gt; &lt;span class="ss"&gt;:organization&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;__owner__:&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;__struct__:&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Association&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;NotLoaded&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="ss"&gt;organization_id:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="ss"&gt;empty_values:&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="ss"&gt;errors:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"can't be blank"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;validation:&lt;/span&gt; &lt;span class="ss"&gt;:required&lt;/span&gt;&lt;span class="p"&gt;]}],&lt;/span&gt;
  &lt;span class="ss"&gt;filters:&lt;/span&gt; &lt;span class="p"&gt;%{},&lt;/span&gt;
  &lt;span class="ss"&gt;params:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"organization_id"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"foo"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="ss"&gt;prepare:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="ss"&gt;repo:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;repo_opts:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="ss"&gt;required:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="ss"&gt;types:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;
    &lt;span class="ss"&gt;id:&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;organization:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:assoc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;%{&lt;/span&gt;
       &lt;span class="ss"&gt;__struct__:&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Association&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;BelongsTo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;cardinality:&lt;/span&gt; &lt;span class="ss"&gt;:one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;defaults:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
       &lt;span class="ss"&gt;field:&lt;/span&gt; &lt;span class="ss"&gt;:organization&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;on_cast:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;on_replace:&lt;/span&gt; &lt;span class="ss"&gt;:raise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;ordered:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;owner:&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;owner_key:&lt;/span&gt; &lt;span class="ss"&gt;:organization_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;queryable:&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Organization&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;related:&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Organization&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;related_key:&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;relationship:&lt;/span&gt; &lt;span class="ss"&gt;:parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;unique:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;where:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
     &lt;span class="p"&gt;}},&lt;/span&gt;
    &lt;span class="ss"&gt;organization_id:&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="ss"&gt;valid?:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;validations:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This contains much more information now.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;data&lt;/code&gt; and &lt;code&gt;params&lt;/code&gt; are self-explanatory — the initial data and the params we fed to &lt;code&gt;cast&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;types&lt;/code&gt; contains additional data about the schema we are working with (fetched using the &lt;code&gt;__schema__&lt;/code&gt; method we saw in the previous section).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;changes&lt;/code&gt; and &lt;code&gt;errors&lt;/code&gt; are where it gets interesting. &lt;code&gt;cast&lt;/code&gt; automatically converts the string &lt;code&gt;organization_id&lt;/code&gt; to an integer because it knows that the &lt;code&gt;organization_id&lt;/code&gt; is a numeric primary key from the association details.&lt;/p&gt;

&lt;p&gt;What's also interesting is that it understands that &lt;code&gt;""&lt;/code&gt; is a blank value and inserts an error into the changeset from the &lt;code&gt;validate_required&lt;/code&gt; call.&lt;/p&gt;

&lt;p&gt;The database manipulation functions from &lt;code&gt;Repo&lt;/code&gt; understand changesets and return errors if the changeset is invalid.&lt;/p&gt;

&lt;p&gt;The Changeset API provides several other functions for validating data and database constraints, as well as dealing with associations.&lt;/p&gt;

&lt;p&gt;These functions interact with the data and eventually update the struct that we saw above. When fed to the &lt;code&gt;Repo&lt;/code&gt; module's functions, structs perform the eventual database operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post, we looked at the core concepts of the Ecto library:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We started with a &lt;code&gt;Schema&lt;/code&gt; that defined our business objects mapped to database tables.&lt;/li&gt;
&lt;li&gt;To get those objects out of the database, we used the &lt;code&gt;Query&lt;/code&gt; API.&lt;/li&gt;
&lt;li&gt;We then used &lt;code&gt;Changeset&lt;/code&gt;s to change those objects, and inserted or updated them in the database.&lt;/li&gt;
&lt;li&gt;Tying everything together was the &lt;code&gt;Repo&lt;/code&gt; module which takes inputs from all the other modules, eventually connecting to the database to fetch data or update records.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these modules work together to provide a structured and safe way to interact with databases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/ecto/Ecto.html" rel="noopener noreferrer"&gt;Check out the official Ecto guide&lt;/a&gt; to integrate Ecto into your Elixir application. I have also included links to the source code in parts of this post, so feel free to go back and dig a little deeper.&lt;/p&gt;

&lt;p&gt;Until next time — happy digging!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, &lt;a href="https://dev.to/elixir-alchemy"&gt;subscribe to our Elixir Alchemy newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>sql</category>
      <category>orm</category>
      <category>beginners</category>
      <category>discuss</category>
    </item>
    <item>
      <title>How to Scale Ruby on Rails Applications</title>
      <dc:creator>Sapan Diwakar</dc:creator>
      <pubDate>Wed, 16 Nov 2022 12:34:39 +0000</pubDate>
      <link>https://dev.to/appsignal/how-to-scale-ruby-on-rails-applications-3fe5</link>
      <guid>https://dev.to/appsignal/how-to-scale-ruby-on-rails-applications-3fe5</guid>
      <description>&lt;p&gt;Today we will dive into some strategies you can use to scale Ruby on Rails applications to a huge user base.&lt;/p&gt;

&lt;p&gt;One obvious way of scaling applications is to throw more money at them. And it works amazingly well — add a few more servers, upgrade your database server, and voila, a lot of the performance issues just go &lt;em&gt;poof&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;But it is often also possible to scale applications without adding more servers. That's what we will discuss today.&lt;/p&gt;

&lt;p&gt;Let's get going!&lt;/p&gt;

&lt;h2&gt;
  
  
  Use AppSignal for your Rails Application
&lt;/h2&gt;

&lt;p&gt;Before we dive into scaling and performance optimization, you first need to identify &lt;em&gt;if&lt;/em&gt; you need to do this, what the bottlenecks are in your application, and what resources can be scaled.&lt;/p&gt;

&lt;p&gt;One easy way to do this is to use &lt;a href="https://www.appsignal.com/ruby"&gt;AppSignal's performance monitoring and metrics for Ruby&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.appsignal.com/tour/performance"&gt;performance dashboard&lt;/a&gt; helps you pinpoint the exact controller actions and background jobs that are slow on average.&lt;/p&gt;

&lt;p&gt;For example, here's how a performance dashboard might look for ActiveRecord:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8mNlrXo6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2022-11/activerecord.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8mNlrXo6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2022-11/activerecord.png" alt="ActiveRecord dashboard" width="880" height="503"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This gives a good starting point to any scaling journey — whether you decide to add more servers or optimize performance through code.&lt;/p&gt;

&lt;p&gt;Now let's move on to one of the simplest techniques you can use to scale your Rails app — caching.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching in Ruby on Rails
&lt;/h2&gt;

&lt;p&gt;Caching allows you to stop computing the same things again and again.&lt;/p&gt;

&lt;p&gt;For example, let's say you run a social media platform and there's a very popular post. Caching can immediately help you regain all the CPU cycles you spend rendering that post for every user. And that's only a part of what caching can help you do.&lt;/p&gt;

&lt;p&gt;Let's look at all the possible resources that can be cached.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching Views
&lt;/h3&gt;

&lt;p&gt;Rendering views can sometimes be an expensive operation, especially when that view has a lot of data to be rendered. Even when the operation isn't expensive, using a pre-rendered view will give you a lot of performance as opposed to rendering that same view a million times.&lt;/p&gt;

&lt;p&gt;Rails supports this out of the box using the &lt;a href="https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/CacheHelper.html#method-i-cache"&gt;&lt;code&gt;cache&lt;/code&gt;&lt;/a&gt; view helper. For example, this is how it can cache each post when rendering a list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;% @posts.each &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="sx"&gt;%&amp;gt;
  &amp;lt;% cache post do %&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= render post %&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this, Rails automatically caches each post under a specific key that depends on the HTML content of the template, post id, and update timestamp.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;To read more about this technique, check out the posts &lt;a href="https://blog.appsignal.com/2018/03/20/fragment-caching-in-rails"&gt;Fragment caching in Rails&lt;/a&gt; and &lt;a href="https://blog.appsignal.com/2018/08/14/rails-collection-caching"&gt;Rails collection caching&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One thing to keep in mind, though, is that the cache keys don’t include nested template content. So if you are nesting the cache calls deeper than one level, there might be stale results. Read more about this in &lt;a href="https://blog.appsignal.com/2018/04/03/russian-doll-caching-in-rails"&gt;Russian doll caching in Rails&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching Responses
&lt;/h3&gt;

&lt;p&gt;In addition to caching views/fragments, you can also choose to cache the full response of &lt;code&gt;GET&lt;/code&gt; requests. This is supported through the &lt;code&gt;If-None-Match&lt;/code&gt; and &lt;code&gt;If-Modified-Since&lt;/code&gt; headers sent by the browsers.&lt;/p&gt;

&lt;p&gt;When an &lt;code&gt;If-None-Match&lt;/code&gt; header is present on the request, the server can return a &lt;code&gt;304 Not Modified&lt;/code&gt; response with no content if there are no changes to the response. The server-computed &lt;code&gt;Etag&lt;/code&gt; is compared with the value inside that header.&lt;/p&gt;

&lt;p&gt;Similarly, if the &lt;code&gt;If-Modified-Since&lt;/code&gt; header is present without an &lt;code&gt;If-None-Match&lt;/code&gt;, the server can return a &lt;code&gt;304 Not Modified&lt;/code&gt; response with no content (as long as the response hasn’t changed since that date).&lt;/p&gt;

&lt;p&gt;Rails provides easy ways to do this inside controller actions. You can simply write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="vi"&gt;@post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&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="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;fresh_when&lt;/span&gt; &lt;span class="ss"&gt;last_modified: &lt;/span&gt;&lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;etag: &lt;/span&gt;&lt;span class="vi"&gt;@post&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rails will send all the required headers to support caching, handle incoming headers, and respond with 304 when the data hasn’t changed. The server can skip rendering the full views again unless things change. You can read more about advanced configuration for this strategy in &lt;a href="https://blog.appsignal.com/2018/05/01/client-side-caching-in-rails-conditional-get-requests"&gt;Client-side caching in Rails: conditional GET requests&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching Values
&lt;/h3&gt;

&lt;p&gt;Finally, it is also possible to cache raw values (anything that can be serialized to the cache store). This is usually useful to cache the results of resource-intensive or slow operations and avoid performing them again.&lt;/p&gt;

&lt;p&gt;Identifying a value that can benefit from this caching depends greatly on the application, but usually, looking at your slowest events can help point you in the correct direction.&lt;/p&gt;

&lt;p&gt;Finally, when you identify what to cache, the API that Rails provides for this is very simple to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache_key_with_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;expires_in: &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;perform_the_slow_computation&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code will &lt;code&gt;perform_the_slow_computation&lt;/code&gt; only once and then cache the value under the &lt;code&gt;cache_key_with_version&lt;/code&gt; key. The next time the same code is called, Rails will first check if we already have a cached value and use that instead of triggering &lt;code&gt;perform_the_slow_computation&lt;/code&gt; again.&lt;/p&gt;

&lt;p&gt;The most important part of this caching strategy is to compute a good cache key that depends on all the inputs used in the value's computation. This is to ensure we don’t keep using a stale value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache Stores
&lt;/h3&gt;

&lt;p&gt;Now that we know what to cache and the techniques Rails provides to store things in the cache, the next logical question is — where do we cache this data? Rails comes with several in-built cache store adapters. The most popular cache stores for production use cases are &lt;a href="https://redis.io/"&gt;Redis&lt;/a&gt; and &lt;a href="https://memcached.org/"&gt;Memcached&lt;/a&gt;. There are a couple of other options as well — the &lt;a href="https://guides.rubyonrails.org/caching_with_rails.html#activesupport-cache-filestore"&gt;file store&lt;/a&gt; and  &lt;a href="https://guides.rubyonrails.org/caching_with_rails.html#activesupport-cache-memorystore"&gt;memory store&lt;/a&gt;. A full discussion of these stores can be found in the post &lt;a href="https://blog.appsignal.com/2018/04/17/rails-built-in-cache-stores"&gt;Rails' built-in cache stores: an overview&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;File and memory stores can be great for development use to get things up and running quickly. However, they are usually unsuitable for production, especially if you're working in a distributed setup with multiple servers. Redis and memcached are both suited for production use. Which one you use usually depends on the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background Workers in Ruby on Rails
&lt;/h2&gt;

&lt;p&gt;Most applications need background jobs for mailers, regular clean-ups, or any other time-consuming operation that doesn't require a user to be present. Chances are, you already have a background worker set up already.&lt;/p&gt;

&lt;p&gt;Whenever you find yourself doing anything that takes more than a second to do inside a controller action, see if you can move it to a background worker instead. This could range from a user-facing operation like searching for data inside a large table to an API method that ingests a large amount of data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example Implementation
&lt;/h3&gt;

&lt;p&gt;To run custom jobs, Rails provides the &lt;a href="https://edgeguides.rubyonrails.org/active_job_basics.html"&gt;Active Job framework&lt;/a&gt;. Let's see how we can use it to move a very complex filtering logic to a background job. First, let’s create our background job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# jobs/filter_huge_dataset_job.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FilterHugeDatasetJob&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationJob&lt;/span&gt;
  &lt;span class="n"&gt;queue_as&lt;/span&gt; &lt;span class="ss"&gt;:default&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# search your data&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can run this job from the controller like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# controllers/huge_datasets_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HugeDatasetsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="no"&gt;FilterHugeDatasetJob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform_later&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@current_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to render a loading indicator on our template while we wait for our job to compute data and deliver the results.&lt;/p&gt;

&lt;p&gt;But how can we get the results from our job to the view? Turbo makes this really easy. For example, inside the view, we can subscribe to turbo-stream events on a specific notification channel using &lt;code&gt;turbo_stream_from&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Using this, let’s write our templates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# view/huge_datasets/index.html.erb&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= render "index", user: @current_user %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# view/huge_datasets/_index.html.erb&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= turbo_stream_from user, :huge_datasets %&amp;gt;
&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"filters"&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"data-container"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;% if &lt;/span&gt;&lt;span class="k"&gt;defined?&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="sx"&gt;%&amp;gt;
    &amp;lt;%= render partial: "item", collection: data  %&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;% else &lt;/span&gt;&lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= render "loading" %&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since data is not defined in the initial controller action, we will only render a loading indicator. Let’s now deliver the results from our job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# jobs/filter_huge_dataset_job.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FilterHugeDatasetJob&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationJob&lt;/span&gt;
  &lt;span class="n"&gt;queue_as&lt;/span&gt; &lt;span class="ss"&gt;:default&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;search_huge_dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;notify_completed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_huge_dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# search your data&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;notify_completed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Turbo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;StreamsChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;broadcast_replace_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:huge_datasets&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="ss"&gt;target: &lt;/span&gt;&lt;span class="s2"&gt;"data-container"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;partial: &lt;/span&gt;&lt;span class="s2"&gt;"huge_datasets/index"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;locals: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part here is the &lt;code&gt;notify_completed&lt;/code&gt; method. It uses &lt;a href="https://github.com/hotwired/turbo-rails/blob/main/app/channels/turbo/streams_channel.rb"&gt;Turbo::StreamsChannel&lt;/a&gt;, broadcasting a replace event to the &lt;code&gt;[user, :huge_datasets]&lt;/code&gt; notification stream that we subscribed to from our view.&lt;/p&gt;

&lt;p&gt;That is everything we need to move complex operations from our controller to background jobs. The main advantage of moving tasks to the background is that background workers can be scaled independently of web servers. This frees up resources on the web server side considerably. For the user, such interfaces also feel much more responsive because we can respond quickly and deliver results incrementally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;em&gt;If you need help deciding between a background job worker, read &lt;a href="https://blog.appsignal.com/2022/02/15/delayed-job-vs-sidekiq-which-is-better.html"&gt;Delayed Job vs. Sidekiq: Which Is Better?&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling a Database in Your Ruby on Rails Application
&lt;/h2&gt;

&lt;p&gt;The last scalable resource that we will discuss in this post is the database. Databases form the core of most applications. As data and the number of servers accessing that data grows, databases start to feel the load.&lt;/p&gt;

&lt;p&gt;The easiest way to scale a database is to add more processing power and memory to the database server. As opposed to scaling web servers, doing this with a database is usually a very slow operation, especially if you have high storage.&lt;/p&gt;

&lt;p&gt;The second option to scale databases is to scale horizontally using multiple databases or by sharding your database. Check out &lt;a href="https://guides.rubyonrails.org/active_record_multiple_databases.html"&gt;Multiple Databases with Active Record&lt;/a&gt; for more details about this.&lt;/p&gt;

&lt;p&gt;Instead, we'll focus on optimizing your database's performance by looking at PostgreSQL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Find Time-Consuming Queries in PostgreSQL
&lt;/h3&gt;

&lt;p&gt;First, we need to identify our most time-consuming queries. The way we can do that is to query the &lt;a href="https://www.postgresql.org/docs/current/pgstatstatements.html"&gt;&lt;code&gt;pg_stat_statements&lt;/code&gt;&lt;/a&gt; table that contains statistics about all SQL statements executed on the server. Let’s see how we can find the top 100 queries with the highest run times:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_exec_time&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;avg_time_ms&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_stat_statements&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;calls&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;avg_time_ms&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will return the query, the number of calls, and the average run time of these queries. Try to find the ones you think could be faster and analyze why they were slow.&lt;/p&gt;

&lt;p&gt;You can also run &lt;code&gt;EXPLAIN&lt;/code&gt; or &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; on the query to see the query plan and actual execution details, respectively.&lt;/p&gt;

&lt;p&gt;One of the most important things to look out for in the results is &lt;code&gt;Seq Scan&lt;/code&gt;, which indicates that Postgres has to go through all the records sequentially to run the query. If this happens, try to bypass that sequential scan by adding an index to the columns that you've filtered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tables with the Most Sequential Scans
&lt;/h3&gt;

&lt;p&gt;Another useful query that I like to run is to find the total number of sequential scans run against a table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;relname&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seq_scan&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_stat_user_tables&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;seq_scan&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see a very large table (with a high number of rows) and a high count value from this result, then you have a problem. Try to check all queries against that table, find ones that can run sequential scans, and add indices to boil that down.&lt;/p&gt;

&lt;h3&gt;
  
  
  Index Usage
&lt;/h3&gt;

&lt;p&gt;You can also find statistics about index usage by running this query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;relname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="n"&gt;idx_scan&lt;/span&gt;
     &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'Insufficient data'&lt;/span&gt;
     &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;idx_scan&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seq_scan&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;idx_scan&lt;/span&gt;&lt;span class="p"&gt;))::&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;
   &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;percent_of_times_index_used&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;n_live_tup&lt;/span&gt; &lt;span class="n"&gt;rows_in_table&lt;/span&gt;
 &lt;span class="k"&gt;FROM&lt;/span&gt;
   &lt;span class="n"&gt;pg_stat_user_tables&lt;/span&gt;
 &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
   &lt;span class="n"&gt;n_live_tup&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns the percentage of index usage for each table. A low number means that you are missing some indexes on that table.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;In this post, we explored several strategies to scale your Ruby on Rails applications, including caching and background workers. We also looked at optimizing your PostgreSQL database's performance.&lt;/p&gt;

&lt;p&gt;Rails makes it really easy to add several layers of performance optimization to your application.&lt;/p&gt;

&lt;p&gt;The most important consideration with scalability is to identify bottlenecks in an application before we can act on them. A good performance monitoring tool can help. If you need one, check out &lt;a href="https://www.appsignal.com/ruby"&gt;AppSignal for Ruby&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Get Started with Hotwire in Your Ruby on Rails App</title>
      <dc:creator>Sapan Diwakar</dc:creator>
      <pubDate>Wed, 13 Jul 2022 11:26:31 +0000</pubDate>
      <link>https://dev.to/appsignal/get-started-with-hotwire-in-your-ruby-on-rails-app-3cea</link>
      <guid>https://dev.to/appsignal/get-started-with-hotwire-in-your-ruby-on-rails-app-3cea</guid>
      <description>&lt;p&gt;Hotwire is a hot topic at the moment for every Rails developer. If you work with Rails, there is a good chance you have already heard a lot about it.&lt;/p&gt;

&lt;p&gt;Hotwire is a completely new way of adding interactivity to your app with very few lines of code, and it works blazing fast by transmitting HTML over the wire.&lt;/p&gt;

&lt;p&gt;That means you can keep your hands clean from most Single Page Applications (SPA) frameworks. You can also keep your rendering logic centralized on the server, while still maintaining quick page load times and interactivity.&lt;/p&gt;

&lt;p&gt;In this post, we'll look at the main components of Hotwire and how to use it in your Rails app. But first: what is Hotwire and why should you use it?&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Hotwire?
&lt;/h2&gt;

&lt;p&gt;Hotwire is not a single library, but a new approach to building web and mobile applications by sending HTML over the wire. It includes Turbo, Stimulus, and Strada (coming later this year).&lt;/p&gt;

&lt;p&gt;We will discuss each of these in detail in the next section.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Side note&lt;/em&gt;: While Hotwire is highly linked with Rails, it is completely language-agnostic, so it can work just as well with other applications.&lt;/p&gt;

&lt;p&gt;I have been using Stimulus in production on several non-Rails apps and some static websites. You can use Turbo without Rails as well.&lt;/p&gt;

&lt;p&gt;But let us come back to the Rails world for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use Hotwire in Your Rails App?
&lt;/h2&gt;

&lt;p&gt;So when should you use Hotwire? The answer is anywhere you want to add interactivity to your application. For example, if you want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some content to be displayed/hidden conditionally based on a user's interaction (e.g., an address form where the list of states automatically changes based on the selected country).&lt;/li&gt;
&lt;li&gt;To update some content in real-time (e.g., a feed like Twitter where new Tweets automatically get added to the page).&lt;/li&gt;
&lt;li&gt;To lazy-load some parts of your pages (e.g., inside an accordion, you can load the titles and mark the details to be lazy-loaded to speed up load times).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hotwire Components
&lt;/h2&gt;

&lt;p&gt;As mentioned before, Hotwire is a collection of new (and some old) techniques for building web apps.&lt;/p&gt;

&lt;p&gt;Let's discuss each of these in the next few sections.&lt;/p&gt;

&lt;h3&gt;
  
  
  Turbo
&lt;/h3&gt;

&lt;p&gt;HTML drives Turbo at its core.&lt;/p&gt;

&lt;p&gt;Turbo provides several techniques to handle HTML data coming over the wire and display it on your application without performing a full page reload.&lt;/p&gt;

&lt;p&gt;It is composed of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Turbo Drive&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have used &lt;a href="https://github.com/turbolinks/turbolinks"&gt;Turbolinks&lt;/a&gt; in the past, you will feel right at home with Turbo Drive. At its core, some JS code intercepts JavaScript events on your application, loads HTML asynchronously, and replaces parts of your HTML markup.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Turbo Frames&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Turbo Frames decouple parts of your markup into different sections that can be loaded independently. For example, if you have a blog application, the content of your post and the comments are two related but independent parts of the page. You can decouple them so navigation works independently or even load them asynchronously with turbo frames.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Turbo Streams&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Turbo Streams offers utilities to easily bring in real-time data to your application. For example, let's say you are building a news feed like Twitter. You want to pull new tweets into a user's feed as soon as they are posted without reloading the page. Turbo Streams allow you to do this without writing a single line of JS.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Turbo Native&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Turbo Native lets you build a native wrapper around your web application. Navigations and interactions will feel native without you having to redo all the screens natively. You'll keep delivering the rest of the application through the web. That way, you can focus on the really interactive parts of your application and get them right.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stimulus
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://stimulus.hotwired.dev/"&gt;Stimulus&lt;/a&gt; is a JavaScript framework for writing controllers that interact with your HTML.&lt;/p&gt;

&lt;p&gt;Let's say we need to add some JavaScript attributes like &lt;code&gt;data-controller&lt;/code&gt;, &lt;code&gt;data-action&lt;/code&gt;, and &lt;code&gt;data-target&lt;/code&gt; to elements on a page. We'll write a stimulus controller with access to elements that receives events based on those attributes.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;data-controller=&lt;/span&gt;&lt;span class="s"&gt;"clipboard"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  PIN:
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;data-clipboard-target=&lt;/span&gt;&lt;span class="s"&gt;"source"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"1234"&lt;/span&gt; &lt;span class="na"&gt;readonly&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;data-action=&lt;/span&gt;&lt;span class="s"&gt;"clipboard#copy"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Copy to Clipboard&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is very easy to get an idea about what this does without even reading the associated Stimulus controller.&lt;/p&gt;

&lt;p&gt;Here's a controller that goes with the HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/controllers/clipboard_controller.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@hotwired/stimulus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;targets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;source&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="nx"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clipboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sourceTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is at the core of Stimulus: keeping things simple and reusable.&lt;/p&gt;

&lt;p&gt;Now, if you ever need a copy-to-the-clipboard button on another page, you can just re-use that controller. Add the &lt;code&gt;data-*&lt;/code&gt; attributes on the markup to get everything working.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strada
&lt;/h3&gt;

&lt;p&gt;Unfortunately, we don't know much about Strada yet. But it will allow a web application to communicate (and possibly perform actions) with a native app using HTML bridge attributes.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use Hotwire in Your Ruby on Rails Application
&lt;/h2&gt;

&lt;p&gt;I don't want to spend too much time discussing Hotwire installation or a basic use case. The Hotwire team has already done an excellent job of it in their &lt;a href="https://hotwired.dev/"&gt;Hotwire screencast&lt;/a&gt;. For full instructions, see &lt;a href="https://github.com/hotwired/turbo-rails#installation"&gt;&lt;code&gt;turbo-rails&lt;/code&gt; installation&lt;/a&gt; and &lt;a href="https://stimulus.hotwired.dev/handbook/installing"&gt;Stimulus installation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's jump straight into some common Hotwire use cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Endless Scroll
&lt;/h3&gt;

&lt;p&gt;Using Turbo Frames, we can easily make a page with automatic pagination as the user scrolls. For this, we need to do two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Render each "page" inside its own frame by appending the page number to the frame id (e.g., &lt;code&gt;turbo_frame_tag "posts_#{@posts.current_page}"&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Use a &lt;a href="https://turbo.hotwired.dev/handbook/frames#lazy-loading-frames"&gt;&lt;code&gt;lazy&lt;/code&gt; frame&lt;/a&gt; for the next page so that it doesn't load automatically unless it comes into view.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="s2"&gt;"posts_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_page&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="vi"&gt;@posts&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vi"&gt;@posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_page?&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="s2"&gt;"posts_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next_page&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:src&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;path_to_next_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@posts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="ss"&gt;:loading&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"lazy"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"loading"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;  &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;  &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that this example uses methods from &lt;a href="https://github.com/kaminari/kaminari"&gt;Kaminari&lt;/a&gt;, but you can adapt it to any other pagination method.&lt;/p&gt;

&lt;p&gt;We don't need anything special in the controller. A standard &lt;code&gt;index&lt;/code&gt; method works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="vi"&gt;@posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;per&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:per_page&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trick here is that we use nested frames, with the frame for the next page nested inside the frame for the previous page.&lt;/p&gt;

&lt;p&gt;That way, when the first page loads, the frame for the next page is placed at the end. When the user scrolls to that frame, it is replaced with the content of the second page. The lazy frame for the third page renders at the end.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic Forms
&lt;/h3&gt;

&lt;p&gt;You can easily implement dynamic forms with Hotwire without custom logic for toggling fields on the front end. This is a bit more involved than the endless scroll use case, as it includes the use of both Turbo Stream and Stimulus.&lt;/p&gt;

&lt;p&gt;Let's start with our form first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- app/views/posts/new.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;data-controller=&lt;/span&gt;&lt;span class="s"&gt;"refresh-form"&lt;/span&gt; &lt;span class="na"&gt;data-refresh-form-url=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;refresh_form_posts_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:target&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"new_post"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"form"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- app/views/posts/_form.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;form_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:target&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"refresh-form.form"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="ss"&gt;:kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options_for_select&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="s2"&gt;"News"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:news&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Blog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:blog&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;action: &lt;/span&gt;&lt;span class="s2"&gt;"change-&amp;gt;refresh-form#refreshForm"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;

  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="ss"&gt;:category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options_for_select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;categories_for_kind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The form is simple enough — we display a &lt;code&gt;kind&lt;/code&gt; select with &lt;code&gt;News&lt;/code&gt; and &lt;code&gt;Blog&lt;/code&gt; options. We want to change the available categories' values based on the kind that is selected (assuming that &lt;code&gt;categories_for_kind(@post.kind)&lt;/code&gt; returns the list of categories for the given kind).&lt;/p&gt;

&lt;p&gt;If you look closer, you'll see that we've added some data attributes to the form.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;data-target&lt;/code&gt; will link the form element to the &lt;code&gt;RefreshFormController&lt;/code&gt; Stimulus Controller's &lt;code&gt;form&lt;/code&gt; target.&lt;br&gt;
And the &lt;code&gt;data-action&lt;/code&gt; with the value of &lt;code&gt;change-&amp;gt;refresh-form#refreshForm&lt;/code&gt; will call the &lt;code&gt;refreshForm&lt;/code&gt; method on the linked Stimulus Controller every time the &lt;code&gt;kind&lt;/code&gt; select is changed.&lt;/p&gt;

&lt;p&gt;Let's look at our Stimulus Controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/javascript/controllers/refresh_form_controller.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stimulus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;put&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@rails/request.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;targets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="nx"&gt;refreshForm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formTarget&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;responseKind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;turbo-stream&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On all &lt;code&gt;refreshForm&lt;/code&gt; calls, we just make a new &lt;code&gt;PUT&lt;/code&gt; request to the controller's URL (set using the &lt;code&gt;data-refresh-form-url&lt;/code&gt; on the same element with a &lt;code&gt;data-controller="refresh-form"&lt;/code&gt;).&lt;br&gt;
The important part here is that the &lt;code&gt;responseKind&lt;/code&gt; is set to &lt;code&gt;turbo-stream&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;@rails/request&lt;/code&gt; library understands this response and performs instructions based on the response stream.&lt;/p&gt;

&lt;p&gt;Now all that's left is to return the correct stream from our &lt;code&gt;refresh_form&lt;/code&gt; call for Turbo to understand and update our form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;refresh_form&lt;/span&gt;
    &lt;span class="vi"&gt;@post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post_params&lt;/span&gt;
    &lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;valid?&lt;/span&gt;
    &lt;span class="n"&gt;respond_to&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;turbo_stream&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just update the attributes on the post and mark that you want to respond in a &lt;code&gt;turbo_stream&lt;/code&gt; format (so that it looks up &lt;code&gt;refresh_form.turbo_stream.erb&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- app/views/posts/refresh_form.turbo_stream.erb --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:target&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"form"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this step, we are reusing our &lt;code&gt;form&lt;/code&gt; partial, wrapping it inside a &lt;code&gt;turbo_stream&lt;/code&gt; with a &lt;code&gt;replace&lt;/code&gt; action.&lt;/p&gt;

&lt;p&gt;And that's all you need to get a dynamic form working.&lt;/p&gt;

&lt;p&gt;I know this looks a bit advanced, but the &lt;code&gt;refresh&lt;/code&gt; stimulus controller is a shared part you can now use for all your dynamic forms by adding the correct &lt;code&gt;data-*&lt;/code&gt; attributes.&lt;br&gt;
So essentially, you now get server-side dynamic form refresh without writing any new JS for other forms. Pretty awesome, right?&lt;/p&gt;
&lt;h3&gt;
  
  
  Append Content to Pages Without Reloading
&lt;/h3&gt;

&lt;p&gt;The next use case that Hotwire makes easy is streaming HTML over a WebSocket connection and updating a page with new content as it comes in.&lt;/p&gt;

&lt;p&gt;A good example of this is the GitHub comments section.&lt;br&gt;
You can implement this very easily using Turbo Streams.&lt;/p&gt;

&lt;p&gt;There are two parts to this.&lt;/p&gt;

&lt;p&gt;First, we embed a turbo stream listener on the listing page that opens a WebSocket connection to the server and listens for events.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- app/views/comments/index.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"comments"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_stream_from&lt;/span&gt; &lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;

  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="vi"&gt;@comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="n"&gt;comment&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we update the model to broadcast new comments to the stream.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/coment.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Comment&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt;

  &lt;span class="n"&gt;after_create_commit&lt;/span&gt; &lt;span class="ss"&gt;:stream&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stream&lt;/span&gt;
    &lt;span class="n"&gt;broadcast_prepend_later_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;target: :comments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't need anything else. Turbo will automatically render the &lt;code&gt;app/views/comments/_comment.html.erb&lt;/code&gt; partial for each new comment and send it over a WebSocket connection. It will be picked up by Turbo's JS and prepended to the target with id &lt;code&gt;comments&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's go one step ahead and add an indication to all newly added comments with a small Stimulus Controller.&lt;/p&gt;

&lt;p&gt;First, modify the broadcast and &lt;code&gt;comment&lt;/code&gt; partial to include the controller conditionally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/coment.rb&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stream&lt;/span&gt;
  &lt;span class="n"&gt;broadcast_prepend_later_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                             &lt;span class="ss"&gt;target: :comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                             &lt;span class="ss"&gt;locals: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;highlight: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- app/views/comments/_comment.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="sx"&gt;%s(data-controller="highlight")&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;local_assigns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:highlight&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="err"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This small Stimulus controller adds a special highlight class on connection for 3 seconds and then removes it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;highlight&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;setTimeout&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;highlight&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="mi"&gt;3000&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: You also need to update the CSS highlighting based on the presence of that class.&lt;/p&gt;

&lt;p&gt;Once this controller is done, you can re-use it on anything that requires a highlight class. You could even modify it to get the duration and class name from data attributes if you need that flexibility.&lt;/p&gt;

&lt;p&gt;That's the great thing about Hotwire — it takes you a long way, and you don't have to dip your hands in JS. When you do need to write some JS, Stimulus gives you the tools to build small generic controllers that can be re-used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap Up and Further Reading
&lt;/h2&gt;

&lt;p&gt;The Rails community has been really excited with the introduction of Hotwire, and rightly so.&lt;/p&gt;

&lt;p&gt;In this post, we looked at the key components of Hotwire and how to use Hotwire in your Rails app. We touched on how you can bring your application to life using Turbo and Stimulus.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://hotwired.dev/#screencast"&gt;official Hotwire screencast introduction&lt;/a&gt; and the &lt;a href="https://turbo.hotwired.dev/handbook/introduction"&gt;Turbo documentation&lt;/a&gt; are great places to see what Hotwire and Turbo can do for you.&lt;/p&gt;

&lt;p&gt;For advanced usage, I suggest heading over to the &lt;a href="https://github.com/hotwired/turbo-rails"&gt;turbo-rails GitHub repo&lt;/a&gt;.&lt;br&gt;
Sadly, the documentation is a bit sparse, but if you are not afraid to get your hands dirty, read the code and inline comments in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/hotwired/turbo-rails/blob/main/app/helpers/turbo/frames_helper.rb"&gt;&lt;code&gt;Turbo::FramesHelper&lt;/code&gt;&lt;/a&gt; for &lt;em&gt;Turbo Frames&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hotwired/turbo-rails/blob/main/app/models/concerns/turbo/broadcastable.rb"&gt;&lt;code&gt;Turbo::Broadcastable&lt;/code&gt;&lt;/a&gt; for broadcasting to &lt;em&gt;Turbo Streams&lt;/em&gt; from the code.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hotwired/turbo-rails/blob/main/app/models/turbo/streams/tag_builder.rb"&gt;&lt;code&gt;Turbo::Streams::TagBuilder&lt;/code&gt;&lt;/a&gt; for broadcasting to &lt;em&gt;Turbo Streams&lt;/em&gt; as part of inline controller actions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>A Guide to Event-Driven Architecture in Elixir</title>
      <dc:creator>Sapan Diwakar</dc:creator>
      <pubDate>Tue, 17 May 2022 12:14:37 +0000</pubDate>
      <link>https://dev.to/appsignal/a-guide-to-event-driven-architecture-in-elixir-1cje</link>
      <guid>https://dev.to/appsignal/a-guide-to-event-driven-architecture-in-elixir-1cje</guid>
      <description>&lt;p&gt;In this post, we will explore how event-driven architecture can make your app more responsive for users and decouple your modules for a better developer experience.&lt;/p&gt;

&lt;p&gt;We will also look at several methods of implementing event-driven architecture with Elixir.&lt;/p&gt;

&lt;p&gt;Elixir is particularly good for this because of the advanced and concise message-passing APIs that it offers and BEAM's outstanding support for concurrency.&lt;/p&gt;

&lt;p&gt;But first: what is event-driven architecture, exactly?&lt;/p&gt;

&lt;h2&gt;
  
  
  Event-Driven Architecture: An Introduction
&lt;/h2&gt;

&lt;p&gt;Event-driven architecture is an architecture where events control the behavior and flow of your application.&lt;/p&gt;

&lt;p&gt;The main components of the architecture are event producers, event bus, and event consumers.&lt;/p&gt;

&lt;p&gt;An event could be anything that represents a change of state in the system.&lt;/p&gt;

&lt;p&gt;For example, in an e-commerce application, the purchase of a product by the user could produce a &lt;code&gt;sold&lt;/code&gt; event which the consumer can then process to update inventory.&lt;/p&gt;

&lt;p&gt;An event-based architecture allows applications to act on events as they occur.&lt;/p&gt;

&lt;p&gt;Different parts of an application work and develop relatively independently in well-crafted event-based design. Organizations can assign separate teams to focused parts of the application and streamline the workflow. This also creates a clear boundary between different parts of an application, assisting in future scalability exercises.&lt;/p&gt;

&lt;p&gt;Event-driven architecture has mainly gained popularity with microservice-based products but can also be used for a monolith.&lt;/p&gt;

&lt;p&gt;Things are always clearer with an example, so let's look at one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Blocks of Event-Driven Architecture
&lt;/h2&gt;

&lt;p&gt;Let's discuss each building block in detail, using an e-commerce application as an example.&lt;/p&gt;

&lt;p&gt;Imagine that a user makes a new purchase on a website.&lt;br&gt;
The &lt;code&gt;new order&lt;/code&gt; is an event generated by the part that controls the ordering: the &lt;strong&gt;event producer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The event can be pushed onto an &lt;strong&gt;event bus&lt;/strong&gt;.&lt;br&gt;
The event bus could be anything, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A table in the database.&lt;/li&gt;
&lt;li&gt;An in-memory event queue inside the app.&lt;/li&gt;
&lt;li&gt;An external tool like RabbitMQ or Apache Kafka.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;strong&gt;event consumers&lt;/strong&gt; interested in this type of event can subscribe to the event bus. The event is delivered to them, and they do some processing on top of it.&lt;/p&gt;

&lt;p&gt;For example, an inventory management system would subscribe to the &lt;code&gt;new order&lt;/code&gt; event and update the inventory of the product.&lt;br&gt;
Another system could also pick the same event in the application — for example, a fulfillment service might process that event and create a delivery route for the product.&lt;/p&gt;
&lt;h2&gt;
  
  
  Benefits of Event-Driven Architecture
&lt;/h2&gt;

&lt;p&gt;There are several advantages of using an event-driven architecture instead of one that processes everything sequentially.&lt;/p&gt;

&lt;p&gt;An event-driven architecture allows us to build several independent parts of an application to work off the same event and do different focused tasks.&lt;/p&gt;

&lt;p&gt;This can be advantageous for a couple of reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One team needs to focus on only one part.&lt;/li&gt;
&lt;li&gt;The application code for small parts can be simple.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another advantage of the design is that it allows us to deliver a snappy interface to the user. As an example from the above application, a user needs to make an order.&lt;/p&gt;

&lt;p&gt;The application can continue processing all other non-interactive tasks, like updating its inventory and interacting with the delivery application, without the user's attention.&lt;/p&gt;

&lt;p&gt;This also makes adding new processing steps in the event pipeline very easy. For example, let's say we need an additional task to be performed from an event. We only need to add a new consumer to handle the event, without touching any other parts of the application.&lt;/p&gt;

&lt;p&gt;Finally, it is much easier to scale each individual module if it is decoupled from the others, than to scale a whole application together.&lt;/p&gt;

&lt;p&gt;This is even more beneficial when you have a part that takes much more resources than its counterparts in the event processing pipeline.&lt;/p&gt;

&lt;p&gt;But event-driven architecture does not come without disadvantages when not properly thought out. If applied to very simple problems, it can lead to complex workflows that are slow and difficult to debug.&lt;/p&gt;

&lt;p&gt;Let's explore some simple ways event-driven architecture can be implemented with Elixir, without the need to write complex pieces of code.&lt;/p&gt;
&lt;h2&gt;
  
  
  Synchronous Event-Driven Architecture in Elixir
&lt;/h2&gt;

&lt;p&gt;The simplest (and most inefficient) way to run the above flow would be to do everything synchronously in the user request.&lt;/p&gt;

&lt;p&gt;So, if you have an &lt;code&gt;Orders&lt;/code&gt; module that processes a user's order request, the synchronous implementation could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Orders&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;create_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;save_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_inventory&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;update_inventory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_delivery&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_delivery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can improve this to more easily scale for new event consumers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Orders&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@event_consumers&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;Inventory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:handle_event&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;Delivery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:handle_event&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;create_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;save_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Orders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="ss"&gt;:new_order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;payload:&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nv"&gt;@event_consumers&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this implementation, all we need to do to add new consumers is to add the specification in the &lt;code&gt;@event_consumers&lt;/code&gt; array, and those consumers can work independently.&lt;/p&gt;

&lt;p&gt;While the synchronous approach works well for a small number of consumers, it has a disadvantage. Creating an order might take a long time because you will need to wait for the inventory to update and the delivery to be created.&lt;/p&gt;

&lt;p&gt;These are all internal tasks that can be performed without user interaction and moved from the synchronous chain.&lt;/p&gt;

&lt;p&gt;Let's see how we can further streamline event-driven architecture using &lt;code&gt;GenServer&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using GenServer for Event-Driven Architecture in Elixir
&lt;/h2&gt;

&lt;p&gt;For this implementation, we will run a separate process for each consumer.&lt;br&gt;
These will subscribe to an event from a producer and run their tasks concurrently.&lt;/p&gt;

&lt;p&gt;For the event bus, we can use &lt;a href="https://hexdocs.pm/phoenix_pubsub/Phoenix.PubSub.html"&gt;&lt;code&gt;Phoenix.PubSub&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
Note that it is possible for apps that don't use Phoenix to directly &lt;a href="https://hexdocs.pm/elixir/Registry.html#module-using-as-a-pubsub"&gt;use Registry as a PubSub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, let's look at the producer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Orders&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;create_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;save_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Orders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="ss"&gt;:new_order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;payload:&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PubSub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:my_app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"new_order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a new order, we create the event struct and use &lt;code&gt;Phoenix.PubSub.broadcast/3&lt;/code&gt; to broadcast that event on the bus. As you can see, it is much simpler than the previous implementation where the &lt;code&gt;Orders&lt;/code&gt; module processed tasks from the other module serially.&lt;/p&gt;

&lt;p&gt;The consumers can then subscribe to the &lt;code&gt;new_order&lt;/code&gt; topic and implement the &lt;code&gt;handle_info/2&lt;/code&gt; to be notified every time a new event is published by the producer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Inventory&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;GenServer&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;GenServer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PubSub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:my_app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"new_order"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_info&lt;/span&gt;&lt;span class="p"&gt;(%&lt;/span&gt;&lt;span class="no"&gt;Orders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="ss"&gt;:new_order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;payload:&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;consume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:noreply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Delivery&lt;/code&gt; module will be very similar to the above, so I am skipping it here.&lt;/p&gt;

&lt;p&gt;As you can see, this is much better than the previous implementations. &lt;code&gt;Inventory&lt;/code&gt; and &lt;code&gt;Delivery&lt;/code&gt; modules can independently subscribe to the &lt;code&gt;new_order&lt;/code&gt; topic. The &lt;code&gt;Orders&lt;/code&gt; module broadcasts to this topic on new orders and events are delivered to the subscribed processes.&lt;/p&gt;

&lt;p&gt;You could even distribute this between multiple nodes and &lt;code&gt;Phoenix.PubSub&lt;/code&gt; (with a PG, Redis, or other adapter), spreading the events to all nodes.&lt;/p&gt;

&lt;p&gt;Great, right? Not really. There are several issues with this approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PubSub provides a real-time broadcast without message queueing, so if one of the subscriber processes is down, it might miss the broadcasts.&lt;/li&gt;
&lt;li&gt;If the subscriber does some heavy work, it might not be able to keep up with the incoming messages, resulting in timeouts and consequently crashing the process tree.&lt;/li&gt;
&lt;li&gt;If the subscriber experiences an error when processing a message, it is considered consumed and won't be retried later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So this approach is a bad one to follow for our current use case.&lt;/p&gt;

&lt;p&gt;However, the approach still has its use cases. It can be used for tasks that aren't critical, or that can be corrected with the next message: for example, if a task computes the suggestions for a user's next purchases based on their last purchase.&lt;/p&gt;

&lt;p&gt;While this would also need to be triggered on a new order, it isn't exactly critical (for a traditional e-commerce website) and can re-compute suggestions for a user on their next purchase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Event-Driven Implementation Using GenStage in Elixir
&lt;/h2&gt;

&lt;p&gt;In the previous section, we saw a great implementation of our event-driven system using GenServer. But it didn't come without its limitations. Let's see how &lt;a href="https://hexdocs.pm/gen_stage/GenStage.html"&gt;GenStage&lt;/a&gt; fares.&lt;/p&gt;

&lt;p&gt;GenStage makes a clear distinction between &lt;code&gt;producers&lt;/code&gt;, &lt;code&gt;consumers&lt;/code&gt;, and &lt;code&gt;producer_consumers&lt;/code&gt;, and each process has to pick one when it starts (in its &lt;code&gt;init/1&lt;/code&gt;). In our case, both &lt;code&gt;Inventory&lt;/code&gt; and &lt;code&gt;Deliver&lt;/code&gt; are &lt;code&gt;consumers&lt;/code&gt;, and &lt;code&gt;Orders&lt;/code&gt; is a producer.&lt;/p&gt;

&lt;p&gt;This is where things start to get a little complicated.&lt;br&gt;
&lt;code&gt;GenStage&lt;/code&gt; has a concept of &lt;em&gt;demand&lt;/em&gt;. Each &lt;code&gt;consumer&lt;/code&gt; can issue a &lt;em&gt;demand&lt;/em&gt; of how many events it can handle. The &lt;code&gt;producer&lt;/code&gt; needs to send those events to the consumer.&lt;/p&gt;

&lt;p&gt;Let's see a basic &lt;code&gt;producer&lt;/code&gt; in action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Orders&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;GenStage&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;GenStage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:producer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:some_state_which_does_not_currently_mattere&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;create_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;GenStage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:create_order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_cast&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;:create_order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;save_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:noreply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[%&lt;/span&gt;&lt;span class="no"&gt;Orders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="n"&gt;new_order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;payload:&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_demand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_demand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:noreply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The meat of our code is in &lt;code&gt;handle_cast&lt;/code&gt;, where we save the order and return a tuple like &lt;code&gt;{:noreply, events, new_state}&lt;/code&gt;.&lt;br&gt;
The new events are stored in an internal &lt;code&gt;GenStage&lt;/code&gt; buffer and dispatched to the consumers as they make new demands (or immediately, if there are consumers with unmet demand).&lt;/p&gt;

&lt;p&gt;Let's check out a sample implementation of the &lt;code&gt;consumer&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Inventory&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;GenStage&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;GenStage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:consumer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="ss"&gt;subscribe_to:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;handle_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;&amp;amp;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:noreply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_event&lt;/span&gt;&lt;span class="p"&gt;(%&lt;/span&gt;&lt;span class="no"&gt;Orders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="ss"&gt;:new_order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;payload:&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;new_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;update_inventory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;new_state&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the consumer, first notice that we have a &lt;code&gt;subscribe_to&lt;/code&gt; inside &lt;code&gt;init/1&lt;/code&gt;. This automatically subscribes &lt;code&gt;Inventory&lt;/code&gt; to any events published by &lt;code&gt;Orders&lt;/code&gt;. Please check the &lt;a href="https://hexdocs.pm/gen_stage/GenStage.html#c:init/1"&gt;GenStage documentation for additional options available in init&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here, most of the work happens inside &lt;code&gt;handle_events/3&lt;/code&gt;, which is automatically called by &lt;code&gt;GenStage&lt;/code&gt; as soon as new events become available.&lt;/p&gt;

&lt;p&gt;We handle the &lt;code&gt;new_order&lt;/code&gt; event here, updating the inventory and returning a new state.&lt;/p&gt;

&lt;p&gt;With this simple implementation, we get several benefits that outpace the GenServer implementation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Automatic buffering of events inside GenStage's internal buffer when the producer has new events without any available consumer.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even if consumers are down when some events are produced, we are still guaranteed to receive them when the consumer comes back up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/gen_stage/GenStage.html#module-buffering-demand"&gt;Check out Genstage's guide on buffering demand&lt;/a&gt; for advanced buffering logic.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Automatic distribution of work on multiple consumers.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have heavy consumer tasks, you can start multiple consumer processes. The default &lt;a href="https://hexdocs.pm/gen_stage/GenStage.DemandDispatcher.html"&gt;&lt;code&gt;DemandDispatcher&lt;/code&gt;&lt;/a&gt; for GenStage will distribute the work evenly across all processes.&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://hexdocs.pm/gen_stage/GenStage.Dispatcher.html"&gt;GenStage.Dispatcher&lt;/a&gt; for other dispatch strategies to &lt;a href="https://hexdocs.pm/gen_stage/GenStage.BroadcastDispatcher.html"&gt;distribute events to all consumers&lt;/a&gt; or &lt;a href="https://hexdocs.pm/gen_stage/GenStage.PartitionDispatcher.html"&gt;partition distribution to consumers based on a hash function&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But, as with GenServer or synchronous implementation, using GenStage does not come without its problems.&lt;/p&gt;

&lt;p&gt;If a consumer crashes while it is processing an event, GenStage will consider the event delivered and will not send it again when the consumer comes back up.&lt;/p&gt;

&lt;p&gt;To make sure that you properly track crashes, you can use a monitoring service &lt;a href="https://www.appsignal.com/"&gt;like AppSignal&lt;/a&gt;. AppSignal is &lt;a href="https://www.appsignal.com/elixir"&gt;easy to install for your Elixir app&lt;/a&gt; and helps you monitor performance as well as &lt;a href="https://www.appsignal.com/tour/errors"&gt;track errors&lt;/a&gt;. Here's an example of an error tracking dashboard that AppSignal provides:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JFhOaAe_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2022-05/errors-appsignal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JFhOaAe_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2022-05/errors-appsignal.png" alt="AppSignal Errors Screenshot" width="880" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can set up notifications for crashes via AppSignal as well.&lt;/p&gt;

&lt;p&gt;On the app side, you can cache such events in a persistent store once they are delivered to the consumer. If the consumer crashes, then once it recovers, it can revert to the cached events.&lt;/p&gt;

&lt;p&gt;Be very cautious about producing too many events without enough consumers, though. While GenStage offers automatic buffering of events, this buffer has a (configurable) maximum size and a practical maximum size constrained by the server's memory.&lt;/p&gt;

&lt;p&gt;If you don't control the frequency with which events are produced, consider using an external data store like Redis or Postgres to buffer events.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap Up: Event-Driven Architecture in Elixir — Going Beyond GenStage
&lt;/h2&gt;

&lt;p&gt;In this post, we examined three approaches to implementing an event-driven system in Elixir: synchronously, using GenServer, and finally, using GenStage. We took a look at some of the advantages and disadvantages of each approach.&lt;/p&gt;

&lt;p&gt;The simple &lt;code&gt;GenStage&lt;/code&gt; example can be a starting point for implementing complex event-driven data processing pipelines that span multiple nodes. I suggest you &lt;a href="https://hexdocs.pm/gen_stage/GenStage.html"&gt;read the great GenStage documentation for more information&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are looking for an even higher-level abstraction, &lt;a href="https://elixir-broadway.org/"&gt;Broadway&lt;/a&gt; is a good starting point. It is built on top of GenStage and offers several additional features, including consuming data from external queues like Amazon SQS, Apache Kafka, and RabbitMQ.&lt;/p&gt;

&lt;p&gt;Until next time, happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, &lt;a href="https://dev.to/elixir-alchemy"&gt;subscribe to our Elixir Alchemy newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
    </item>
    <item>
      <title>Delayed Job vs. Sidekiq: Which Is Better?</title>
      <dc:creator>Sapan Diwakar</dc:creator>
      <pubDate>Tue, 08 Mar 2022 12:44:49 +0000</pubDate>
      <link>https://dev.to/appsignal/delayed-job-vs-sidekiq-which-is-better-1k47</link>
      <guid>https://dev.to/appsignal/delayed-job-vs-sidekiq-which-is-better-1k47</guid>
      <description>&lt;p&gt;Most applications need background jobs for mailers, regular clean-ups, or any other time-consuming operation that doesn't require a user to be present.&lt;/p&gt;

&lt;p&gt;Several gems support job queues and background processing in the Rails world — &lt;a href="https://github.com/collectiveidea/delayed_job"&gt;Delayed Job&lt;/a&gt; and &lt;a href="https://github.com/mperham/sidekiq"&gt;Sidekiq&lt;/a&gt; being the two most popular ones.&lt;/p&gt;

&lt;p&gt;In this post, we will take a detailed look at Delayed Job and Sidekiq, including how they fare against each other.&lt;/p&gt;

&lt;p&gt;Let's go!&lt;/p&gt;

&lt;h2&gt;
  
  
  A Quick Introduction to Delayed Job
&lt;/h2&gt;

&lt;p&gt;Delayed Job is a direct extraction from Shopify and uses a table to maintain all background jobs. It follows a very simple pattern. Any Ruby object that responds to a &lt;code&gt;perform&lt;/code&gt; method can be enqueued in the jobs table.&lt;/p&gt;

&lt;p&gt;In addition, if you don't need to maintain special job objects (although this is highly recommended for testability and clear separation of long-running operations), it also allows you to call &lt;code&gt;.delay.method(params)&lt;/code&gt; on any Ruby object. It will process the method in the background.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/collectiveidea/delayed_job#queuing-jobs"&gt;Delayed Job README&lt;/a&gt; does a great job of explaining all common usage patterns.&lt;/p&gt;

&lt;p&gt;Many teams choose Delayed Job because it is simple and uses their already existing database. They don't need to spend/maintain other resources.&lt;/p&gt;

&lt;p&gt;However, it will still take up space in your database table. If you have too many jobs queued at the same time, you might need more disk space to accommodate them all.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Quick Introduction to Sidekiq
&lt;/h2&gt;

&lt;p&gt;Sidekiq, on the other hand, uses Redis as its data store to maintain all job metadata. This comes with the obvious benefit of being much faster than the regular database systems Delayed Jobs uses. In addition to this, each Sidekiq process spawns multiple threads to process the jobs even faster.&lt;/p&gt;

&lt;p&gt;For each background job in Sidekiq, we need a specialized class that includes the &lt;code&gt;Sidekiq::Worker&lt;/code&gt; concern and responds to the &lt;code&gt;perform&lt;/code&gt; method. To enqueue the job, we need to call &lt;code&gt;perform_async(arg1, arg2)&lt;/code&gt; on the worker with the arguments.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/mperham/sidekiq/wiki/Getting-Started"&gt;Sidekiq 'Getting Started' guide&lt;/a&gt; explains this and other usage patterns in good detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Active Job with Delayed Job or Sidekiq
&lt;/h2&gt;

&lt;p&gt;Rails already provides a mature job framework for top-level declaration and handling of jobs. Both Delayed Job and Sidekiq support running jobs through ActiveJob's unified API. Just inherit from &lt;code&gt;ApplicationJob&lt;/code&gt; and call &lt;code&gt;perform_later&lt;/code&gt; on your job class to enqueue the job to the configured queuing backend.&lt;/p&gt;

&lt;p&gt;The advantage of running jobs with Active Job is that your application code becomes framework agnostic, and switching from Delayed Job to Sidekiq (or vice versa) becomes pretty easy. The &lt;a href="https://edgeapi.rubyonrails.org/classes/ActiveJob/TestHelper.html"&gt;&lt;code&gt;ActiveJob::TestHelper&lt;/code&gt;&lt;/a&gt; also makes testing enqueued jobs a breeze.&lt;/p&gt;

&lt;p&gt;But the abstraction provided by Active Job also comes with a performance overhead, as job data has to be wrapped before it's pushed to the store.&lt;/p&gt;

&lt;p&gt;Sidekiq claims that ActiveJob is about 2-20x slower when pushing to Redis, with ~3x the processing overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delayed Jobs vs. Sidekiq
&lt;/h2&gt;

&lt;p&gt;Now that we know the basics of Delayed Jobs and Sidekiq, let's dive deeper into their differences and what each brings to the table.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Features
&lt;/h3&gt;

&lt;p&gt;For basic applications, both Sidekiq and Delayed Job provide a good set of features out of the box.&lt;/p&gt;

&lt;p&gt;These include assigning job priorities, named queues, and auto-retry on failures.&lt;/p&gt;

&lt;p&gt;Delayed Job also provides a way to configure max run time out of the box (Sidekiq does not).&lt;/p&gt;

&lt;p&gt;Sidekiq, on the other hand, provides support for &lt;a href="https://github.com/mperham/sidekiq/wiki/Middleware"&gt;Middleware&lt;/a&gt; to update job metadata, skip queuing a job, or execute a job.&lt;/p&gt;

&lt;p&gt;Sidekiq supports more callbacks, though &lt;a href="https://github.com/collectiveidea/delayed_job/#hooks"&gt;some hooks are available for Delayed Job apps&lt;/a&gt;. Instead of callbacks, you can use Delayed Job with Active Job (namely, the &lt;code&gt;before_enqueue&lt;/code&gt; and &lt;code&gt;around_perform&lt;/code&gt; callbacks inbuilt into Rails).&lt;/p&gt;

&lt;p&gt;Web UI is another feature that comes out of the box with Sidekiq. This provides historical statistics about jobs and information about workers, currently enqueued and dead jobs. You can perform operations like deleting or running jobs immediately without going through the console.&lt;/p&gt;

&lt;p&gt;Delayed Job does not have an inbuilt Web UI, but &lt;a href="https://github.com/ejschmitt/delayed_job_web"&gt;&lt;code&gt;delayed_job_web&lt;/code&gt;&lt;/a&gt; gives access to a basic Web UI with similar features to Sidekiq's.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sidekiq Wins at Performance
&lt;/h3&gt;

&lt;p&gt;Performance-wise, Sidekiq beats Delayed Job quite convincingly. According to &lt;a href="https://github.com/mperham/sidekiq#performance"&gt;Sidekiq's open-source benchmark&lt;/a&gt;, it is approximately 30x faster than Delayed Job.&lt;/p&gt;

&lt;p&gt;There are two major reasons for this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Redis is much faster at querying data than traditional databases like Postgres because it stores data in memory as opposed to the disk.&lt;/li&gt;
&lt;li&gt;Delayed Job runs a single thread to process jobs, compared to Sidekiq, which uses multiple threads.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While all of this looks great on paper, the differences do not matter much unless you work on a big scale (something like 10k jobs per minute).&lt;/p&gt;

&lt;p&gt;The exact number also depends on the average run time of a job. The longer the run time, the less the performance overhead of Delayed Job matters.&lt;/p&gt;

&lt;p&gt;If you're worried about the performance of Delayed Job, you can make some performance optimizations. The exact indexes to use will depend on the statistics of your job system.&lt;/p&gt;

&lt;p&gt;For example, if you use multiple queues and only one gets a major chunk of jobs, a simple index on the queue column (&lt;code&gt;add_index :delayed_jobs, :queue&lt;/code&gt;) can significantly improve performance.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In AppSignal, &lt;a href="https://www.appsignal.com/ruby/sidekiq-monitoring"&gt;Sidekiq magic dashboard&lt;/a&gt; gets automatically generated and it enables you to monitor queue length, queue latency, job duration, job statuses, memory usage, etc.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Odn0qsZu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2022-02/sidekiq-dashboard.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Odn0qsZu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2022-02/sidekiq-dashboard.png" alt="Sidekiq" width="880" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;p&gt;Both Delayed Job and Sidekiq have a similar deployment strategy for workers.&lt;/p&gt;

&lt;p&gt;Using Heroku, you just need to add entries inside your &lt;code&gt;Procfile&lt;/code&gt; to start the job processor and run the workers.&lt;/p&gt;

&lt;p&gt;For Sidekiq:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;worker: bundle &lt;span class="nb"&gt;exec &lt;/span&gt;sidekiq &lt;span class="nt"&gt;-t&lt;/span&gt; 25 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SIDEKIQ_CONCURRENCY&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;5&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-q&lt;/span&gt; name,priority &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-q&lt;/span&gt; another_queue,another_priority]]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Delayed Job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;worker: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;QUEUE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;x,y,z] bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rails &lt;span class="nb"&gt;jobs&lt;/span&gt;:work
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Memory
&lt;/h3&gt;

&lt;p&gt;Here's where things start to get a bit more interesting. Sidekiq has a concurrency option to control how many threads it runs.&lt;/p&gt;

&lt;p&gt;Most of the Sidekiq vs. Delayed Job benchmarks mention Sidekiq's very high concurrency of up to 25 threads, which contributes to its super-fast performance.&lt;/p&gt;

&lt;p&gt;But in a real setting, you have to limit the threads to something more conservative. The actual number depends on how heavy your application is and what kinds of jobs you perform.&lt;/p&gt;

&lt;p&gt;What I have seen in practice is that if you run a worker on 512MB memory (equivalent to &lt;code&gt;standard-1x&lt;/code&gt; on Heroku), the number of threads is somewhere between 2 and 5 instead of 25.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.mikeperham.com/2018/04/25/taming-rails-memory-bloat/"&gt;'Taming Rails memory bloat' by Mike Perham&lt;/a&gt;, the creator of Sidekiq, discusses memory issues in more detail and is well worth a read.&lt;/p&gt;

&lt;p&gt;I won't jump into the full discussion, but he recommends that you set &lt;code&gt;MALLOC_ARENA_MAX=2&lt;/code&gt; on all workers that run Sidekiq.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;jemalloc&lt;/code&gt; instead of regular &lt;code&gt;malloc&lt;/code&gt; helps too. The exact way to do this depends on the platform you use, but it is pretty simple on Heroku. Just set &lt;a href="https://github.com/gaffneyc/heroku-buildpack-jemalloc.git"&gt;heroku-buildpack-jemalloc&lt;/a&gt; as the first buildpack (ahead of the &lt;code&gt;heroku/ruby&lt;/code&gt; buildpack).&lt;/p&gt;

&lt;h3&gt;
  
  
  Delayed Job Uses Simpler Resources
&lt;/h3&gt;

&lt;p&gt;As we discussed, Delayed Job runs on your existing database instance.&lt;/p&gt;

&lt;p&gt;You might need to increase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the available memory&lt;/li&gt;
&lt;li&gt;disk space&lt;/li&gt;
&lt;li&gt;max connections&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;depending on the job load or the number of workers you run. But the only resource you need is the job processor.&lt;/p&gt;

&lt;p&gt;On the other hand, Sidekiq requires a Redis instance to handle jobs. If you also use Redis as a cache store, it is recommended that you use a separate instance configured as a "persistent store" for Sidekiq jobs.&lt;/p&gt;

&lt;p&gt;Since Redis works best when everything fits in memory, if you have too many jobs (for example, if Sidekiq stops processing them for some time due to an issue in the app), it might take some downtime to clear everything up.&lt;/p&gt;

&lt;p&gt;This is especially troublesome if you have Redis on the same server as your app. They will start competing for memory, leading to swapping and eventually destroying your app's performance.&lt;/p&gt;

&lt;p&gt;One important point to note about Redis is that it has to be configured with &lt;code&gt;maxmemory-policy noeviction&lt;/code&gt; to avoid silent drops of Sidekiq's data. Otherwise, you will find yourself missing jobs that need to be performed, without any trace.&lt;/p&gt;

&lt;h4&gt;
  
  
  A Side-Note: Paid Upgrades in Sidekiq
&lt;/h4&gt;

&lt;p&gt;If you need extra features, Sidekiq comes with &lt;code&gt;Pro&lt;/code&gt; and &lt;code&gt;Enterprise&lt;/code&gt; versions.&lt;/p&gt;

&lt;p&gt;The most notable addition to Pro is &lt;code&gt;Batch Jobs&lt;/code&gt; that can run in parallel, be monitored, and interact as a group, invoking a callback when all jobs are done. Pro also has improved reliability features to ensure that no jobs are dropped silently, even during network problems.&lt;/p&gt;

&lt;p&gt;The Enterprise version comes with yet more features. If you are looking for something that a regular Sidekiq installation can't solve, &lt;a href="https://sidekiq.org/"&gt;explore the paid Sidekiq features&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In practice, the free version of Sidekiq still works great.&lt;br&gt;
But it is good to know that there are paid options you can upgrade to as needed, instead of switching to a different solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Community and Development Status: Sidekiq Has the Edge
&lt;/h3&gt;

&lt;p&gt;There is a huge community behind both Sidekiq and Delayed Job. However, it is not always easy to find quick answers to your questions in StackOverflow or the official documentation.&lt;/p&gt;

&lt;p&gt;On the development side, things are not looking very bright for Delayed Job. There was some minor work done on Delayed Job in December 2021 and January 2022, but it doesn't seem like it is getting any major developments going forward. It appears to be in maintenance-only mode, and there are a lot of open issues on Github.&lt;/p&gt;

&lt;p&gt;In contrast, Sidekiq is still under active development, and its creator is working full time on it. There are very few open issues, and they get addressed regularly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-up: Sidekiq or Delayed Job? It Depends on Your Needs
&lt;/h2&gt;

&lt;p&gt;In this post, we covered two major job processing systems for Rails applications — Sidekiq and Delayed Job — taking a look at some of their pros and cons.&lt;/p&gt;

&lt;p&gt;There are different use cases for each. It all depends on the budget and scale of your operation.&lt;/p&gt;

&lt;p&gt;If performance and long-term maintainability are of importance, Sidekiq is a no-brainer. On the other hand, if running costs are a concern, Delayed Job can help you there.&lt;/p&gt;

&lt;p&gt;Whether you choose Delayed Job or Sidekiq, good luck with your project and happy coding!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👋 If you liked this article, take a look at other Ruby (on Rails) performance articles in our &lt;a href="https://www.appsignal.com/ruby#ruby-monitoring-checklist"&gt;Ruby performance monitoring checklist&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>rails</category>
    </item>
    <item>
      <title>Authorization and Policy Scopes for Phoenix Apps</title>
      <dc:creator>Sapan Diwakar</dc:creator>
      <pubDate>Tue, 16 Nov 2021 15:09:20 +0000</pubDate>
      <link>https://dev.to/appsignal/authorization-and-policy-scopes-for-phoenix-apps-19h9</link>
      <guid>https://dev.to/appsignal/authorization-and-policy-scopes-for-phoenix-apps-19h9</guid>
      <description>&lt;p&gt;Authorization (not to be confused with authentication) is vital to every application but often isn't given much thought before implementation. The &lt;a href="https://datatracker.ietf.org/doc/html/rfc2196"&gt;IETF Site Security Handbook&lt;/a&gt; defines authorization as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The process of granting privileges to processes and, ultimately, users. This differs from authentication in that authentication is the process used to identify a user. Once identified (reliably), the privileges, rights, property, and permissible actions of the user are determined by authorization.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, in short, authorization is about defining access policies and scoping.&lt;/p&gt;

&lt;p&gt;For example, consider a platform like Github.&lt;br&gt;
In very simple terms, it must handle which repositories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a particular user is allowed to read (this is policy scoping)&lt;/li&gt;
&lt;li&gt;the user is allowed to write to (authorization)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you come from the Rails world, you might be familiar with some gems that provide APIs to handle this, the most popular ones being &lt;a href="https://github.com/CanCanCommunity/cancancan"&gt;cancancan&lt;/a&gt; and &lt;a href="https://github.com/varvet/pundit"&gt;pundit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In today's post, we'll take a close look at the two critical components of authorization — access policies and scoping. I'll show you how you can roll out your own solution for each in Phoenix and how to leverage the Bodyguard library for quick and easy authorization.&lt;/p&gt;
&lt;h2&gt;
  
  
  Authorization in Phoenix
&lt;/h2&gt;

&lt;p&gt;There are two parts to authorization that we need to keep in mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Access policy — Is &lt;em&gt;this user&lt;/em&gt; allowed to perform &lt;em&gt;this&lt;/em&gt; operation on &lt;em&gt;this resource&lt;/em&gt;?&lt;/li&gt;
&lt;li&gt;Policy scope — &lt;em&gt;Which resources&lt;/em&gt; is &lt;em&gt;this user&lt;/em&gt; allowed to see?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While it is definitely possible to roll out something by hand, it usually makes sense not to reinvent the wheel if well-maintained and tested libraries are available.&lt;br&gt;
&lt;a href="https://github.com/jarednorman/canada"&gt;Canada&lt;/a&gt; and &lt;a href="https://hexdocs.pm/bodyguard/Bodyguard.html"&gt;Bodyguard&lt;/a&gt; are two of the more popular ones that I have seen in the community.&lt;/p&gt;

&lt;p&gt;Let's see what implementation might look like for our own solution and also with Bodyguard. We will use a CMS example similar to what the &lt;a href="https://hexdocs.pm/phoenix/contexts.html"&gt;official Phoenix Context guide&lt;/a&gt; uses. This CMS allows users to create pages and share them with everyone. Only the author should be able to edit, update, or delete a page once created, but everyone else should see the page.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementing Access Policies
&lt;/h2&gt;

&lt;p&gt;Getting back to our CMS example — when the user is on a page, we need an access policy that decides if the user is allowed to perform an action (say, &lt;code&gt;edit&lt;/code&gt;) on the page.&lt;/p&gt;
&lt;h3&gt;
  
  
  Roll Your Own Access Policy
&lt;/h3&gt;

&lt;p&gt;The following is what the official Phoenix guide suggests. This is also what most of us would do, were we rolling out our own authorization solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CMS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PageController&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="ss"&gt;:authorize_page&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:edit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:delete&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;authorize_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CMS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_page!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;author_id&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;conn&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;put_flash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"You can't modify that page"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;to:&lt;/span&gt; &lt;span class="no"&gt;Routes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cms_page_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;halt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The implementation is straightforward. We use the &lt;code&gt;:authorize_page&lt;/code&gt; plug for edit, update, or delete actions. In that plug, we allow the action only if the page's author is the same as the current user. Otherwise, we redirect to an index page that shows an error.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Bodyguard
&lt;/h3&gt;

&lt;p&gt;We can also implement an access policy using &lt;code&gt;Bodyguard.Policy&lt;/code&gt; behavior. Depending on the level of access scoping you need, this behavior could be placed on the controller or directly on the underlying context. I usually like to define a separate &lt;code&gt;Policy&lt;/code&gt; module to handle this and then delegate the methods from the behavior's target:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CMS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Policy&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@behaviour&lt;/span&gt; &lt;span class="no"&gt;Bodyguard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Policy&lt;/span&gt;

  &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;

  &lt;span class="c1"&gt;# Super Admins can do anything&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;role:&lt;/span&gt; &lt;span class="ss"&gt;:super_admin&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;_params&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# Users can list/get/create anything&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;role:&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="sx"&gt;~w[index show create]a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# Users can edit/update/delete own pages&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;role:&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;author_id:&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="sx"&gt;~w[update edit delete]a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

   &lt;span class="c1"&gt;# Default blacklist&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_params&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we see that we defined &lt;code&gt;authorize(action, user, params)&lt;/code&gt; that returns &lt;code&gt;true&lt;/code&gt;/&lt;code&gt;false&lt;/code&gt; to permit (or not) the action on the resource. You can also get additional control on the error messages by returning  &lt;code&gt;{:error, reason}&lt;/code&gt; instead of just &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Bodyguard.Policy&lt;/code&gt; behavior expects the callbacks to return:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;:ok&lt;/code&gt; or &lt;code&gt;true&lt;/code&gt; to permit an action.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:error&lt;/code&gt;, &lt;code&gt;{:error, reason}&lt;/code&gt;, or &lt;code&gt;false&lt;/code&gt; to deny an action.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, on the context or the controller, all you need is to delegate the &lt;code&gt;authorize&lt;/code&gt; method to our &lt;code&gt;Policy&lt;/code&gt; module and then call &lt;code&gt;Bodyguard.permit&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CMS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PageController&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="ss"&gt;:authorize_page&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;authorize_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CMS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Bodyguard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="ss"&gt;:ok&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_reason&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;put_flash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"You can't access that page"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;to:&lt;/span&gt; &lt;span class="no"&gt;Routes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cms_page_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;halt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defdelegate&lt;/span&gt; &lt;span class="n"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="ss"&gt;to:&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CMS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Policy&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To authorize an action, we can call &lt;code&gt;Bodyguard.permit(Hello.CMS.PageController, action, user)&lt;/code&gt;.&lt;br&gt;
&lt;code&gt;Bodyguard.permit/4&lt;/code&gt; also accepts passing a fourth argument's authorization in the actual resource.&lt;/p&gt;

&lt;p&gt;This form can be used to authorize actions performed on a single resource (for example, &lt;code&gt;update&lt;/code&gt; or &lt;code&gt;delete&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Under the hood, &lt;code&gt;Bodyguard.permit&lt;/code&gt; calls &lt;code&gt;authorize&lt;/code&gt; on the module we provided as the first argument.&lt;/p&gt;

&lt;p&gt;The return value is then normalized into &lt;code&gt;:ok&lt;/code&gt; or &lt;code&gt;{:error, reason}&lt;/code&gt; (regardless of whether we were returning &lt;code&gt;true&lt;/code&gt;/&lt;code&gt;:ok&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;/&lt;code&gt;:error&lt;/code&gt;/&lt;code&gt;{:error, reason}&lt;/code&gt; from that method).&lt;/p&gt;

&lt;p&gt;While delegating &lt;code&gt;authorize&lt;/code&gt; from the controller works well, there might be cases when you need similar access control in multiple places. For example, the same resource could be accessed from your controller and through an &lt;a href="https://hexdocs.pm/absinthe/schemas.html"&gt;Absinthe Resolver&lt;/a&gt; exposed through the GraphQL API.&lt;/p&gt;

&lt;p&gt;For such cases, I suggest delegating &lt;code&gt;authorize/3&lt;/code&gt; on the Phoenix context module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CMS&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;defdelegate&lt;/span&gt; &lt;span class="n"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="ss"&gt;to:&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CMS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Policy&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, when you need to call &lt;code&gt;Bodyguard.permit&lt;/code&gt; from your controller (or the Absinthe Resolver), pass in a first argument of that context module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CMS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PageController&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;authorize_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CMS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Bodyguard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CMS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="ss"&gt;:ok&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;# OK. Render page&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;# Error. Show flash and redirect&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is also very easy to write tests for the above policy. Here's what the tests might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"allows users to list pages"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Fixtures&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Bodyguard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CMS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"allows user to update/delete own pages"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Fixtures&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page_fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Bodyguard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CMS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"allows user to create pages"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Fixtures&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Bodyguard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CMS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"does not allow user to update/delete pages of someone else"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;user1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Fixtures&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;user2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Fixtures&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page_fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:unauthorized&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Bodyguard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CMS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"allows super_admin to do anything"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;super_admin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Fixtures&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user_super_admin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Fixtures&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page_fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Bodyguard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CMS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;super_admin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Bodyguard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CMS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;super_admin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Bodyguard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CMS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;super_admin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementing Policy Scopes
&lt;/h2&gt;

&lt;p&gt;We've now allowed users to access resources based on some attributes. The next part of the puzzle is to handle the listing of resources. As you might have noticed above, we are allowing users to list everything.&lt;/p&gt;

&lt;p&gt;But you might have specific business requirements on what a user can and can't list. For example, in the Github example, users can only see public repositories and the repositories that they have access to (e.g., through their organization or team). Similarly, you could set a restriction that users see all published posts but only their own drafts in a CMS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Roll Your Own Policy Scoping
&lt;/h3&gt;

&lt;p&gt;This just boils down to using the correct queries based on the user and their access rights. For example, a simple implementation inside a context could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CMS&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;list_pages&lt;/span&gt;&lt;span class="p"&gt;(%{&lt;/span&gt;&lt;span class="ss"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Page&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;author_id:&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;or_where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;state:&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is definitely possible to do this with simple Ecto queries on the accessor methods. However, it usually makes much more sense to centralize these kinds of domain requirements so that future developers don't forget to include one of the requirements while adding a new feature.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Bodyguard
&lt;/h3&gt;

&lt;p&gt;With Bodyguard, we can provide default scoping to query items that a user can access. Implement a &lt;code&gt;scope/3&lt;/code&gt; function inside an &lt;code&gt;Ecto.Schema&lt;/code&gt; module from the &lt;code&gt;@behaviour Bodyguard.Schema&lt;/code&gt;. The function should filter the &lt;code&gt;query&lt;/code&gt; down to only include the resources the user is allowed to access. You can also pass custom params when invoking the scoping to provide further filtering.&lt;/p&gt;

&lt;p&gt;Here's how it looks in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CMS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Page&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;

  &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;scope_published&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Signed in users can access published posts or their own posts (in any state)&lt;/span&gt;
  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;scope_published&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;role:&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;author_id:&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;or_where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;state:&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Super admins can access anything&lt;/span&gt;
  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;scope_published&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;role:&lt;/span&gt; &lt;span class="ss"&gt;:super_admin&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;_params&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;

  &lt;span class="c1"&gt;# Anonymous users can access only published posts&lt;/span&gt;
  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;scope_published&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_params&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;state:&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, this can then be used inside your context when querying. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CMS&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;list_pages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Page&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Bodyguard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;-- defers to Page.scope/3&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we need to add another listing of the pages later, we can simply use the same &lt;code&gt;Bodyguard.scope(query, user)&lt;/code&gt; call and know that everything will be appropriately scoped.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authorization in Phoenix: Further Reading
&lt;/h2&gt;

&lt;p&gt;In this post, we saw how to implement authorization in your Phoenix apps.&lt;/p&gt;

&lt;p&gt;We focused on a simple CMS example to explore authorization with — and without — external libraries.&lt;/p&gt;

&lt;p&gt;I recommend &lt;a href="https://dockyard.com/blog/2017/08/01/authorization-for-phoenix-contexts"&gt;Dockyard's Authorization Considerations For Phoenix Contexts blog post&lt;/a&gt; for a more detailed look at rolling out your authorization solution.&lt;/p&gt;

&lt;p&gt;If you are looking for external libraries, &lt;a href="https://hexdocs.pm/bodyguard/Bodyguard.html"&gt;check out Bodyguard&lt;/a&gt;.&lt;br&gt;
I have been using it in production for a while and can vouch for its customizability in authorization. It stays out of the way when we don't want it and also clarifies operations that would otherwise be scattered inside the context and controller methods.&lt;/p&gt;

&lt;p&gt;I hope you've found this a useful guide that's inspired you to dive into policy scoping and authorization in Phoenix apps.&lt;/p&gt;

&lt;p&gt;Until next time, happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/category/elixir-alchemy.html#elixir-alchemy"&gt;subscribe to our Elixir Alchemy newsletter and never miss a single post!&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sapan Diwakar is a full-stack developer. He writes about his interests &lt;a href="https://sapandiwakar.in/"&gt;on his blog&lt;/a&gt; and is a big fan of keeping things simple, in life and in code. When he’s not working with technology, he loves to spend time in the garden, hiking around forests, and playing outdoor sports.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
    </item>
  </channel>
</rss>
