<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Phoenix framework - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Phoenix framework - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 16 Jun 2026 21:36:14 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/phoenix/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How I Contributed to a Major Open Source Project Without Writing Any Code ]]>
                </title>
                <description>
                    <![CDATA[ By Adam Gordon Bell I recently got a pull request merged into the popular Phoenix Framework, and I did it without writing any Elixir code. I didn't write any documentation either. What I did was help improve the build process. In this post, I'd like ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/open-source-continuous-integration/</link>
                <guid isPermaLink="false">66d45d59677cb8c6c15f314a</guid>
                
                    <category>
                        <![CDATA[ community ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Continuous Integration ]]>
                    </category>
                
                    <category>
                        <![CDATA[ open source ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Phoenix framework ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 12 Jan 2021 18:48:35 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/01/Screen-Shot-2021-01-12-at-9.56.22-AM.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Adam Gordon Bell</p>
<p>I recently got a pull request merged into the popular Phoenix Framework, and I did it without writing any Elixir code. I didn't write any documentation either. What I did was help improve the build process.</p>
<p>In this post, I'd like to share the improvements I made to their build process. These improvements are not Phoenix Framework-specific and they might change the way you approach continuous integration.</p>
<p>But first, some background.</p>
<h2 id="heading-what-is-the-phoenix-framework">What is the Phoenix Framework?</h2>
<p>Phoenix is a web framework with some very interesting properties. With Phoenix, you can build rich interactive web applications without writing client-side code. </p>
<p>You can do this using a feature called LiveView which sends real-time updates from the server to update the client browser's HTML.</p>
<p>We can create a page that shows the latest tweets on a topic, in real-time, quite easily.</p>
<p>Here is an example:</p>
<pre><code class="lang-elixir"><span class="hljs-class"><span class="hljs-keyword">defmodule</span> <span class="hljs-title">TimelineLive</span></span> <span class="hljs-keyword">do</span>
  <span class="hljs-keyword">use</span> Phoenix.LiveView

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">render</span></span>(assigns) <span class="hljs-keyword">do</span>
    render(<span class="hljs-string">"timeline.html"</span>, assigns)
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">mount</span></span>(_, socket) <span class="hljs-keyword">do</span>
    Twitter.subscribe(<span class="hljs-string">"elixirphoenix"</span>)
    {<span class="hljs-symbol">:ok</span>, assign(socket, <span class="hljs-symbol">:tweets</span>, [])}
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">handle_info</span></span>({<span class="hljs-symbol">:new</span>, tweet}, socket) <span class="hljs-keyword">do</span>
    {<span class="hljs-symbol">:noreply</span>,
     update(socket, <span class="hljs-symbol">:tweets</span>, <span class="hljs-keyword">fn</span> tweets -&gt;
       Enum.take([tweet | tweets], <span class="hljs-number">10</span>)
     <span class="hljs-keyword">end</span>)}
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p><img src="https://firebasestorage.googleapis.com/v0/b/firescript-577a2.appspot.com/o/imgs%2Fapp%2FCorecursive%2FDUy3Kzmdsn.png?alt=media&amp;token=edf6aee0-7744-435e-9e3f-0557e000214e" alt="Image" width="498" height="770" loading="lazy">
<em>Real-Time Twitter Results with No Javascript Written</em></p>
<p>The framework is written in the programming language Elixir</p>
<p>It was created by José Valim. It looks a lot like Ruby but has very different semantics. Elixir runs on the Erlang VM, and it powers projects like Discord and is used at companies like Heroku.</p>
<h2 id="heading-how-to-reproduce-the-builds">How to Reproduce the Builds</h2>
<p>The Phoenix Framework uses GitHub Actions for their build pipeline. Like many great projects, they have a suite of unit tests that they need to run on every user contribution. </p>
<p>This isn't where their testing efforts stop though. They also have a suite of integration tests. Phoenix uses an ORM to talk to various databases and the integration tests ensure that no changes break the integration with any of the 3 supported databases.</p>
<p>This is a common pattern. Having a large number of unit tests that are easy to run and well as a handful of slower but more comprehensive integration tests is a great way to prevent bugs from being introduced into the project.</p>
<p>The Phoenix Framework takes this even further, though, as they also need to support several versions of the Elixir language and a handful of versions of Open Telecom Platform (OTP).</p>
<p>This is starting to sound complex. We have to test each change with all combinations of the following:</p>
<ul>
<li>Databases (Postgres, MySQL MSSQL)</li>
<li>Elixir (Current and Previous Version)</li>
<li>OTP (Current and Previous Version)</li>
</ul>
<p>It's relatively easy to set this up in GitHub Actions, but how would you run these tests locally? </p>
<p>Installing all these would be a lot to ask, so contributors tend to rely on GitHubActions to test these combinations. However, if everyone has to rely on pushing things to GitHub it see if the tests pass then development gets slower.</p>
<p>How do we fix this?</p>
<h2 id="heading-how-to-unify-the-test-runs">How to Unify the Test Runs</h2>
<p>This is where I got involved. I work at Earthly Technologies as an open-source developer advocate. We have a pretty cool open-source build tool, and although I occasionally contribute directly to the project my job is to be the contact point between the community using the tool and the team working on it.</p>
<p>I had heard about this reproducibility problem the Phoenix team was having. I thought I could help write a build script that could be used both in GitHub Actions and for a local development workflow. So I set to work on a PR.</p>
<h3 id="heading-running-the-tests-locally">Running The Tests Locally</h3>
<p>What I ended up creating, slightly simplified, is this:</p>
<pre><code class="lang-dockerfile">setup:
   <span class="hljs-keyword">ARG</span> ELIXIR=<span class="hljs-number">1.10</span>.<span class="hljs-number">4</span>
   <span class="hljs-keyword">ARG</span> OTP=<span class="hljs-number">23.0</span>.<span class="hljs-number">3</span>
   <span class="hljs-comment"># Pull a Docker Image to Run Build Inside Of</span>
   <span class="hljs-keyword">FROM</span> hexpm/elixir:$ELIXIR-erlang-$OTP-alpine-<span class="hljs-number">3.12</span>.<span class="hljs-number">0</span>
   ...

integration-test:
    <span class="hljs-keyword">FROM</span> +setup
    <span class="hljs-keyword">COPY</span><span class="bash"> . .</span>
    <span class="hljs-comment"># Pull In Dependencies</span>
    <span class="hljs-keyword">RUN</span><span class="bash"> mix deps.get </span>
    <span class="hljs-comment"># Start Up Service Dependencies</span>
    WITH DOCKER --compose docker-compose.yml 
        <span class="hljs-comment"># Run Tests</span>
        <span class="hljs-keyword">RUN</span><span class="bash"> mix <span class="hljs-built_in">test</span> --include database </span>
    <span class="hljs-comment"># Stop Service Dependencies</span>
    END
</code></pre>
<p>This is an Earthfile. Its made up of several targets, like <code>setup</code> and <code>integration-test</code>. The targets can have dependencies between them.  You can use the command-line tool <code>earthly</code> to run any target and each is run in a Docker container.  Containerization is going allow us to run the build wherever we choose.</p>
<p>This example runs the <code>integration-test</code> inside of a the <code>hexpm/elixir</code> Docker container with the specified version of Elixir and OTP installed.</p>
<p>Before running the tests with  <code>mix test --include database</code>, we use Docker compose to start up all the needed dependencies:</p>
<pre><code class="lang-dockerfile"> WITH DOCKER --compose docker-compose.yml
        <span class="hljs-keyword">RUN</span><span class="bash"> mix <span class="hljs-built_in">test</span> --include database</span>
 END
</code></pre>
<p>The Docker compose file looks like this:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">'3'</span>
<span class="hljs-attr">services:</span>
  <span class="hljs-attr">postgres:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">postgres</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"5432:5432"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">POSTGRES_PASSWORD:</span> <span class="hljs-string">postgres</span>
  <span class="hljs-attr">mysql:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mysql</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"3306:3306"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">MYSQL_ALLOW_EMPTY_PASSWORD:</span> <span class="hljs-string">"yes"</span>
  <span class="hljs-attr">mssql:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mcr.microsoft.com/mssql/server:2019-latest</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">ACCEPT_EULA:</span> <span class="hljs-string">Y</span>
      <span class="hljs-attr">SA_PASSWORD:</span> <span class="hljs-string">some!Password</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"1433:1433"</span>
</code></pre>
<p>These are the databases we need for testing Phoenix.</p>
<p>Now we can run the integration tests at the command line like so:</p>
<pre><code>&gt;  earthly -P +integration-test
</code></pre><p>And if we want to test a different version of Elixir, we can specify the version as build arguments:</p>
<pre><code> &gt; earthly -P --build-arg ELIXIR=<span class="hljs-number">1.11</span><span class="hljs-number">.0</span> --build-arg OTP=<span class="hljs-number">23.1</span><span class="hljs-number">.1</span> +integration-test
</code></pre><p>There are other ways to accomplish this. A combination of a makefile and dockerfiles would have worked as well. The key is to get the build logic out of a GHA specific format and into something that be run can anywhere.</p>
<h2 id="heading-how-to-run-it-in-github-actions">How to Run it in GitHub Actions</h2>
<p>To use this same process inside GitHub Actions, the only thing we need to do is adjust our GitHub Actions yaml to use Earthly for the build pipeline and we are all set.</p>
<pre><code class="lang-javascript">  integration-test-elixir:
    runs-on: ubuntu-latest
    <span class="hljs-attr">env</span>:
      FORCE_COLOR: <span class="hljs-number">1</span>

    <span class="hljs-attr">strategy</span>:
      fail-fast: <span class="hljs-literal">false</span>
      <span class="hljs-attr">matrix</span>:
        include:
          - elixir: <span class="hljs-number">1.11</span><span class="hljs-number">.1</span>
            <span class="hljs-attr">otp</span>: <span class="hljs-number">21.3</span><span class="hljs-number">.8</span><span class="hljs-number">.18</span>
          - elixir: <span class="hljs-number">1.11</span><span class="hljs-number">.1</span>
            <span class="hljs-attr">otp</span>: <span class="hljs-number">23.1</span><span class="hljs-number">.1</span>
    <span class="hljs-attr">steps</span>:
      - uses: actions/checkout@v2
      - name: Download released earth
        <span class="hljs-attr">run</span>: <span class="hljs-string">"sudo /bin/sh -c 'wget https://github.com/earthly/earthly/releases/download/v0.4.1/earthly-linux-amd64 -O /usr/local/bin/earthly &amp;&amp; chmod +x /usr/local/bin/earthly'"</span>
      - name: Execute tests
        <span class="hljs-attr">run</span>: earthly -P --build-arg ELIXIR=${{ matrix.elixir }}  --build-arg OTP=${{ matrix.otp }} +integration-test
</code></pre>
<p>There we go, we are now able to run our build pipeline locally, without any complex environment setup. We can also run the same build process on our developer machine without needing to install anything except Earthly. This makes it easier for new contributors to approach the project.</p>
<h2 id="heading-the-end-result">The End Result</h2>
<p>Eventually, with help from the Phoenix Team, I got this change approved and the Phoenix project now has an easy way to test and iterate on their build pipeline locally. And I didn't even write any Elixir code! You can find more details in the <a target="_blank" href="https://github.com/phoenixframework/phoenix/pull/4072">PR</a>.</p>
<p>Thank you for reading this article.  If you'd like to learn more about Earthly, <a target="_blank" href="http://earthly.dev/">you can find out a lot here</a>. And if you'd like my help on your open source project's build, let me know.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to build a simple, extensible blog with Elixir and Phoenix ]]>
                </title>
                <description>
                    <![CDATA[ By Raman Sah In this post, we’ll discuss how to build a boilerplate Phoenix web app with user authentication and an admin panel, along with image upload in Elixir. TodoMVC has become a de facto tool to compare various JavaScript-based MV* frameworks.... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/simple-extensible-blog-built-with-elixir-and-phoenix-61d4dfafabb1/</link>
                <guid isPermaLink="false">66c35ed6258ebfc3dc8f1f86</guid>
                
                    <category>
                        <![CDATA[ Elixir ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Phoenix framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 28 Sep 2018 18:06:58 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/0*qT3nBMIsmRiQa4nc" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Raman Sah</p>
<p>In this post, we’ll discuss how to build a boilerplate Phoenix web app with user authentication and an admin panel, along with image upload in Elixir.</p>
<p><a target="_blank" href="http://todomvc.com/">TodoMVC</a> has become a de facto tool to compare various JavaScript-based MV* frameworks. Along the same lines, I feel that a blog application can be a tiebreaker in choosing a new backend or API framework.</p>
<p>So let’s get started and build one in Phoenix. We’ll follow the default setup, that is Phoenix hooked up with Ecto running on PostgreSQL.</p>
<p>Here are the final screens to give you an idea of what the app will look like at the end.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*x59piiG96eAfObns-aPzvQ.png" alt="Image" width="800" height="454" loading="lazy"></p>
<p>The landing page will show all the published blogs in a card layout. A card can be clicked to view that particular post.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*mxWIdSAc-p9elJI3x2yQKw.png" alt="Image" width="800" height="454" loading="lazy"></p>
<p>We will have a dashboard that will show the statistics in brief. Access to this page requires admin user login.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*xwuFgK253CAjphO0IVu8Ow.png" alt="Image" width="800" height="454" loading="lazy"></p>
<p>There will be a separate section that has an overview of all the posts. Here you can publish / modify / delete posts.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*fkzXp5iJjFJPzyk-bu1NFg.png" alt="Image" width="800" height="454" loading="lazy"></p>
<p>This is the post editor layout featuring a markdown editor along with a file picker for the featured image.</p>
<blockquote>
<p><em>Note: The full working code is hosted on <a target="_blank" href="https://github.com/ramansah/cms">GitHub</a>. There are numerous files in the project which cannot be shared in a single blog. So I have explained the specific ones which I assume are critical.</em></p>
</blockquote>
<p>Let’s keep the project’s name as CMS for now. So we’ll start with creating a new project with <code>mix phx.new cms</code>. Run <code>mix deps.get</code> to install dependencies.</p>
<p>Generate a migration file for users and posts, respectively.</p>
<pre><code># User migration file
</code></pre><pre><code>mix phx.gen.schema Auth.User users name:string email:string password_hash:string is_admin:boolean
</code></pre><pre><code># Posts migration file
</code></pre><pre><code>mix phx.gen.schema Content.Post posts title:string body:text published:boolean cover:string user_id:integer slug:string
</code></pre><p>Two tables have to be created in the database which represent users and posts. I’ve kept it rather simple, keeping only the required fields and expanding when the need arises.</p>
<p>Subsequently, we can define changesets and additional methods in the user and post schema as well.</p>
<p><strong>user.ex</strong></p>
<p><strong>post.ex</strong></p>
<pre><code>@derive {Phoenix.Param, <span class="hljs-attr">key</span>: :slug}
</code></pre><p>Since we want the posts to have a readable and SEO friendly URL structure, we inform route helpers to reference <code>slug</code> instead of <code>id</code> in the URL namespace.</p>
<p>The routes are described here:</p>
<p>Resources which are specific to the admin section are clubbed together and assigned a pipeline which forces authentication.</p>
<p>Meanwhile, global routes are treated with passive authentication. User details are fetched if a session is present but the pages are still accessible. Login and home pages belong here.</p>
<p>Executing <code>mix phx.routes</code> gives me this output:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*C0G1-utBGFbtv8332dWFfA.png" alt="Image" width="800" height="239" loading="lazy"></p>
<p>The view is divided into three logical sections:</p>
<ol>
<li>Navigation bar</li>
<li>Sidebar</li>
<li>Main Content</li>
</ol>
<p>While the navigation bar is always visible, the sidebar appears only if an admin user is logged in. Browsing content will be inside the admin context. The links in the sidebar will grow as and when the app evolves.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*UkNS-kHZ4Dpo9lgMpzqSdA.png" alt="Image" width="553" height="531" loading="lazy"></p>
<p>The Admin.Post controller follows the typical CRUD architecture and includes an action to toggle the published state of a given post.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*xwuFgK253CAjphO0IVu8Ow.png" alt="Image" width="800" height="454" loading="lazy"></p>
<p>A lot of controls reside in the index page of admin’s post section. Here, posts can be deleted, published and modified.</p>
<p><strong>templates/admin/post/index.html.eex</strong></p>
<p>To keep the template uncluttered, we can define convenience view helpers like formatting time etc. separately.</p>
<p><strong>views/admin/post_view.ex</strong></p>
<p>Arc along with arc_ecto provides out of the box file upload capabilities. Since a post features a cover image, we have to define an arc configuration in our app.</p>
<p>Each post in our blog requires two versions of cover images — original which is visible inside specific post view and a thumb version with a smaller footprint to populate the cards. For now, let’s go with 250x250 resolution for the thumb version.</p>
<p>Coming back to the app’s landing page, it will house the cards for all the published posts. And each post will be accessible through the slug formed.</p>
<pre><code>controllers/page_controller.ex
</code></pre><p>This project explores Phoenix — how a Phoenix app is structured and how to dismantle a Phoenix-based project. I hope you’ve learned something and enjoyed it!</p>
<p>The full working app is on Github : <a target="_blank" href="https://github.com/ramansah/votex">https://github.com/ramansah/</a>cms. Feel free to clone ? and do clap if you find this blog useful ?</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Authenticate your Elixir/Phoenix APIs using Guardian ]]>
                </title>
                <description>
                    <![CDATA[ By Nirmalya Ghosh Authentication is always a tricky subject. People tend to use so many types of authentication in their apps. Authentication using an email address and a password with an option confirm password field is the most common. But every ti... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/authentication-using-elixir-phoenix-f9c162b2c398/</link>
                <guid isPermaLink="false">66c345139972b7c5c7624e05</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Elixir ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Phoenix framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Sun, 19 Mar 2017 19:13:09 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*kK_wSy_F4xp77Uj8aSFkeQ.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Nirmalya Ghosh</p>
<p>Authentication is always a tricky subject. People tend to use so many types of authentication in their apps. Authentication using an email address and a password with an option confirm password field is the most common. But every time you see a registration form, you feel bored thinking that you’ll have to type in so much just to register! What’s the fun in that!</p>
<p>So, for this app, I’ll be doing authentication using your Google account. It’s pretty straightforward. You just need to click on a button, give the app the necessary permissions to access you basic Google profile and you’re set! Cool, isn’t it?</p>
<p>We will be using <a target="_blank" href="https://github.com/ueberauth/ueberauth">Ueberauth</a>, <a target="_blank" href="https://github.com/ueberauth/ueberauth_google">Ueberauth Google</a> and <a target="_blank" href="https://github.com/ueberauth/guardian">Guardian</a> for authenticating our user. Ueberauth and Ueberauth Google will help authenticate the user with their Google credentials. Guardian will help us in generating a JSON Web Token for logged in users. That token is necessary and needs to be in the header of each request for any route which needs authentication.</p>
<p>Guardian will check for that token in the requests’ header and if the token is valid, the authenticated routes will be available to the user. I’ll explain these things in details.</p>
<p>If you hadn’t already installed <a target="_blank" href="http://www.phoenixframework.org">Phoenix</a> and its necessary dependencies, you can head over to the <a target="_blank" href="http://www.phoenixframework.org/docs/installation">Phoenix Guides</a> to install and get up and running.</p>
<p>To get started add the dependencies to our <code>mix.exs</code>.</p>
<pre><code>defp deps <span class="hljs-keyword">do</span>  [   ...      {:ueberauth, <span class="hljs-string">"~&gt; 0.4"</span>},   {:ueberauth_google, <span class="hljs-string">"~&gt; 0.2"</span>},   {:ja_serializer, <span class="hljs-string">"~&gt; 0.11.2"</span>},   {:guardian, <span class="hljs-string">"~&gt; 0.14.2"</span>}]end
</code></pre><p>After this, run <code>mix deps.get</code> to fetch the dependencies.</p>
<p>You also need to add <code>ueberauth</code> and <code>ueberauth_google</code> to our application in <code>mix.exs</code>.</p>
<pre><code>def application <span class="hljs-keyword">do</span>  [mod: {SocialAppApi, []},   <span class="hljs-attr">applications</span>: [   ...      :ueberauth, :ueberauth_google]]end
</code></pre><p>Now, you will need to add your <code>ueberauth</code>, <code>ueberauth_google</code> and <code>guardian</code> configuration to your <code>config/config.exs</code> file.</p>
<pre><code># Ueberauth Config <span class="hljs-keyword">for</span> oauthconfig :ueberauth, Ueberauth,  <span class="hljs-attr">base_path</span>: <span class="hljs-string">"/api/v1/auth"</span>,  <span class="hljs-attr">providers</span>: [    google: { Ueberauth.Strategy.Google, [] },    <span class="hljs-attr">identity</span>: { Ueberauth.Strategy.Identity, [        callback_methods: [<span class="hljs-string">"POST"</span>],        <span class="hljs-attr">uid_field</span>: :username,        <span class="hljs-attr">nickname_field</span>: :username,      ] },  ]
</code></pre><pre><code># Ueberauth Strategy Config <span class="hljs-keyword">for</span> Google oauthconfig :ueberauth, Ueberauth.Strategy.Google.OAuth,  <span class="hljs-attr">client_id</span>: System.get_env(<span class="hljs-string">"GOOGLE_CLIENT_ID"</span>),  <span class="hljs-attr">client_secret</span>: System.get_env(<span class="hljs-string">"GOOGLE_CLIENT_SECRET"</span>),  <span class="hljs-attr">redirect_uri</span>: System.get_env(<span class="hljs-string">"GOOGLE_REDIRECT_URI"</span>)
</code></pre><pre><code># Guardian configurationconfig :guardian, Guardian,  <span class="hljs-attr">allowed_algos</span>: [<span class="hljs-string">"HS512"</span>], # optional  verify_module: Guardian.JWT,  # optional  issuer: <span class="hljs-string">"SocialAppApi"</span>,  <span class="hljs-attr">ttl</span>: { <span class="hljs-number">30</span>, :days },  <span class="hljs-attr">allowed_drift</span>: <span class="hljs-number">2000</span>,  <span class="hljs-attr">verify_issuer</span>: <span class="hljs-literal">true</span>, # optional  secret_key: System.get_env(<span class="hljs-string">"GUARDIAN_SECRET"</span>) || <span class="hljs-string">"rFtDNsugNi8jNJLOfvcN4jVyS/V7Sh+9pBtc/J30W8h4MYTcbiLYf/8CEVfdgU6/"</span>,  <span class="hljs-attr">serializer</span>: SocialAppApi.GuardianSerializer
</code></pre><p>As you can see here, I’ve used <code>System.get_env() .</code> This is a way to store credentials in your app which you don’t want to be a part of your codebase. You can create a <code>.env</code> file and store all of these credentials like:</p>
<pre><code><span class="hljs-keyword">export</span> DB_NAME_PROD=<span class="hljs-string">"social_app_api_db"</span><span class="hljs-keyword">export</span> DB_PASSWORD_PROD=<span class="hljs-string">"password"</span><span class="hljs-keyword">export</span> DB_USERNAME_PROD=<span class="hljs-string">"password"</span>
</code></pre><p>After this, you need to do <code>source .env</code> and then, you can use them in your app.</p>
<p>Now, we’ll need to do a bunch of stuff with our controllers which will let the user to sign up or sign in.</p>
<p>First, create a new file <code>web/controllers/auth_controller.ex</code>.</p>
<pre><code>defmodule SocialAppApi.AuthController <span class="hljs-keyword">do</span>  use SocialAppApi.Web, :controller  plug Ueberauth
</code></pre><pre><code>  alias SocialAppApi.User  alias MyApp.UserQuery
</code></pre><pre><code>  plug :scrub_params, <span class="hljs-string">"user"</span> when action <span class="hljs-keyword">in</span> [:sign_in_user]
</code></pre><pre><code>  def request(_params) <span class="hljs-keyword">do</span>  end
</code></pre><pre><code>  def <span class="hljs-keyword">delete</span>(conn, _params) <span class="hljs-keyword">do</span>    # Sign out the user    conn    |&gt; put_status(<span class="hljs-number">200</span>)    |&gt; Guardian.Plug.sign_out(conn)  end
</code></pre><pre><code>  def callback(%{<span class="hljs-attr">assigns</span>: %{<span class="hljs-attr">ueberauth_failure</span>: _fails}} = conn, _params) <span class="hljs-keyword">do</span>    # This callback is called when the user denies the app to get the data <span class="hljs-keyword">from</span> the oauth provider    conn    |&gt; put_status(<span class="hljs-number">401</span>)    |&gt; render(SocialAppApi.ErrorView, <span class="hljs-string">"401.json-api"</span>)  end
</code></pre><pre><code>  def callback(%{<span class="hljs-attr">assigns</span>: %{<span class="hljs-attr">ueberauth_auth</span>: auth}} = conn, _params) <span class="hljs-keyword">do</span>    <span class="hljs-keyword">case</span> AuthUser.basic_info(auth) <span class="hljs-keyword">do</span>      {:ok, user} -&gt;        sign_in_user(conn, %{<span class="hljs-string">"user"</span> =&gt; user})    end
</code></pre><pre><code>  <span class="hljs-keyword">case</span> AuthUser.basic_info(auth) <span class="hljs-keyword">do</span>      {:ok, user} -&gt;        conn        |&gt; render(SocialAppApi.UserView, <span class="hljs-string">"show.json-api"</span>, %{<span class="hljs-attr">data</span>: user})      {:error} -&gt;        conn        |&gt; put_status(<span class="hljs-number">401</span>)        |&gt; render(SocialAppApi.ErrorView, <span class="hljs-string">"401.json-api"</span>)    end  end
</code></pre><pre><code>  def sign_in_user(conn, %{<span class="hljs-string">"user"</span> =&gt; user}) <span class="hljs-keyword">do</span>    <span class="hljs-keyword">try</span> <span class="hljs-keyword">do</span>      # Attempt to retrieve exactly one user <span class="hljs-keyword">from</span> the DB, whose      # email matches the one provided <span class="hljs-keyword">with</span> the login request      user = User      |&gt; where(email: ^user.email)      |&gt; Repo.one!
</code></pre><pre><code>      cond <span class="hljs-keyword">do</span>        <span class="hljs-literal">true</span> -&gt;          # Successful login          # Encode a JWT          { :ok, jwt, _ } = Guardian.encode_and_sign(user, :token)
</code></pre><pre><code>          auth_conn = Guardian.Plug.api_sign_in(conn, user)          jwt = Guardian.Plug.current_token(auth_conn)          {:ok, claims} = Guardian.Plug.claims(auth_conn)
</code></pre><pre><code>          auth_conn          |&gt; put_resp_header(<span class="hljs-string">"authorization"</span>, <span class="hljs-string">"Bearer #{jwt}"</span>)          |&gt; json(%{<span class="hljs-attr">access_token</span>: jwt}) # Return token to the client
</code></pre><pre><code>        <span class="hljs-literal">false</span> -&gt;          # Unsuccessful login          conn          |&gt; put_status(<span class="hljs-number">401</span>)          |&gt; render(SocialAppApi.ErrorView, <span class="hljs-string">"401.json-api"</span>)      end    rescue      e -&gt;        IO.inspect e # Print error to the <span class="hljs-built_in">console</span> <span class="hljs-keyword">for</span> debugging
</code></pre><pre><code>        # Successful registration        sign_up_user(conn, %{<span class="hljs-string">"user"</span> =&gt; user})    end  end
</code></pre><pre><code>  def sign_up_user(conn, %{<span class="hljs-string">"user"</span> =&gt; user}) <span class="hljs-keyword">do</span>    changeset = User.changeset %User{}, %{<span class="hljs-attr">email</span>: user.email,      <span class="hljs-attr">avatar</span>: user.avatar,      <span class="hljs-attr">first_name</span>: user.first_name,      <span class="hljs-attr">last_name</span>: user.last_name,      <span class="hljs-attr">auth_provider</span>: <span class="hljs-string">"google"</span>}
</code></pre><pre><code>    <span class="hljs-keyword">case</span> Repo.insert changeset <span class="hljs-keyword">do</span>      {:ok, user} -&gt;        # Encode a JWT        { :ok, jwt, _ } = Guardian.encode_and_sign(user, :token)
</code></pre><pre><code>        conn        |&gt; put_resp_header(<span class="hljs-string">"authorization"</span>, <span class="hljs-string">"Bearer #{jwt}"</span>)        |&gt; json(%{<span class="hljs-attr">access_token</span>: jwt}) # Return token to the client      {:error, changeset} -&gt;        conn        |&gt; put_status(<span class="hljs-number">422</span>)        |&gt; render(SocialAppApi.ErrorView, <span class="hljs-string">"422.json-api"</span>)    end  end
</code></pre><pre><code>  def unauthenticated(conn, params) <span class="hljs-keyword">do</span>    conn    |&gt; put_status(<span class="hljs-number">401</span>)    |&gt; render(SocialAppApi.ErrorView, <span class="hljs-string">"401.json-api"</span>)  end
</code></pre><pre><code>  def unauthorized(conn, params) <span class="hljs-keyword">do</span>    conn    |&gt; put_status(<span class="hljs-number">403</span>)    |&gt; render(SocialAppApi.ErrorView, <span class="hljs-string">"403.json-api"</span>)  end
</code></pre><pre><code>  def already_authenticated(conn, params) <span class="hljs-keyword">do</span>    conn    |&gt; put_status(<span class="hljs-number">200</span>)    |&gt; render(SocialAppApi.ErrorView, <span class="hljs-string">"200.json-api"</span>)  end
</code></pre><pre><code>  def no_resource(conn, params) <span class="hljs-keyword">do</span>    conn    |&gt; put_status(<span class="hljs-number">404</span>)    |&gt; render(SocialAppApi.ErrorView, <span class="hljs-string">"404.json-api"</span>)  endend
</code></pre><p>Here <code>sign_in_user</code> will sign the user in and throw an <code>access_token</code> as the response. The <code>sign_up_user</code> will sign the user up using their Google credentials and then throw an <code>access_token</code> as the response. This token is essential in the way that Guardian will check for this <code>access_token</code> in all the requests’ header. It will check if the user is currently in session or not. If yes, all the authenticated routes will be available to the user. Otherwise, he will receive a <code>401</code> response for the authenticated routes.</p>
<p>Let’s add some routes to our app. Our <code>router.ex</code> file looks like this:</p>
<pre><code>defmodule SocialAppApi.Router <span class="hljs-keyword">do</span>  use SocialAppApi.Web, :router
</code></pre><pre><code>  pipeline :api <span class="hljs-keyword">do</span>    plug :accepts, [<span class="hljs-string">"json"</span>, <span class="hljs-string">"json-api"</span>]    plug JaSerializer.Deserializer  end
</code></pre><pre><code>  pipeline :api_auth <span class="hljs-keyword">do</span>    plug :accepts, [<span class="hljs-string">"json"</span>, <span class="hljs-string">"json-api"</span>]    plug Guardian.Plug.VerifyHeader, <span class="hljs-attr">realm</span>: <span class="hljs-string">"Bearer"</span>    plug Guardian.Plug.LoadResource    plug JaSerializer.Deserializer  end
</code></pre><pre><code>  scope <span class="hljs-string">"/api/v1"</span>, SocialAppApi <span class="hljs-keyword">do</span>    pipe_through :api_auth
</code></pre><pre><code>  resources <span class="hljs-string">"/users"</span>, UserController, <span class="hljs-attr">except</span>: [:<span class="hljs-keyword">new</span>, :edit]    get <span class="hljs-string">"/user/current"</span>, UserController, :current, <span class="hljs-attr">as</span>: :current_user    <span class="hljs-keyword">delete</span> <span class="hljs-string">"/logout"</span>, AuthController, :<span class="hljs-keyword">delete</span>  end
</code></pre><pre><code>  scope <span class="hljs-string">"/api/v1/auth"</span>, SocialAppApi <span class="hljs-keyword">do</span>    pipe_through :api
</code></pre><pre><code>    get <span class="hljs-string">"/:provider"</span>, AuthController, :request    get <span class="hljs-string">"/:provider/callback"</span>, AuthController, :callback    post <span class="hljs-string">"/:provider/callback"</span>, AuthController, :callback  endend
</code></pre><p>Here, pipeline <code>api_auth</code> is the one which is authenticated. The pipeline <code>api</code> isn’t. So, we can visit <code>get “/:provider”, AuthController, :request</code> without signing in.</p>
<p>Create another file called <code>web/models/auth_user.ex</code> with the following code:</p>
<pre><code>defmodule AuthUser <span class="hljs-keyword">do</span>  alias Ueberauth.Auth
</code></pre><pre><code>  def basic_info(%Auth{} = auth) <span class="hljs-keyword">do</span>    {:ok,      %{        <span class="hljs-attr">avatar</span>: auth.info.image,        <span class="hljs-attr">email</span>: auth.info.email,        <span class="hljs-attr">first_name</span>: auth.info.first_name,        <span class="hljs-attr">last_name</span>: auth.info.last_name      }    }  endend
</code></pre><p>You will also need to create a <code>User</code> model.</p>
<pre><code>mix phoenix.gen.json User users email:string auth_provider:string first_name:string last_name:string avatar:string
</code></pre><p>This will generate your necessary model and migration.</p>
<p>Your model will look something like this:</p>
<pre><code>defmodule SocialAppApi.User <span class="hljs-keyword">do</span>  use SocialAppApi.Web, :model
</code></pre><pre><code>  schema <span class="hljs-string">"users"</span> <span class="hljs-keyword">do</span>    field :email, :string    field :auth_provider, :string    field :first_name, :string    field :last_name, :string    field :avatar, :string
</code></pre><pre><code>    timestamps()  end
</code></pre><pre><code>  def changeset(struct, params \\ %{}) <span class="hljs-keyword">do</span>    struct    |&gt; cast(params, [:email, :auth_provider, :first_name, :last_name, :avatar])    |&gt; validate_required([:email, :auth_provider, :first_name, :last_name, :avatar])    |&gt; unique_constraint(:email)  endend
</code></pre><p>Your migration file will look something like this:</p>
<pre><code>defmodule SocialAppApi.Repo.Migrations.CreateUser <span class="hljs-keyword">do</span>  use Ecto.Migration
</code></pre><pre><code>  def change <span class="hljs-keyword">do</span>    create table(:users) <span class="hljs-keyword">do</span>      add :email, :string      add :auth_provider, :string      add :first_name, :string      add :last_name, :string      add :avatar, :string
</code></pre><pre><code>      timestamps()    end
</code></pre><pre><code>    # Unique email address constraint, via DB index    create index(:users, [:email], <span class="hljs-attr">unique</span>: <span class="hljs-literal">true</span>)  endend
</code></pre><p>Now, run the migration.</p>
<pre><code>mix ecto.migrate
</code></pre><p>Also, create a <code>UserController</code> for our <code>user</code> model. That will contain the following code:</p>
<pre><code>defmodule SocialAppApi.UserController <span class="hljs-keyword">do</span>  use SocialAppApi.Web, :controller
</code></pre><pre><code>  alias SocialAppApi.User
</code></pre><pre><code>  plug Guardian.Plug.EnsureAuthenticated, <span class="hljs-attr">handler</span>:     SocialAppApi.AuthController
</code></pre><pre><code>  def index(conn, _params) <span class="hljs-keyword">do</span>    users = Repo.all(User)    render(conn, <span class="hljs-string">"index.json-api"</span>, <span class="hljs-attr">data</span>: users)  end
</code></pre><pre><code>  def current(conn, _) <span class="hljs-keyword">do</span>    user = conn    |&gt; Guardian.Plug.current_resource
</code></pre><pre><code>    conn    |&gt; render(SocialAppApi.UserView, <span class="hljs-string">"show.json-api"</span>, <span class="hljs-attr">data</span>: user)  endend
</code></pre><p>This is useful in case you want to check if the authenticated routes work or not after all your hard work.</p>
<p>Create two more views at <code>web/views/error_view.ex</code> with the following code:</p>
<pre><code>defmodule SocialAppApi.ErrorView <span class="hljs-keyword">do</span>  use SocialAppApi.Web, :view  use JaSerializer.PhoenixView
</code></pre><pre><code>  def render(<span class="hljs-string">"401.json-api"</span>, _assigns) <span class="hljs-keyword">do</span>    %{<span class="hljs-attr">title</span>: <span class="hljs-string">"Unauthorized"</span>, <span class="hljs-attr">code</span>: <span class="hljs-number">401</span>}    |&gt; JaSerializer.ErrorSerializer.format  end
</code></pre><pre><code>  def render(<span class="hljs-string">"403.json-api"</span>, _assigns) <span class="hljs-keyword">do</span>    %{<span class="hljs-attr">title</span>: <span class="hljs-string">"Forbidden"</span>, <span class="hljs-attr">code</span>: <span class="hljs-number">403</span>}    |&gt; JaSerializer.ErrorSerializer.format  end
</code></pre><pre><code>  def render(<span class="hljs-string">"404.json-api"</span>, _assigns) <span class="hljs-keyword">do</span>    %{<span class="hljs-attr">title</span>: <span class="hljs-string">"Page not found"</span>, <span class="hljs-attr">code</span>: <span class="hljs-number">404</span>}    |&gt; JaSerializer.ErrorSerializer.format  end
</code></pre><pre><code>  def render(<span class="hljs-string">"422.json-api"</span>, _assigns) <span class="hljs-keyword">do</span>    %{<span class="hljs-attr">title</span>: <span class="hljs-string">"Unprocessable entity"</span>, <span class="hljs-attr">code</span>: <span class="hljs-number">422</span>}    |&gt; JaSerializer.ErrorSerializer.format  end
</code></pre><pre><code>  def render(<span class="hljs-string">"500.json-api"</span>, _assigns) <span class="hljs-keyword">do</span>    %{<span class="hljs-attr">title</span>: <span class="hljs-string">"Internal Server Error"</span>, <span class="hljs-attr">code</span>: <span class="hljs-number">500</span>}    |&gt; JaSerializer.ErrorSerializer.format  end
</code></pre><pre><code>  # In <span class="hljs-keyword">case</span> no render clause matches or no  # template is found, <span class="hljs-keyword">let</span><span class="hljs-string">'s render it as 500  def template_not_found(_template, assigns) do    render "500.json-api", assigns  endend</span>
</code></pre><p>Also, create another view <code>web/views/user_view.ex</code> with the following code:</p>
<pre><code>defmodule SocialAppApi.UserView <span class="hljs-keyword">do</span>  use SocialAppApi.Web, :view  use JaSerializer.PhoenixView
</code></pre><pre><code>  attributes [:avatar, :email, :first_name, :last_name, :auth_provider]end
</code></pre><p>And, you are all set. Fire up your server:</p>
<pre><code>mix phoenix.server
</code></pre><p>Now, go to <a target="_blank" href="http://localhost:4000/api/v1/auth/google">http://localhost:4000/api/v1/auth/google</a> and you will be redirected to Google’s login page. Once you give the app the necessary permissions, you will get an <code>access_token</code> in the response:</p>
<pre><code>{  <span class="hljs-attr">access_token</span>: <span class="hljs-string">"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJVc2VyOjIiLCJleHAiOjE0ODk4NjM4MzUsImlhdCI6MTQ4NzI3MTgzNSwiaXNzIjoiU29jaWFsQXBwQXBpIiwianRpIjoiODU0NzJhODAtN2Q4Ny00MjM0LWIxNmUtODgyMTBmYWZkZDJmIiwicGVtIjp7fSwic3ViIjoiVXNlcjoyIiwidHlwIjoiYWNjZXNzIn0.L2LjpsyJAjF1r99hR11WVGcQ"</span>}
</code></pre><p>Now, you can install the <a target="_blank" href="https://chrome.google.com/webstore/detail/modheader/idgpnmonknjnojddfkpgkljpfnnfcklj?hl=en">Modheader</a> extension for Chrome and any other extension through which you can set response headers. Add <code>Authorization</code> as a <code>Request Header</code> and the <code>access_token</code> with <code>Bearer &lt;access_tok</code>en&gt;.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/8s46wKgYRFAp7mdlDFIYgtInAUZzUpw3-INz" alt="Image" width="542" height="171" loading="lazy">
<em>Modheader</em></p>
<p>Go to <a target="_blank" href="http://localhost:4000/api/v1/users">http://localhost:4000/api/v1/users</a> and you will be able to see an array of users you’ve already sign up with. You can also go to <a target="_blank" href="http://localhost:4000/api/v1/user/current">http://localhost:4000/api/v1/user/current</a> to see the current user in the session.</p>
<p>If you remove that value from Modheader and go to <a target="_blank" href="http://localhost:4000/api/v1/users">http://localhost:4000/api/v1/users</a>, you will get the following response:</p>
<pre><code>{  <span class="hljs-attr">jsonapi</span>: {    <span class="hljs-attr">version</span>: <span class="hljs-string">"1.0"</span>  },  <span class="hljs-attr">errors</span>: [{    <span class="hljs-attr">title</span>: <span class="hljs-string">"Unauthorized"</span>,    <span class="hljs-attr">code</span>: <span class="hljs-number">401</span>  }]}
</code></pre><p>As I’ve mentioned earlier, you need to send the <code>access_token</code> received to view the authenticated routes. Now, you know how to do API authentication in Elixir. You can compare your code with my code on <a target="_blank" href="https://github.com/ghoshnirmalya/social_app_api/tree/5f16f6432796f3fe372e971522dd588b5db3a421">Github</a>.</p>
<p>If you have some feedback, let me know in the comments below.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Happy Little Projects: Elixir, Phoenix, Twilio, and the Spotify API ]]>
                </title>
                <description>
                    <![CDATA[ By Nathan One of the most difficult aspects of starting a programming project is coming up with a project idea in the first place. No inspiration means no programming! Luckily, I recently discovered a project idea with a nice pacing to it: a basic No... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/happy-little-projects-ef8cd157287/</link>
                <guid isPermaLink="false">66c34c19a7aea9fc97bdfb3f</guid>
                
                    <category>
                        <![CDATA[ Elixir ]]>
                    </category>
                
                    <category>
                        <![CDATA[ learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Phoenix framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 15 Jun 2016 18:32:55 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*9RWesRs9XuAaOeObRW81SQ.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Nathan</p>
<p>One of the most difficult aspects of starting a programming project is coming up with a project idea in the first place. No inspiration means no programming!</p>
<p>Luckily, I recently discovered a project idea with a nice pacing to it: a basic Node.js and Express.js application using Spotify’s API and Twilio.</p>
<p>If you’d like to follow along with the JavaScript version, check out this <a target="_blank" href="https://youtu.be/EKb8XD_V32o">video</a>. If you want to build it in Gradle and Spark, here’s Twilio’s <a target="_blank" href="https://www.twilio.com/blog/2015/09/getting-started-with-gradle-and-the-spark-framework-3.html">original post</a>.</p>
<p>For this article, we’ll build this app using Elixir and Phoenix. Let’s get started.</p>
<p>Here’s the general flow of the app:</p>
<ul>
<li>You text the title of a song to a Twilio number</li>
<li>Twilio makes an HTTP POST to a preconfigured url with some infomation including the song title and information about the requester</li>
<li>The application searches the Spotify API for the song, and parses out a preview url</li>
<li>The application dispatches a message to the Twilio API. This includes the number to call and a url to fetch some TwiML(Twilio Markup Language)</li>
<li>Twilio fetches the TwiML from our application, calls the recipient, and plays them the song preview.</li>
</ul>
<p>I’ve been through a majority of the curriculum at <a target="_blank" href="http://www.freecodecamp.com">Free Code Camp</a>. I helped design and build a large part of the core systems behind Free Code Camp, so I’m very comfortable with JavaScript. Following along with the presentation was easy, and I was quick to recreate it. The questions was, could I do it with a language and framework I didn’t have solid experience with?</p>
<p>If you’d like to know what the finished product <em>looks</em> like, text the title of a song to +1 (334) 721–2652. Don’t worry, we won’t save your phone number or song. Request all the ABBA you want!</p>
<p>NOTE: I’ve loaded some funds into this to keep it going a while. I’m hosting it on Heroku so it may take a moment to wake up and respond. If it doesn’t respond at all, the funds I added have been exhausted.</p>
<p>Here’s a short video of me interacting with my app using Google Voice.</p>
<p>I wanted to challenged myself and play with a language that I’ve been smitten with since hearing about it. <a target="_blank" href="http://elixir-lang.org/">Elixir</a> is a gorgeous language with Ruby inspired syntax. Elixir runs on the BEAM (Erlang VM), and is interoperable with Erlang. Yes, Erlang of <a target="_blank" href="http://www.wired.com/2015/09/whatsapp-serves-900-million-users-50-engineers/">WhatsApp fame</a>. I love the idea of being able to tap into that kind of power and reliability. I also <strong>love</strong> functional programming.</p>
<p>On top of Elixir, I’m also a fan of the <a target="_blank" href="http://www.phoenixframework.org/">Phoenix web framework</a>. It’s easy to get started with, and easy to get things done in. The error messages are excellent and tend to tell you exactly how to fix them. Trust me on this, I’ve seen enough of them.</p>
<p>The first task is is to generate a new Phoenix application. I called mine Philter, so I typed:</p>
<pre><code>mix phoenix.new philter --no-ecto --no-brunch
</code></pre><p>With this, we’re creating a new phoenix application called Philter, with no database layer and no JavaScript build system. We won’t be using any JavaScript in this project!</p>
<p>Follow the on-screen instructions to finish setting up the application. We’re now ready to work through our list of tasks.</p>
<h4 id="heading-twilio"><strong>Twilio</strong></h4>
<p>Twilio makes it pretty easy to setup an account. As an aside, their documentation and console are top notch. It’s one of my favorite web services to use.</p>
<p>Sign up for Twilio <a target="_blank" href="https://www.twilio.com/try-twilio">here</a>. If you want to follow along with this tutorial, you’ll have to add some funds. $5 would be more than enough to give you days of playing around. If you decide to follow along, buy a phone number and keep your browser tab open.</p>
<h4 id="heading-ngrok">Ngrok</h4>
<p>The next service you’ll want to use is <a target="_blank" href="https://ngrok.com/">ngrok</a>. This handy little service tunnels into a specified port on your computer and gives you a public url to use. The service is completely free, but I signed up for the $5/mo plan so I could have a reserved subdomain. It’s the little things, I tell ya.</p>
<p>Open a new terminal tab and install ngrok via npm. Then use ngrok to specify that you’d like to create an http tunnel to port 4000 on your computer.</p>
<pre><code># is a comment <span class="hljs-keyword">for</span> your information# ~ represents your terminal prompt~ npm i -g ngrok
</code></pre><pre><code>...~ ngrok http <span class="hljs-number">4000n</span>grok by @inconshreveable                           (Ctrl+C to quit)
</code></pre><pre><code>Tunnel Status             onlineVersion                   <span class="hljs-number">2.1</span><span class="hljs-number">.1</span>Region                    United States (us)Web Interface             http:<span class="hljs-comment">//127.0.0.1:4040Forwarding                http://someurl.ngrok.io -&gt; localhost:4000Forwarding                https://someurl.ngrok.io -&gt; localhost:4000</span>
</code></pre><p>Now switch back to your original terminal tab and start your phoenix app.</p>
<pre><code>~ iex -S mix phoenix.server      # &lt;or&gt;~ mix phoenix.server
</code></pre><p>The first option starts the server in an interactive shell that will let you interact with it. Regardless of your method of starting it, you’ll see it log out that it’s listening on your local machine on port 4000.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/zIq2uSM4GlYePld0AMckR7YAYtGlrYH3F5kk" alt="Image" width="566" height="333" loading="lazy"></p>
<p>Now open a new browser tab and visit <em>localhost:4000</em> to confirm it’s working. Then, paste in the url from the <em>Forwarding</em> http line in the ngrok terminal that I bolded above. You’ll see your app there too. Magic!</p>
<p>Go back to the tab you have your Twilio console open in, and find your phone number. Click on it, and you should see some configuration information. Under the messaging section, “When a message comes in”, enter the url from ngrok followed by “api/sms”. Ensure the HTTP method is set to POST. For reference, while building this application mine was set to <em>http://tkb.ngrok.com/api/sms</em></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/ISv5k57nBcVTQJp-xVCfpvLE33K-FDkxJ5Vb" alt="Image" width="800" height="579" loading="lazy"></p>
<p>While we have the Twilio console open, get your ACCOUNT SID and AUTH TOKEN credentials. You can find them by clicking on your account name in the top right hand corner of the window and looking at the “API Credentials” section. Create two environmental variables, TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN. I use an extension of the preferences panel on my Mac called <a target="_blank" href="https://github.com/hschmidt/EnvPane">EnvPane</a>. You can also search google and get a ton of results if you need help setting yours.</p>
<p>With all that information at hand, we’re nearly ready to tie it all together. We have one last thing to configure. We’re going to use <a target="_blank" href="https://github.com/danielberkompas/ex_twilio">ExTwilio</a>, a library to help our phoenix application talk to Twilio.</p>
<p>Open <em>config/config.exs</em> and add the following above the final <em>import</em> statement:</p>
<pre><code>config :ex_twilio,  <span class="hljs-attr">account_sid</span>: System.get_env(“TWILIO_ACCOUNT_SID”),  <span class="hljs-attr">auth_token</span>: System.get_env(“TWILIO_AUTH_TOKEN”)
</code></pre><p>Here’s where we’re telling our application to read in these two environmental variables so we can send messages and make phone calls through Twilio’s API.</p>
<p>In your favorite editor of choice (mine is <a target="_blank" href="http://spacemacs.org">Spacemacs</a>), open your Phoenix application directory. Let’s get down to business.</p>
<p>Open <em>web/router.ex</em> and get rid of any <strong>scope</strong> stuff you see. Replace it with:</p>
<pre><code>scope “/”, Philter <span class="hljs-keyword">do</span>  pipe_through :browser
</code></pre><pre><code>  post “/twiml”, TwimlController, :indexend
</code></pre><pre><code>scope “/api”, Philter <span class="hljs-keyword">do</span>  pipe_through :api
</code></pre><pre><code>  post “/sms”, SmsController, :indexend
</code></pre><p>Replace any mention of Philter with whatever name you gave your application.</p>
<p>The above code did a few things. We have a created a route that will match POSTs to <em>http://yourngrokurl/twiml</em> and route to <em>TwimlController</em>’s index function. We also did the same for the <em>http://yourngrokurl/api/sms</em> route, passing off to <em>SmsController</em>’s index function. Learn more about routing in Phoenix by checking out the excellent <a target="_blank" href="http://www.phoenixframework.org/docs/routing">documentation</a>.</p>
<p>Now create two files in the <em>web/controllers/</em> directory, _sms<em>controller.ex</em> and _twiml<em>controller.ex</em>. Make your _sms<em>controller.ex</em> look like:</p>
<pre><code>defmodule Philter.SmsController <span class="hljs-keyword">do</span>  use Philter.Web, :controller
</code></pre><pre><code>  alias Philter.Sms
</code></pre><pre><code>  def index(conn, %{<span class="hljs-string">"Body"</span> =&gt; song, <span class="hljs-string">"From"</span> =&gt; <span class="hljs-keyword">from</span>, <span class="hljs-string">"To"</span> =&gt; to}) <span class="hljs-keyword">do</span>
</code></pre><pre><code>    Task.start_link(fn -&gt; search_spotify(song, %{<span class="hljs-attr">from</span>: <span class="hljs-keyword">from</span>, <span class="hljs-attr">to</span>:     to}) end)    send_resp(conn, <span class="hljs-number">200</span>, “”)   end
</code></pre><pre><code>  defp search_spotify(song, twilio_data) <span class="hljs-keyword">do</span>    Philter.Spotify.search(song, twilio_data)  endend
</code></pre><p>To those Elixirists reading this, please keep in mind I’m still very much learning. With that disclaimer out of the way, on to a brief explanation.</p>
<p>Twilio will post the phone number of the requester in a <em>From</em> field, our Twilio number in the <em>To</em> field, and the body of their text in the <em>Body</em> field. We’re fishing those out, then spawning a task to search the Spotify API.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/-pfgkBdJYkKY--vlTT9D32JaH9mjrnyqP8PP" alt="Image" width="636" height="450" loading="lazy"></p>
<p>Spawning a task executes some long running function in a super lightweight BEAM process. If something happens to the process, like it crashes or catches on fire or is stricken by cosmic rays, our application will happily continue accepting connections and prevent moments of developer panic.</p>
<p>A full discussion of Tasks and OTP in general is far beyond the scope of this article. Please refer to the Elixir links in this post if you’d like to learn more about this amazing beast.</p>
<p>Now refer to the <a target="_blank" href="https://github.com/terakilobyte/Philter">GitHub repository</a> for this project. The files you’ll want to copy are <em>lib/philter/spotify.ex</em> and the entire <em>lib/philter/spotify/</em> directory. Ensure you go through the files, changing any mention of Philter to your own application name. In <em>spotify.ex</em>, <a target="_blank" href="https://github.com/terakilobyte/Philter/blob/master/lib/philter/spotify.ex#L55">on line 55</a>, replace “tkb” in the url to whatever your ngrok url is.</p>
<p>Now make your _twiml<em>controller.ex</em> look like the following:</p>
<pre><code>defmodule Philter.TwimlController <span class="hljs-keyword">do</span>  use Philter.Web, :controller
</code></pre><pre><code>  alias Philter.Twiml
</code></pre><pre><code>  def index(conn, %{<span class="hljs-string">"song"</span> =&gt; song) <span class="hljs-keyword">do</span>    render(conn, “index.html”, <span class="hljs-attr">song</span>: song)  endend
</code></pre><p>All we’re doing here is fishing the song out and and then passing it along as a variable to our template.</p>
<p>Open the <em>app.html.eex</em> in the <em>web/templates/</em> directory and delete everything except for:</p>
<pre><code> &lt;%= render @view_module, @view_template, assigns %&gt;
</code></pre><p>We don’t need any of that other markup!</p>
<p>Next, create a file under the <em>web/views/</em> directory called _twiml<em>view.ex</em>. We could put helper functions in here, but we don’t need any so it’s just going to live as a shell file. The contents should be:</p>
<pre><code>defmodule Philter.TwimlView <span class="hljs-keyword">do</span>  use Philter.Web, :viewend
</code></pre><p>Now create a new directory under <em>web/templates/</em> called <em>twiml/</em> and inside of it create a file called <em>index.html.eex</em>. The contents are straightforward:</p>
<pre><code> &lt;?xml version=”<span class="hljs-number">1.0</span><span class="hljs-string">" encoding=”UTF-8"</span> ?&gt; <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Response</span>&gt;</span>   <span class="hljs-tag">&lt;<span class="hljs-name">Say</span>&gt;</span>Please enjoy the clip!<span class="hljs-symbol">&amp;lt;</span>/Say&gt;   <span class="hljs-tag">&lt;<span class="hljs-name">Play</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">%=</span> @<span class="hljs-attr">song</span> %&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">Play</span>&gt;</span>   <span class="hljs-tag">&lt;<span class="hljs-name">Say</span>&gt;</span>I hope you enjoyed your song clip<span class="hljs-tag">&lt;/<span class="hljs-name">Say</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">Response</span>&gt;</span></span>
</code></pre><p>This is the response we’ll send to Twilio when they ask us for TwiML in response to our API interaction within the task I barely touched on. Feel free to play around, and reference the great <a target="_blank" href="https://www.twilio.com/docs/api/twiml">TwiML documentation</a>.</p>
<p>With a restart of Phoenix, you should be able to text your Twilio phone number and get a phone call with the song preview!</p>
<h4 id="heading-denouement">Dénouement</h4>
<p>I had a lot of fun implementing this little application with Elixir. Much like when I was learning JavaScript, I had to wrap my head around how to go about doing things, using functions correctly (even finding the correct functions to use!), and ensuring the application logic was correct.</p>
<p>There were many furrowed brows and a lot of documentation and online tutorials were referenced as I dove deeper into Elixir. I was very pleasantly surprised at how little Phoenix specific code was needed though. It’s a great framework in my opinion.</p>
<p>In the end, the final product made the temporary frustrations worth it. Getting reactions out of my wife and friends like “That’s so cool!” or “You made that?” are definitely worth the effort. It also helped cement patterns and notions I had about how things worked more clearly. Maybe after I get out of the military, I will indeed be able to transition into a career in programming.</p>
<p>I have to give a shout out to Twilio. They’ve made an excellent project. Every interaction I needed to perform was well documented, and their management console itself made it easy to find what I needed and configure actions on that end.</p>
<p>If you are interested in learning more about Elixir and Phoenix, I’d highly recommend <a target="_blank" href="https://pragprog.com/book/elixir12/programming-elixir-1-2">Programming Elixir</a>, <a target="_blank" href="https://www.manning.com/books/elixir-in-action">Elixir In Action</a>, and <a target="_blank" href="https://pragprog.com/book/phoenix/programming-phoenix">Programming Phoenix</a>. I have all 3 and they are excellent! The online documentation is well above par, and more and more tutorials and articles are popping up. In general, the Elixir community is one of the best out there.</p>
<p>We are definitely excited to add Elixir to our backend languages at FreeCodeCamp.</p>
<p>Learning to code is hard. I’d highly recommend signing up for <a target="_blank" href="http://www.freecodecamp.com">FreeCodeCamp</a> and following along the very structured and self-paced learning track we’ve established. Thousands upon thousands of people are finding success, and we have a <strong>massive</strong> community of helpful people - beginners to professionals - in our Gitter room.</p>
<p>Remember, don’t let something as ephemeral as frustration at a task today keep you from achieving your goals tomorrow. Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
