<?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" version="2.0"><channel><title><![CDATA[Untitled Publication]]></title><description><![CDATA[I am a, Husband, Latino, Full Stack Software Developer, Content Creator, Open Source Developer, and Twitch Tools Builder.
My proudest creation this the Stream C]]></description><link>https://blog.guzman.codes</link><generator>RSS for Node</generator><lastBuildDate>Sat, 11 Apr 2026 23:04:31 GMT</lastBuildDate><atom:link href="https://blog.guzman.codes/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[GitHub Copilot Chat, What Why Don't You Know About My Selected File?]]></title><description><![CDATA[The Change In Behavior
GitHub Copilot is a fantastic tool. I have used it since the closed beta, and it has become part of my daily process. I enjoy opening the GitHub Copilot Chat view after working on a file and asking, "Is there any other suggeste...]]></description><link>https://blog.guzman.codes/github-copilot-chat-what-why-dont-you-know-about-my-selected-file</link><guid isPermaLink="true">https://blog.guzman.codes/github-copilot-chat-what-why-dont-you-know-about-my-selected-file</guid><category><![CDATA[github copilot]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[troubleshooting]]></category><category><![CDATA[guide]]></category><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Mon, 12 Feb 2024 14:00:55 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-the-change-in-behavior">The Change In Behavior</h2>
<p>GitHub Copilot is a fantastic tool. I have used it since the closed beta, and it has become part of my daily process. I enjoy opening the GitHub Copilot Chat view after working on a file and asking, <strong>"Is there any other suggested refactor to be made?"</strong> Giving a "second pair of eyes" on my code to give me new ideas on writing cleaner code. But as of late, something weird has been happening. After selecting the file I have been working on and going to the GitHub Copilot Chat view to ask my usual questions, it doesn't know anything about my selected code; it gives general answers and nothing specific to my actual code, or worse.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707681078624/a916245c-1cdd-4846-b9ac-c22b42855ef7.png" alt class="image--center mx-auto" /></p>
<p>What the heck happened? Since I had no clue what was happening, I would select my code changes as a temporary solution and use the Copilot Chat options that would appear in the editor window. This works in the short term but becomes more cumbersome when getting suggestions for an entire file.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707759157362/b0644935-6af5-494a-a93f-738fa354848b.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-the-solution">The Solution</h2>
<p>It turns out that the default behavior of automatically including a selected Windows code in the context of Copilot Chat was changed between the beta release and general availability. That explains a lot...</p>
<p>How do you go back to the previous behavior? Use the <strong><em>context variables.</em></strong> Here is the documentation for each one directly from GitHub Copilot Chat docs:</p>
<blockquote>
<p>The <code>#selection</code> context variable enables you to focus Copilot's suggestions on the specific text you select in the editor.</p>
<p>The <code>#file</code> variable lets you reference specific files from your workspace in your chat prompt. This helps make the answers from Copilot Chat more relevant to your code by providing context about the file you are working with. You can ask questions like "Can you suggest improvements to #[file:package.json](file:package.json)?" or "How do I add an extension in #[file:devcontainer.json](file:devcontainer.json)?". By using the <code>#file</code> variable, you can get more targeted and accurate responses from Copilot.</p>
<p>With the <code>#editor</code> context variable, you have control over whether to include the visible code of the active editor in your prompt to Copilot Chat. Previously, this information was automatically included when you hadn't selected text in the editor. Now, you can choose to explicitly add the visible code to the context or omit it for more general questions.</p>
</blockquote>
<p>Given this newfound information, I need to tweak my prompt to include the <code>#editor</code> context variable to get the old behavior back, <strong>"#editor Is there any other suggested refactor to be made?"</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707682724763/471fbcf3-6818-40b0-afe7-7d7962519d2f.png" alt class="image--center mx-auto" /></p>
<p>And just like that, I can return to my old workflow by asking Copilot Chat to grill my entire file!</p>
<p>Hopefully, this little guide helps you out if you were an early adopter of Copilot Chat like me, and if you are a new user, maybe you learned something new.</p>
<p>Happy Coding!</p>
]]></content:encoded></item><item><title><![CDATA[Avoid Trips To The Database With Nebulex - Phoenix Series]]></title><description><![CDATA[Recently, I have been looking for ways to optimize my Stream Closed Captioner application. During one of my typical scroll-throughs of my Following lists and other sites, I discovered Nebulex.

Nebulex, a local and distributed caching toolkit for Eli...]]></description><link>https://blog.guzman.codes/avoid-trips-to-the-database-with-nebulex-phoenix-series</link><guid isPermaLink="true">https://blog.guzman.codes/avoid-trips-to-the-database-with-nebulex-phoenix-series</guid><category><![CDATA[Elixir]]></category><category><![CDATA[Phoenix framework]]></category><category><![CDATA[caching]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Fri, 01 Dec 2023 23:21:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/19pdhEmwkBU/upload/e6fdab65646cb4cee212b6dd58589254.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently, I have been looking for ways to optimize my Stream Closed Captioner application. During one of my typical scroll-throughs of my Following lists and other sites, I discovered Nebulex.</p>
<blockquote>
<p><a target="_blank" href="https://github.com/cabol/nebulex">Nebulex</a>, a local and distributed caching toolkit for Elixir.</p>
</blockquote>
<p>Like with many other caching toolkits, Nebulex covers all the basics.</p>
<blockquote>
<p>This article is not a deep dive into Nebulex features but an example of how I used it in my own application.</p>
</blockquote>
<h2 id="heading-nebulex-basics">Nebulex Basics</h2>
<h3 id="heading-inserting-with-time-to-live">Inserting (with time to live):</h3>
<pre><code class="lang-elixir">Blog.Cache.put(key, data, <span class="hljs-symbol">ttl:</span> <span class="hljs-symbol">:timer</span>.hours(<span class="hljs-number">1</span>))
</code></pre>
<h3 id="heading-retrieving">Retrieving:</h3>
<pre><code class="lang-elixir">Blog.Cache.get(key)
</code></pre>
<h3 id="heading-counters">Counters:</h3>
<pre><code class="lang-elixir">Blog.Cache.incr(<span class="hljs-symbol">:my_counter</span>)

Blog.Cache.incr(<span class="hljs-symbol">:my_counter</span>, <span class="hljs-number">5</span>)
</code></pre>
<h2 id="heading-deleting">Deleting:</h2>
<pre><code class="lang-elixir">Blog.Cache.delete(<span class="hljs-number">1</span>)
</code></pre>
<p>Now, this is a partial list of Nebulex capabilities. There are several more functions to interact with the cache, covering many more use cases you might have.</p>
<h2 id="heading-but-why-choose-nebulex">But Why Choose Nebulex?</h2>
<p>While looking at Nebulex, two things caught my eye that would make Nebulex easily fit into any Phoenix implication (in my opinion).</p>
<ol>
<li><p>The easy setup using a mix task</p>
</li>
<li><p>Nebulex implementation of <strong>Cache as System of Record</strong></p>
</li>
</ol>
<h3 id="heading-easy-setup">Easy Setup</h3>
<p>Starting with Nebulex is extremely easy because of their handy dandy mix task. Just run:</p>
<pre><code class="lang-elixir">mix nbx.gen.cache -c Blog.Cache
</code></pre>
<p>This command will generate the cache configuration in your config.exs and create a cache module. The last step is to add the cache module to your supervision tree. This step is called out in the docs and task output.</p>
<h3 id="heading-cache-as-system-of-record-cache-as-sor">Cache as System of Record (Cache-as-SoR)</h3>
<p>This feature caught my eye and is perfect for how I want to use the cache.</p>
<p>If you need to learn what <strong>Cache as System of Record</strong> is, here is an excerpt from the <a target="_blank" href="https://hexdocs.pm/nebulex/cache-usage-patterns.html#cache-as-sor">Nebulex docs</a>.</p>
<blockquote>
<p>The cache-as-SoR pattern implies using the cache as though it were the primary system-of-record (SoR).</p>
<p>The pattern delegates SoR reading and writing activities to the cache, so that application code is (at least directly) absolved of this responsibility. To implement the cache-as-SoR pattern, use a combination of the following read and write patterns:</p>
<ul>
<li><p><strong>Read-through</strong></p>
</li>
<li><p><strong>Write-through</strong></p>
</li>
</ul>
<p>Advantages of using the cache-as-SoR pattern are:</p>
<ul>
<li><p>Less cluttered application code (improved maintainability through centralized SoR read/write operations)</p>
</li>
<li><p>Choice of write-through or write-behind strategies on a per-cache basis</p>
</li>
<li><p>Allows the cache to solve the thundering-herd problem</p>
</li>
</ul>
</blockquote>
<p>To better understand what this means, here is a code example of implementing a <a target="_blank" href="https://hexdocs.pm/nebulex/cache-usage-patterns.html#cache-as-sor"><strong>read-through</strong></a> pattern in my project:</p>
<pre><code class="lang-elixir">  <span class="hljs-variable">@decorate</span> cacheable(
              <span class="hljs-symbol">cache:</span> Cache,
              <span class="hljs-symbol">key:</span> {StreamSettings, user_id}
            )
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_stream_settings_by_user_id</span></span>(user_id) <span class="hljs-keyword">do</span>
    Repo.get_by(StreamSettings, <span class="hljs-symbol">user_id:</span> user_id)
  <span class="hljs-keyword">end</span>
</code></pre>
<p>In my Settings module, the <code>get_stream_settings_by_user_id</code> is called every time a caption payload is broadcasted to viewers to check if the broadcaster wants to add additional profanity filters or other text modifiers. Nebulex offers the <code>cacheable</code> decorator function that will cache the result of my <code>get_stream_settings_by_user_id</code> function using a specified <strong>key</strong>. If data already exists in the cache and hasn't expired, it will return the cached result the next time this function is called.</p>
<p>In the above code, I am caching <code>StreamSettings</code> by the <code>user_id</code> and saving time by avoiding trips to the database till the cache entry expires. I love how easy it is to set up caching on any function I use.</p>
<p>But what happens if the user's StreamSettings are updated? That's where the <a target="_blank" href="https://hexdocs.pm/nebulex/cache-usage-patterns.html#write-through"><strong>write-through</strong></a> pattern's decorator comes in! Let's look at some code again:</p>
<pre><code class="lang-elixir"><span class="hljs-variable">@decorate</span> cache_put(
            <span class="hljs-symbol">cache:</span> Cache,
            <span class="hljs-symbol">key:</span> {StreamSettings, stream_settings.user_id}
          )
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update_stream_settings</span></span>(%StreamSettings{} = stream_settings, attrs) <span class="hljs-keyword">do</span>
  stream_settings
  |&gt; StreamSettings.update_changeset(attrs)
  |&gt; Repo.update()
<span class="hljs-keyword">end</span>
</code></pre>
<p><code>cache_put</code> is the magic function here. It will update the cache with the result of <code>update_stream_settings</code> using the key specified, <code>{StreamSettings, stream_settings.user_id}</code>. I like how Nebulex makes it simple to wrap your existing functions with cache reads and writes.</p>
<blockquote>
<p><strong><em>But the value you are caching in your read-through is different from the value you are caching in your write-through function!?!?</em></strong></p>
</blockquote>
<p>Good Catch! Well, Nebulex also has a function for that. If the value you are caching using the <code>cacheable</code> function is a different shape from the value that the <code>cache_put</code> function, you can use the <code>cache_evict</code> function. Let's go through an example:</p>
<pre><code class="lang-elixir"><span class="hljs-comment"># Cache - key:{StreamSettings, 123}, value: nil</span>
get_stream_settings_by_user_id(<span class="hljs-number">123</span>) 
<span class="hljs-comment"># Return: %StreamSettings{some_data}</span>
<span class="hljs-comment"># Cache - key:{StreamSettings, 123}, value: %StreamSettings{some_data}</span>
</code></pre>
<p>Calling <code>get_stream_settings_by_user_id</code> places takes the returned value and places it in the cache, going from <code>nil</code> --&gt; <code>%StreamSettings{some_data}</code></p>
<pre><code class="lang-elixir">update_stream_settings(stream_settings, %{update_data}) 
<span class="hljs-comment"># Return: {:ok, %StreamSettings{some_updated_data}}</span>
<span class="hljs-comment"># Cache - key: {StreamSettings, 123}, value: {:ok, %StreamSettings{some_updated_data}}</span>
</code></pre>
<p>But the problem arises with the return value of <code>update_stream_settings</code>. Instead of the result being a StreamSettings struct, it instead returns a tuple <code>{:ok, %StreamSettings{}}</code>. <a target="_blank" href="https://hexdocs.pm/nebulex/cache-usage-patterns.html#write-through">But <code>cache_evict</code> is here to save the day!</a></p>
<pre><code class="lang-elixir"><span class="hljs-variable">@decorate</span> cache_evict( <span class="hljs-comment"># &lt;------- The change -------</span>
            <span class="hljs-symbol">cache:</span> Cache,
            <span class="hljs-symbol">key:</span> {StreamSettings, stream_settings.user_id}
          )
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update_stream_settings</span></span>(%StreamSettings{} = stream_settings, attrs) <span class="hljs-keyword">do</span>
  stream_settings
  |&gt; StreamSettings.update_changeset(attrs)
  |&gt; Repo.update()
<span class="hljs-keyword">end</span>
<span class="hljs-comment"># Cache - key:{StreamSettings, 123}, value: nil</span>
</code></pre>
<p>Instead of caching the new result from <code>update_stream_settings</code> this will remove the value from the cache, removing the worry or mismatched cached data and instead letting the <strong>read-through</strong> function, aka <code>cacheable</code> write to the action on the following invocation.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Nebulex's <strong>Cache as System of Record</strong> is a feature that fits right into my mental model of how I would like to work with a cache. Just decorate the functions I use throughout my applications and BAM caching with minimal configuration. Nebulex has more features; look at Nebulex if you're in the market for a caching toolkit.</p>
<p>Happy Coding!</p>
]]></content:encoded></item><item><title><![CDATA[Using Phoenix Channels? High Memory Usage? Save Money With ERL_FULLSWEEP_AFTER - Phoenix Series]]></title><description><![CDATA[Phoenix Channels are amazing. They make using WebSocket connections a breeze. They are even more lovely using them with Absinthe, a GraphQL server implementation, and GraphQL Connections.
But over time, I discovered Phoenix Channels has a downside. I...]]></description><link>https://blog.guzman.codes/using-phoenix-channels-high-memory-usage-save-money-with-erlfullsweepafter</link><guid isPermaLink="true">https://blog.guzman.codes/using-phoenix-channels-high-memory-usage-save-money-with-erlfullsweepafter</guid><category><![CDATA[Phoenix Channels]]></category><category><![CDATA[Elixir]]></category><category><![CDATA[Phoenix framework]]></category><category><![CDATA[memory-management]]></category><category><![CDATA[garbagecollection]]></category><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Fri, 17 Nov 2023 20:00:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/5OUMf1Mr5pU/upload/2ee083047d0ad891c7fad066d0ebf473.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Phoenix Channels</strong> are amazing. They make using WebSocket connections a breeze. They are even more lovely using them with <strong>Absinthe</strong>, a GraphQL server implementation, and <strong>GraphQL Connections</strong>.</p>
<p>But over time, I discovered Phoenix Channels has a downside. If you use long-lived connections, memory usage on your server will remain unusually high.</p>
<h2 id="heading-how-i-use-phoenix-channels">How I Use Phoenix Channels</h2>
<p>Now for a bit of background on how I use Phoenix Channels. My <a target="_blank" href="https://stream-cc.gooseman.codes"><strong>Stream Closed Captioner</strong></a> application is a free Closed Caption service I offer for Twitch streamers and Zoom meetings. Users log into a dashboard on the website and turn on Speech-to-text. Then a Phoenix Channel sends Text from the front end to the back end, where it goes through a couple of filters like profanity filtering, among other things.</p>
<p>If a Twitch streamer is using the service, it will broadcast the resulting text over another Phoenix Channel for that user's viewers to receive. If they use Zoom, it will send an HTTP request to the Zoom meeting's Closed Caption endpoint.</p>
<p>On the Twitch viewer side, the app communicates to the server via GraphQL. When a Twitch streamer goes live, the viewer client will initialize a <strong>GraphQL Connection</strong> (aka WebSocket/Phoenix Channel) to receive broadcaster captions from the server.</p>
<p>In other words, each streamer will have a connection to send captions to the backend. Each viewer is another connection that lasts for the live broadcast. For example, if a live stream has 200 viewers, that makes for 200 + 1 connections.</p>
<h2 id="heading-what-does-that-mean-for-memory-consumption">What Does That Mean For Memory Consumption?</h2>
<p>Typically, Twitch live streams last at least 3 hours, and I quickly discovered these connections can take up a lot of memory. 6-7 gigabytes of memory to support the number of connections necessary for all streams... YIKES</p>
<p>But why? I spent a little time with my nose in some books and found this passage in<br /><strong>Real-Time Phoenix:</strong></p>
<blockquote>
<h3 id="heading-short-life-versus-long-life-processes">Short-Life Versus Long-Life Processes</h3>
<p>Real-time applications rely on long-lived processes in order to have a direct connection to clients—this allows them to send data immediately when the data is available. There is a downside to this, though. It’s possible that processes get stuck in a state where garbage collection doesn’t occur, but memory isn’t being used. This can cause large memory bloat when amplified across thousands of processes.</p>
<p>If a piece of memory makes it past generational garbage collection, it lives until a full-sweep garbage collection occurs. This happens, by default, every 65535 generational passes or when the process is close to using its available memory. It’s possible for a Channel, Socket, or other long-lived process to get stuck in a state where there is plenty of free memory, but not enough work to trigger a generational pass. A process in this state will live without memory being collected, and it will potentially take up more memory than necessary.</p>
<p>Processes that are created and terminated quickly (short-lived processes) do not get into this state because their memory is reclaimed when the process is terminated. While all applications use long-life processes to some degree, real-time apps use them in the thousands. This amplifies the impact of memory bloat.</p>
<p>You can fix this problem by forcing a full-sweep garbage collection to occur...</p>
</blockquote>
<p>Why memory consumption is so high now makes a lot more sense. Every connection that is being initialized is long-lived, and the garage collector won't clean up the memory of these processes until 65k garbage collection sweeps have happened. Now what?</p>
<h2 id="heading-but-there-is-a-solution">But There Is A Solution!</h2>
<p>Luckily, <strong>Real-Time Phoenix</strong> also recommends a few things you can do to force garbage collection sooner.</p>
<p>One such setting is <code>ERL_FULLSWEEP_AFTER</code>.</p>
<h2 id="heading-understanding-erlfullsweepafter">Understanding ERL_FULLSWEEP_AFTER</h2>
<p><code>ERL_FULLSWEEP_AFTER</code> is a setting related to the Erlang Virtual Machine’s (VM) garbage collection mechanism. It allows you to specify the maximum number of generational collections before forcing a full-sweep, a type of garbage collection that cleans up memory that is no longer in use. By default, this is set to 65535.</p>
<pre><code class="lang-elixir">ERL_FULLSWEEP_AFTER <span class="hljs-number">20</span>
</code></pre>
<p><strong>Real-Time Phoenix</strong> author mentioned that they had success setting this value to 20 for their application, so this is what I set my environment variable, but it can be any value that works for you.</p>
<h2 id="heading-the-result">The Result!</h2>
<p>Within minutes of setting the <code>ERL_FULLSWEEP_AFTER</code> environment variable to 20, I saw results in my Gigalixir memory consumption dashboard. I went from using 6 gigabytes of memory down to around 3.5 gigabytes in a dramatic fashion. Unfortunately, I forgot to snap a screenshot of the dashboard, but the screenshots below still show the updated setting at work.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1700324136879/097bb0e3-9ce6-4051-8543-132261bd2b32.png" alt class="image--center mx-auto" /></p>
<p>The above image shows memory consumption after two deployments. You can see the memory consumption jump up to my historical average of around 6 gigabytes after all existing users on the application reconnect to the new instance. After the 20 minor garbage collections kick in, the major garbage collector is triggered, and you see a significant drop.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1700324390858/8c4b1088-0816-4cd1-afd6-d578d0df7c04.png" alt class="image--center mx-auto" /></p>
<p>In this image, those spikes are Twitch streamers going live and connections for all their viewers being made. You can quickly see how much memory connections can take up. But after 20 minor garbage collections, the major garbage collector starts cleaning things up, and you see a significant drop. Amazing! With one simple setting, I solved my memory consumption woes without issues.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>If you're building a real-time application using Phoenix Channels, it's essential to be mindful of memory usage, especially if you're dealing with long-lived connections. While Phoenix Channels are fantastic for using WebSocket connections, they can cause memory bloat when used in thousands of processes. However, with a bit of knowledge and tweaking of the ERL_FULLSWEEP_AFTER setting, you can force garbage collection sooner and dramatically reduce memory consumption. So, if you're experiencing a similar issue with your Phoenix Channels application, give this solution a try and see the results for yourself.</p>
<p>I would also like to make a plug for <strong>Real-Time Phoenix by Stephen Bussey</strong>. It's an awesome book, and you can find the passage referenced in the chapter <strong>Manage Your Application’s Memory Effectively,</strong> along with more goodies.</p>
<p>Happy coding!</p>
]]></content:encoded></item><item><title><![CDATA["Understanding GraphQL Security: Protecting Your Server from Abuse" Resources]]></title><description><![CDATA[https://www.youtube.com/watch?v=2peiqLuKKgs
 
Supporting resource for my talk at GraphQL Summit, "Understanding GraphQL Security: Protecting Your Server from Abuse" Resources
https://docs.google.com/presentation/d/1n57LAAscp3KpWydJ8SkfRj4Bd4Vi8oxQDoe...]]></description><link>https://blog.guzman.codes/understanding-graphql-security-protecting-your-server-from-abuse-resources</link><guid isPermaLink="true">https://blog.guzman.codes/understanding-graphql-security-protecting-your-server-from-abuse-resources</guid><category><![CDATA[GraphQL]]></category><category><![CDATA[Security]]></category><category><![CDATA[APIs]]></category><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Thu, 05 Oct 2023 03:37:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1696476761532/16ced179-6e74-4862-a447-5e3ee4492354.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=2peiqLuKKgs">https://www.youtube.com/watch?v=2peiqLuKKgs</a></div>
<p> </p>
<p>Supporting resource for my talk at GraphQL Summit, <strong>"Understanding GraphQL Security: Protecting Your Server from Abuse" Resources</strong></p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://docs.google.com/presentation/d/1n57LAAscp3KpWydJ8SkfRj4Bd4Vi8oxQDoetDJ8dD_Y/edit">https://docs.google.com/presentation/d/1n57LAAscp3KpWydJ8SkfRj4Bd4Vi8oxQDoetDJ8dD_Y/edit</a></div>
<p> </p>
<h3 id="heading-graphql-server-features-to-limit-or-disable-in-production">GraphQL server features to limit or disable in production</h3>
<ul>
<li><p><strong>Disable Introspection</strong> - Not only disable GraphiQL but also prevent introspection.</p>
</li>
<li><p><strong>Block field suggestions</strong> - Prevent returning field suggestions and leaking your schema to unauthorized actors.</p>
</li>
<li><p><strong>Character Limit</strong> - Limit the number of characters in a GraphQL query document.</p>
</li>
<li><p><strong>Cost limit</strong> - Limit the complexity of a GraphQL document.</p>
</li>
<li><p><strong>Max Aliases</strong> - Limit the number of aliases in a GraphQL document.</p>
</li>
<li><p><strong>Max Depth</strong> - Limit the depth of a GraphQL document.</p>
</li>
<li><p><strong>Max Directives</strong> - Limit the number of directives in a GraphQL document.</p>
</li>
</ul>
<h2 id="heading-learning-resource">Learning Resource</h2>
<ul>
<li><p><a target="_blank" href="https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html">GraphQL Cheat Sheet - Learning resource for possible vulnerabilities attack vectors</a></p>
</li>
<li><p><a target="_blank" href="https://nostarch.com/black-hat-graphql">Black Hat GraphQL</a></p>
<p>  <a target="_blank" href="https://nostarch.com/black-hat-graphql"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696476208815/2e2cdc2f-3696-4762-b47b-a9c7fa8fede0.png" alt /></a></p>
</li>
</ul>
<h2 id="heading-language-specific-solutions">Language Specific Solutions</h2>
<h3 id="heading-nodejs">NodeJS</h3>
<ul>
<li><a target="_blank" href="https://escape.tech/graphql-armor/docs/getting-started">GraphQL Armor - Add a security layer to Apollo Server, GraphQL Yoga, Envelop</a></li>
</ul>
<h3 id="heading-elixir">Elixir</h3>
<ul>
<li><p><a target="_blank" href="https://hexdocs.pm/absinthe/complexity-analysis.html">Safety Limits - Absinthe configurations to help enforce cost and token limits</a></p>
</li>
<li><p><a target="_blank" href="https://hexdocs.pm/vigil/Vigil.html">Vigil - Disallows introspection of a GraphQL schema and sanitizes error messages to be completely generic and not reveal information about your schema.</a></p>
</li>
<li><p><a target="_blank" href="https://hexdocs.pm/absinthe_security/readme.html">AbsintheSecurity - provides utilities to improve the security posture of APIs built with Absinthe GraphQL.</a></p>
</li>
</ul>
<h3 id="heading-ruby-graphql">Ruby GraphQL</h3>
<ul>
<li><p><a target="_blank" href="https://graphql-ruby.org/schema/definition.html#default-limits">How to set max_depth and max_complexity to apply limits to incoming queries.</a></p>
</li>
<li><p><a target="_blank" href="https://graphql-ruby.org/schema/introspection.html#disabling-introspection">Disable introspection</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Learn How to Use the GitHub Command Palette Like a Pro and Improve Your Git Workflow]]></title><description><![CDATA[You'll love the GitHub Command Palette if you're looking for a way to navigate, search, and run commands on GitHub with just your keyboard. This feature is currently in public beta, and it lets you access any page or resource on GitHub with a few key...]]></description><link>https://blog.guzman.codes/learn-how-to-use-the-github-command-palette-like-a-pro-and-improve-your-git-workflow</link><guid isPermaLink="true">https://blog.guzman.codes/learn-how-to-use-the-github-command-palette-like-a-pro-and-improve-your-git-workflow</guid><category><![CDATA[GitHub]]></category><category><![CDATA[features]]></category><category><![CDATA[workflow]]></category><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Wed, 19 Apr 2023 18:27:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1681928237241/b1434c57-b76e-4940-bced-b94a67ac9b8f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You'll love the GitHub Command Palette if you're looking for a way to navigate, search, and run commands on GitHub with just your keyboard. This feature is currently in public beta, and it lets you access any page or resource on GitHub with a few keystrokes. You can also execute commands to optimize your workflows, such as switching themes, creating issues, or opening pull requests. In this post, I'll show you how to activate the GitHub Command Palette and share some tips on how to get the most out of it.</p>
<h2 id="heading-how-to-activate-the-github-command-palette">How to activate the GitHub Command Palette</h2>
<p>The GitHub Command Palette is available as a feature preview, which means you can opt-in to try it out before it becomes generally available. To activate the GitHub Command Palette, follow these steps:</p>
<ol>
<li><p>Go to <a target="_blank" href="https://github.com/">https://github.com/</a></p>
</li>
<li><p>Click on your top-right profile icon</p>
</li>
<li><p>Select "Feature preview."</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681926557007/3a705d55-a88b-469a-aa24-19478cda49dd.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Under "GitHub Command Palette," click "Enable."</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681926781729/6d2bf0be-6b4d-4c4b-a1d7-62fddacf1872.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>You'll see a confirmation message that says, "GitHub Command Palette enabled."</p>
</li>
<li><p>You can now use the GitHub Command Palette on any page on GitHub</p>
</li>
</ol>
<h2 id="heading-how-to-open-the-github-command-palette">How to open the GitHub Command Palette</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681928099502/89569f1a-a4b3-4185-bec2-9c7ba818707e.png" alt class="image--center mx-auto" /></p>
<p>To open the GitHub Command Palette, use one of the following default keyboard shortcuts:</p>
<ul>
<li><p>Windows and Linux: Ctrl + K or Ctrl + Alt + K</p>
</li>
<li><p>Mac: Command + K or Command + Option + K</p>
</li>
</ul>
<p>You can also customize the keyboard shortcuts in the Accessibility section of your user settings. For more information, see "<a target="_blank" href="https://docs.github.com/en/get-started/using-github/github-command-palette#customizing-your-github-command-palette-keyboard-shortcuts">Customizing your GitHub Command Palette keyboard shortcuts</a>."</p>
<p>When you open the GitHub Command Palette, it shows your location at the top left and uses it as the scope for suggestions. For example, if you're on a repository page, it will show suggestions related to that repository.</p>
<h2 id="heading-how-to-navigate-with-the-github-command-palette">How to navigate with the GitHub Command Palette</h2>
<p>You can use the GitHub Command Palette to navigate to any page you can access on GitHub. For example, you can quickly jump to your organizations, repositories, issues, pull requests, projects, files, and more.</p>
<p>When you open the GitHub Command Palette, the suggestions are optimized to give you easy access to top-level pages like the Issues page or Pull requests page. If the location you want isn't listed, start entering the name or number for the location to refine the suggestions.</p>
<p>You can also use modes to find specific types of resources and execute commands. To enter a mode, type one of the following symbols:</p>
<pre><code class="lang-plaintext">- &gt; Enter command mode
- # Search for issues, pull requests, discussions, and projects
- ! Search for projects
- @ Search for users, organizations, and repositories
- / Search for files within a repository scope
</code></pre>
<p>For example, if you want to find an issue by its number, type # followed by the issue number and press Enter.</p>
<h2 id="heading-example-of-supercharging-your-development-workflow">Example of Supercharging Your Development Workflow</h2>
<p>If you want to check the status of a pull request you reviewed earlier but don't remember the number or the title. You can use the GitHub Command Palette to find it quickly.</p>
<p>Press <code>Ctrl + K</code> (Windows/Linux) or <code>Command + K</code> (Mac) to open the GitHub Command Palette. Type <code>#</code> and see a list of issues, pull requests, discussions, and projects you have recently interacted with. You can type a few keywords that you remember from the pull request and see the matching results. Select the one that you are looking for and press Enter. You will be taken to the pull request page to see the latest comments and changes.</p>
<p>Boom, navigating GitHub efficiently just happened.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Give GitHub Command Palette a try and see if it helps improve your developer workflow if you find yourself navigating GitHub repos on a regular basis.</p>
]]></content:encoded></item><item><title><![CDATA["Animated Drawings: Bring Your Doodles to Life with AI" - A Tool from Meta AI]]></title><description><![CDATA[Have you ever wondered what your doodles would look like if they came to life? Well, now you can find out with Animated Drawings, a new tool from Meta AI that can turn your sketches into animations using artificial intelligence.
Animated Drawings is ...]]></description><link>https://blog.guzman.codes/animated-drawings-bring-your-doodles-to-life-with-ai-a-tool-from-meta-ai</link><guid isPermaLink="true">https://blog.guzman.codes/animated-drawings-bring-your-doodles-to-life-with-ai-a-tool-from-meta-ai</guid><category><![CDATA[AI]]></category><category><![CDATA[tools]]></category><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Sat, 15 Apr 2023 22:13:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1681596820722/69cb2e22-2367-4b09-a838-57b3e0475157.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Have you ever wondered what your doodles would look like if they came to life? Well, now you can find out with Animated Drawings, a new tool from Meta AI that can turn your sketches into animations using artificial intelligence.</p>
<p>Animated Drawings is a web-based application that lets you upload your own drawings of human-like figures or choose from some demo figures and then select from various preset animations such as dance, funny, jumping, and walking. The tool uses advanced AI techniques to capture, rig, deform, and animate your drawings in seconds.</p>
<p>You can try it out for yourself here: <a target="_blank" href="https://sketch.metademolab.com/">https://sketch.metademolab.com/</a></p>
<p>Here are some tips to get the best results from Animated Drawings:</p>
<ul>
<li><p>Make sure your drawing is clear and simple. Avoid using too many colors, details, or background elements that might confuse the tool.</p>
</li>
<li><p>Draw your figure with two arms, two legs, a head, and a torso. The tool works best with human-like characters that have these body parts.</p>
</li>
<li><p>Adjust the capture box to fit snugly around your drawing. Make sure to use the pen and eraser tools to select regions that were missed during auto masking or remove unwanted areas before animating. This will make your animation look even better.</p>
</li>
<li><p>Experiment with different animations.</p>
</li>
<li><p>Remember to have fun. It’s free, after all.</p>
</li>
</ul>
<p>Since I am horrible at drawing, I thought, "Why not get DALL-E to draw me something and use AI to animate my AI-created drawing." Here is the photo I decided to use:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681595177570/d66c4a62-bed9-4220-998e-2e02d2fa6228.png" alt class="image--center mx-auto" /></p>
<p>I used the prompt: "<strong>Children style drawing, white background, goose spreading wings."</strong></p>
<p>And here is the end result using the "waving hello" animation.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExMTM0N2U2MWFhZDM4Y2JmYzM0MDBiOTUxZjRlZmIwNzI2N2I0YmY3YSZjdD1n/ejkhhgkm3SIPw5ttoR/giphy.gif">https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExMTM0N2U2MWFhZDM4Y2JmYzM0MDBiOTUxZjRlZmIwNzI2N2I0YmY3YSZjdD1n/ejkhhgkm3SIPw5ttoR/giphy.gif</a></div>
<p> </p>
<p>Meta AI has open-sourced the project and encourages developers to create new, immersive experiences with drawing-to-animation. You can learn more about the project and access the code and data here: <a target="_blank" href="https://github.com/facebookresearch/AnimatedDrawings">https://github.com/facebookresearch/AnimatedDrawings</a></p>
<p>I think Animated Drawings is an excellent example of how AI can inspire creativity and joy. What do you think? Have you tried it out? Feel free to share your creations in the comment below.</p>
]]></content:encoded></item><item><title><![CDATA[Bing Has a Hidden Feature: An AI Image Creator]]></title><description><![CDATA[Have you ever wished you could create images from your imagination, without any drawing skills or expensive software? Well, now you can, thanks to Bing Image Creator, a new feature from Microsoft's search engine that lets you generate images from wor...]]></description><link>https://blog.guzman.codes/bing-has-a-hidden-feature-an-ai-image-creator</link><guid isPermaLink="true">https://blog.guzman.codes/bing-has-a-hidden-feature-an-ai-image-creator</guid><category><![CDATA[AI]]></category><category><![CDATA[Artificial Intelligence]]></category><category><![CDATA[Bing]]></category><category><![CDATA[#Bing-AI]]></category><category><![CDATA[Dall-e2]]></category><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Tue, 28 Mar 2023 14:49:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1679985149400/a2744702-0324-4452-9cfc-bcd1dee68601.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Have you ever wished you could create images from your imagination, without any drawing skills or expensive software? Well, now you can, thanks to Bing Image Creator, a new feature from Microsoft's search engine that lets you generate images from words with AI.</p>
<p>Bing Image Creator is powered by Dall-E 2, an AI tool developed by OpenAI that can create images based on text prompts. You can use Bing Image Creator to explore your creativity, illustrate your ideas, or just have fun with AI.</p>
<p>The weird part about this new feature is that when visiting Bing.com you won't see any indications that this feature exists but momentarily I will show you exactly how to use it.</p>
<h2 id="heading-how-to-use-bing-image-creator">How to use Bing Image Creator</h2>
<p>There are two ways to use Bing Image Creator.</p>
<ol>
<li><p>Through Bing Chat</p>
</li>
<li><p>Dedicated Image Creator website</p>
</li>
</ol>
<h3 id="heading-using-it-in-the-bing-chat">Using It In The Bing Chat</h3>
<p>To use Bing Image Creator through the Bing chat window, you need to have access to Bing Preview, a new version of Bing that features an AI chat functionality. You can sign up for the Bing Preview by going to <a target="_blank" href="http://bing.com/chat">bing.com/chat</a> in Microsoft Edge and signing in with your Microsoft account.</p>
<p>If you do already have access and are on the “Chat” page. Then, you need to change the conversation style to Creative using the selection box.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679981151158/ab22e112-21b5-4052-a29d-302e35148f74.png" alt class="image--center mx-auto" /></p>
<p>Next, you can enter a prompt into the chat window for what you'd like your image to be. Make sure you specify somehow that you'd like an image created. For example, you can type "draw me a picture of a dragon" or "create an image of a cat wearing sunglasses". Hit enter and see what Bing Image Creator comes up with. It will make 4 different interpretations of your image and use up one of your <strong>"boosts"</strong> (Find out about what boosts are in the Image Creator section).</p>
<p>You can make further adjustments from there using additional prompts which will use up your <strong>"boosts"</strong>.</p>
<blockquote>
<p>The annoying aspect about using the Image Creator through Bing Chat is that it hides the concept of "boosts" and you can easily run out of them as you ask it to generate more images or make edits.</p>
</blockquote>
<p>Here is what it created for me using the prompt <strong>"draw me a goose wearing aviators, cartoon"</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679981354406/c0bb4752-5984-4ea8-bb58-ff9c1b836984.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-using-the-dedicated-image-creator">Using The Dedicated Image Creator</h3>
<p>To use Bing Image Creator through the dedicated website, <strong><em>you don't need to have access to Bing Preview or use Microsoft Edge</em></strong>. You can simply go to <a target="_blank" href="http://bing.com/create">bing.com/create</a> and sign in with your Microsoft account.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679981701778/24dcf519-0209-43b0-92d1-9032e1156a80.png" alt class="image--center mx-auto" /></p>
<p>Starting you will have a set amount of <strong>“boosts”</strong> to have <strong>“priority”</strong> image generation. But fear not, you can still generate more images for free after you use up all your <strong>"boosts"</strong> but at a slower rate.</p>
<p>Once you have Image Creator open, you can enter a prompt into the text box for what you'd like your image to be. You can also provide additional contexts like location or activity, and choose an art style like you would in the Bing Chat. Click Create and see what Bing Image Creator generates.</p>
<p>Here is what was generated for me using the following detailed prompt <strong>"Looking down a downtown street with sky scraper on each side of the street and the sunset in the distance over the water, pixel art"</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679982274405/b144e3af-78af-42c2-84ba-cf783b9c7b9a.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-bing-chat-vs-image-creator">Bing Chat vs Image Creator</h3>
<p>Although you are essentially using the same product, the "Image Creator". One observation from using the <strong>Image Creator</strong> directly versus the <strong>Bing Chat</strong> is that adjusting the image generation using the <strong>Image Creator</strong> isn't as easy as the Bing Chat follow-up prompts. You will need to completely adjust your prompt with trial and error. Also, Bing Chat gives you some great follow-up prompts to give you ideas on what else you can do.</p>
<p>Here is how easy it was to improve the image using the Bing Chat with 2 follow-up prompts.</p>
<p><strong>"Make it more details with more of the water visible and the sun half way sunset.</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679983207319/09da5e26-2e24-4f9a-81ab-1237ac2d8d15.png" alt class="image--center mx-auto" /></p>
<p>Then, <strong>"Add cars on the street and seagulls flying in the distance silouette by the sun."</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679983234471/e375b6fe-2540-4ee4-aae6-c54000023d0b.png" alt class="image--center mx-auto" /></p>
<p>But one nice thing <strong>Image Creator</strong> is that you are not forced to use the Edge browser if you find that to be an annoyance.</p>
<h2 id="heading-tips-and-tricks">Tips and Tricks</h2>
<p>Here are some tips and tricks for using Bing Image Creator effectively:</p>
<ul>
<li><p>Be specific and descriptive with your prompts. The more details you provide, the more likely you are to get an image that matches your expectations. For example, instead of typing "a dog", try typing "a brown Labrador retriever sitting on a couch".</p>
</li>
<li><p>Experiment with different prompts and art styles. You can try different combinations of words and styles to see how they affect the output. For example, you can type "a dragon made of flowers" and choose "realistic" or "abstract" as the art style.</p>
</li>
<li><p>Use quotation marks for exact phrases. If you want Bing Image Creator to use a specific phrase as part of your prompt, put it in quotation marks. For example, if you type "a poster for \"The Matrix\"", Bing Image Creator will try to create an image that resembles a poster for the movie The Matrix.</p>
</li>
<li><p>Don't worry about saving your creations, you can see everything you created in both Bing Chat and Image Creator by visiting the Image Creator dedicated page and clicking on the <strong>"Creations"</strong> tab</p>
</li>
</ul>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Bing Image Creator is a great way to unleash your imagination and see what AI can do with little commitment. Try it out today and share your creations with us!</p>
<p>It's a tie for my favorite between these two, click them for direct links to them in Image Creator:</p>
<p><a target="_blank" href="https://www.bing.com/images/create/a-downtown-street-with-sky-scraper-on-each-side-of/6422816a23344f42b79c06f47faa55f8?id=wHqlB5tECqjIIU%2fNRhR8qw%3d%3d&amp;view=detailv2&amp;idpp=genimg&amp;FORM=GCRIDP&amp;mode=overlay"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679983934365/969b41c2-ca27-4194-8539-1d3d96660231.jpeg" alt class="image--center mx-auto" /></a></p>
<p><a target="_blank" href="https://www.bing.com/images/create/a-downtown-street-with-sky-scraper-on-each-side-of/6422816a23344f42b79c06f47faa55f8?id=c%2b%2fnfRG%2b5%2bI9JU123yECKQ%3d%3d&amp;view=detailv2&amp;idpp=genimg&amp;FORM=GCRIDP&amp;mode=overlay"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679984037623/c117dbc1-c246-41f1-909d-d2800d2d3b5b.jpeg" alt class="image--center mx-auto" /></a></p>
]]></content:encoded></item><item><title><![CDATA[What!? ReactJS isn’t for SPAs anymore?]]></title><description><![CDATA[ReactJS has been a popular choice for building single-page applications (SPAs) for years. However, with the release of new documentation, they stripped away almost all mention of SPAs to some people's confusion. Instead, they are pushing the broader ...]]></description><link>https://blog.guzman.codes/what-reactjs-isnt-for-spas-anymore</link><guid isPermaLink="true">https://blog.guzman.codes/what-reactjs-isnt-for-spas-anymore</guid><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Mon, 20 Mar 2023 01:47:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1679633594729/51b1b4c6-3206-4bc3-873c-62f744dc57f4.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>ReactJS has been a popular choice for building single-page applications (SPAs) for years. However, with the release of new documentation, they stripped away almost all mention of SPAs to some people's confusion. Instead, they are pushing the broader idea of “React Architecture.” But what is “React Architecture”? To summarize it:</p>
<blockquote>
<p>React architecture is flexible and does not enforce any particular pattern to write a complex application. Developers are free to choose the design pattern of their choice. React architecture is built on a solid foundation and is simple, flexible, and extensible.</p>
</blockquote>
<p>In other words, React is a library that can be used to build complex applications with the design pattern of your choice. The primary piece to this change is React Server Components, which were teased a couple of years ago. React Server Components are components that can be rendered on both the server and the client. They allow developers to build applications that span both sides of the application, combining the rich interactivity of client-side apps with the improved performance of traditional server rendering. The new documentation now reflects React as a library to use on both the client and server sides. It pushes single-page apps as a footnote and now recommends using a framework like Next.js that incorporates React Server Components instead of create-react-app.</p>
<p>The React team has admitted that they need to do a better job of adequately communicating the initiatives they have going on. We were teased with React Server Components and other features like Suspense a couple of years ago, but there was mostly radio silence after that until the new docs were released, making it appear like React took a hard right turn off the SPAs highway. But React has been silently heading in this new "direction" for a while now; it just wasn't noticeable.</p>
<p>Is React no longer for SPAs? NO, it’s not only for SPAs but also for the server. The React team wants you to start considering React in that way so you can get the best performance possible for your users by first considering frameworks that use the newer features of React to their fullest extent. But in the end, the beauty of “React Architecture” is that you can choose how to use React.</p>
]]></content:encoded></item><item><title><![CDATA[Ruby Turns 30!]]></title><description><![CDATA[30 years ago Ruby programming language was born. Interesting to read that it could have been named coral instead by Ruby. But "coral has worse impression than perl" and Ruby is Matsumoto's birthstone.
Ruby is a programming language I love because of ...]]></description><link>https://blog.guzman.codes/ruby-turns-30</link><guid isPermaLink="true">https://blog.guzman.codes/ruby-turns-30</guid><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Sat, 25 Feb 2023 19:01:14 GMT</pubDate><content:encoded><![CDATA[<p>30 years ago Ruby programming language was born. Interesting to read that it could have been named coral instead by Ruby. But "coral has worse impression than perl" and Ruby is Matsumoto's birthstone.</p>
<p>Ruby is a programming language I love because of its simplicity and community. It doesn't hinder me from building what I want, and I can do it easily.</p>
<p>Check out the original conversation Matz and Keiju had when deciding the code name for what will be know as Ruby today.</p>
<p>https://blade.ruby-lang.org/ruby-talk/88819</p>
<p>How has your experience been with Ruby?</p>
]]></content:encoded></item><item><title><![CDATA[GitHub Copilot Labs blew my mind with the Custom brush]]></title><description><![CDATA[{% embed https://youtu.be/sEW1BriD-u4 %}
Sorry about the mouse cursor not showing up in this video! If you're not already using the GitHub Copilot Labs extension for VSCode then you missing out on some awesome features. In this video I show you one f...]]></description><link>https://blog.guzman.codes/github-copilot-labs-blew-my-mind-with-the-custom-brush</link><guid isPermaLink="true">https://blog.guzman.codes/github-copilot-labs-blew-my-mind-with-the-custom-brush</guid><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Sun, 05 Feb 2023 09:04:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1679633600780/37b3b99c-d0b7-44f8-8d2a-a5eb3b33bbb5.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>{% embed https://youtu.be/sEW1BriD-u4 %}</p>
<p>Sorry about the mouse cursor not showing up in this video! If you're not already using the GitHub Copilot Labs extension for VSCode then you missing out on some awesome features. In this video I show you one feature that blew my mind.</p>
<p>You can install Github Copilot Labs here https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-labs</p>
<p>Find out all the details about Github Copilot Labs Here: https://githubnext.com/projects/copilot-labs/#how-to-use-copilot-labs</p>
]]></content:encoded></item><item><title><![CDATA[Fix iPhone not showing as webcam | Continuity Camera]]></title><description><![CDATA[{% embed https://youtube.com/shorts/3WU3cA5xHtA?feature=share %}
So if you're like me, you've been frustrated with trying to use your iPhone as a webcam on Mac Os using the continuity feature.
Sometimes you load up Chrome, and it just doesn't appear....]]></description><link>https://blog.guzman.codes/fix-iphone-not-showing-as-webcam-or-continuity-camera</link><guid isPermaLink="true">https://blog.guzman.codes/fix-iphone-not-showing-as-webcam-or-continuity-camera</guid><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Sat, 07 Jan 2023 21:22:15 GMT</pubDate><content:encoded><![CDATA[<p>{% embed https://youtube.com/shorts/3WU3cA5xHtA?feature=share %}</p>
<p>So if you're like me, you've been frustrated with trying to use your iPhone as a webcam on Mac Os using the continuity feature.
Sometimes you load up Chrome, and it just doesn't appear. You can use it in FaceTime, but Chrome doesn't have it as a selection. Well, I figured out a workaround, and all you need is one application.</p>
<p>OBS Studio! https://obsproject.com/</p>
<p>All you have to do is</p>
<ol>
<li>Go and download OBS Studio for your Mac Os.</li>
<li>Open up, and you're presented with two sections at the bottom of the window called “Scenes” and “Sources”. You will want to add a new source.</li>
<li>Click the “+” at the bottom of the “Sources” window</li>
<li>Select “Video Capture Device”, and click Ok.</li>
<li>Select your iPhone in the “Device” selection list. Click Ok.</li>
</ol>
<p>You should see your webcam taking up the largest preview area. Everything you see in the preview screen will show up as your webcam.</p>
<ol>
<li>Next, click “Start Virtual Camera”</li>
</ol>
<p>Now go back to whatever website or application you want to use your webcam. Go into the camera selection. And now you have OBS Virtual Camera as a selection. You hopefully have a reliable way to get your iOS webcam showing up consistently.</p>
]]></content:encoded></item><item><title><![CDATA[Making RedwoodJS Easier with VSCode Dev Containers]]></title><description><![CDATA[In this short (but wordy) guide, I would like to show you how you can set up a RedwoodJS application using Postgres to run in a VSCode Dev Container or Github Codespaces. 
But first, if you're not sure what RedwoodJS is, it's an open-source full-stac...]]></description><link>https://blog.guzman.codes/making-redwoodjs-easier-with-vscode-dev-containers</link><guid isPermaLink="true">https://blog.guzman.codes/making-redwoodjs-easier-with-vscode-dev-containers</guid><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Fri, 08 Jul 2022 17:01:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1679633616427/0e21201b-c7ed-4e7c-9cf5-2b50992b2e22.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this short (but wordy) guide, I would like to show you how you can set up a RedwoodJS application using Postgres to run in a VSCode Dev Container or Github Codespaces. </p>
<p>But first, if you're not sure what RedwoodJS is, it's an open-source full-stack web framework that makes building applications extremely easy. Think of Ruby on Rails, but all in JavaScript using libraries you might already be familiar with and extremely easy to set up. You can check it out here <a target="_blank" href="https://redwoodjs.com/docs/introduction">https://redwoodjs.com/docs/introduction</a>.</p>
<p>RedwoodJS is easy to set up and start, but what would make it even better? Making it extremely easy to get your developer environment set up and running with a couple of clicks and compatible with Github Codespaces. This short guide will give you a template needed to do just that!</p>
<p>We will use the Visual Studio Code Dev Container extension/feature to accomplish this goal. It's a fantastic tool I use almost daily to run my projects in what I like to think of as a development sandbox. Since each project runs in its own sandbox, it doesn't have to worry about installing different Ruby, Rails, NodeJS, Elixir, or Erlang to get my project up and running on my computer. So let's get to it.</p>
<h2 id="heading-required-to-get-started">Required To Get Started</h2>
<ul>
<li>Docker for Desktop</li>
<li>Visual Studio Code</li>
</ul>
<h2 id="heading-set-up-dev-container">Set Up Dev Container!</h2>
<p>Alright, to get started, open up your RedwoodJS project in VSCode. If you don't have one, it's as simple as.</p>
<pre><code class="lang-jsx">yarn create redwood-app ./<span class="hljs-built_in">super</span>-awesome-project-name
</code></pre>
<p>Now let's install the <code>Remote Development</code> extension. It should look like the following image.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679633606104/8a6b20e9-c4c8-4d6b-968f-3fb235f6a77b.png" alt="VS Code Remote Development" /></p>
<p>The <code>Remote Development</code> extension package contains everything we would need and more to use Docker as a remote development environment.</p>
<p>Now let's start generating our development container configuration. Bring up the command palette using <code>Command + Shift + P</code> on Mac and <code>Ctrl + Shift + P</code> on Windows.  Type "add dev" and select <code>Add Development Container Configuration Files.</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679633607232/54c3e637-8103-4c7b-be1f-26c2dfb73c38.png" alt="Command palette with add dev typed in" /></p>
<p>You will see a list of pre-built definitions. Well, go with the <code>Node.js &amp; PostgreSQL</code> since it is already almost everything we need to get RedwoodJS up and running.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679633608695/6169afe8-cbed-4dea-8350-70cab3adcfb8.png" alt="Prompt with Node.js &amp; Postgres highlights" /></p>
<p>You will next be asked which version of Node.js you would like your sandbox to run. Let's stick with the default.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679633610221/df8ad7ea-715e-4413-adc3-018d12c9873f.png" alt="Node version selection prompt with default selected" /></p>
<p>The last window allows you to install additional packages/features, but clicking "OK" in the top right will be good enough.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679633611425/a88b2dbb-8a7e-4164-b5b6-66cf4178ab9f.png" alt /></p>
<p>You should now see a new folder in your project called <code>.devcontainer</code>. Inside it will be 4 files, <code>devconatiner.json</code>, <code>docker-compose.yml</code>, <code>create-db-user.sql</code> and <code>Dockerfile</code>. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679633612682/50a138c6-4133-4dc9-8eae-173afee08e11.png" alt="Display of files that will be generated" /></p>
<p>Well, ignore <code>create-db-user.sql</code> since it's unimportant for this guide. We also won't be touching the <code>Dockerfile</code> but it's what Docker will use to build a development environment with all bells and whistles we need to run Node.js and more.</p>
<p>Let's open up <code>docker-compose.yml</code> file. We will need to make a minor tweak to it. If you dont know what <code>docker-compose</code> is, it's a tool to tell Docker how to step up containers that will run services we would like. Usually, most web development projects need 2-3 services. One service is your web server, RedwoodJS, for us, and another service would be a database like Postgres.</p>
<p>Out of the box, RedwoodJS will use SQLite, but most of the time, I find myself using Postgres in production, and wouldn't it be cool to emulate production? Using Development Container makes it super easy to use Postgres locally, also. So let's try making our local environment run.  </p>
<p>Our new <code>docker-compose.yml</code> already comes pre-configured with an <code>app</code> container we will use to develop inside. Looking closely, you will see this container is based on our <code>Dockerfile</code> image. It also has a <code>db</code> container to run our development Postgres database. We need to add one more thing, a container to run our test database. I have annotated the code below to add a <code>db-test</code> container and corresponding volumes. You might be able to copy and paste the whole thing.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">'3.8'</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-attr">app:</span>
    <span class="hljs-comment"># Using a Dockerfile is optional, but included for completeness.</span>
    <span class="hljs-attr">build:</span>
      <span class="hljs-attr">context:</span> <span class="hljs-string">.</span>
      <span class="hljs-attr">dockerfile:</span> <span class="hljs-string">Dockerfile</span>
      <span class="hljs-attr">args:</span>
        <span class="hljs-comment"># Update 'VARIANT' to pick an LTS version of Node.js: 18, 16, 14.</span>
        <span class="hljs-comment"># Append -bullseye or -buster to pin to an OS version.</span>
        <span class="hljs-comment"># Use -bullseye variants on local arm64/Apple Silicon.</span>
        <span class="hljs-attr">VARIANT:</span> <span class="hljs-number">16</span><span class="hljs-string">-bullseye</span>

    <span class="hljs-attr">volumes:</span>
      <span class="hljs-comment"># This is where VS Code should expect to find your project's source code and the value of "workspaceFolder" in .devcontainer/devcontainer.json</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">..:/workspace:cached</span>

      <span class="hljs-comment"># Uncomment the next line to use Docker from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker-compose for details.</span>
      <span class="hljs-comment"># - /var/run/docker.sock:/var/run/docker.sock</span>

    <span class="hljs-comment"># Overrides default command so things don't shut down after the process ends.</span>
    <span class="hljs-attr">command:</span> <span class="hljs-string">sleep</span> <span class="hljs-string">infinity</span>

    <span class="hljs-comment"># Runs app on the same network as the service container, allows "forwardPorts" in devcontainer.json function.</span>
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">service:db</span>

    <span class="hljs-comment"># Use "forwardPorts" in **devcontainer.json** to forward an app port locally.</span>
    <span class="hljs-comment"># (Adding the "ports" property to this file will not forward from a Codespace.)</span>

    <span class="hljs-comment"># Uncomment the next line to use a non-root user for all processes - See https://aka.ms/vscode-remote/containers/non-root for details.</span>
    <span class="hljs-comment"># user: vscode</span>

    <span class="hljs-comment"># Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust.</span>
    <span class="hljs-comment"># cap_add:</span>
    <span class="hljs-comment">#   - SYS_PTRACE</span>
    <span class="hljs-comment"># security_opt:</span>
    <span class="hljs-comment">#   - seccomp:unconfined</span>

  <span class="hljs-comment"># You can include other services not opened by VS Code as well</span>
  <span class="hljs-attr">db:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">postgres:latest</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">unless-stopped</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">postgres-data:/var/lib/postgresql/data</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">POSTGRES_USER:</span> <span class="hljs-string">postgres</span>
      <span class="hljs-attr">POSTGRES_DB:</span> <span class="hljs-string">development_db</span>
      <span class="hljs-attr">POSTGRES_PASSWORD:</span> <span class="hljs-string">postgres</span>
  <span class="hljs-comment">###### START ADDED SECTION: Create a container to run the test database</span>
  <span class="hljs-attr">db-test:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">postgres:latest</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">unless-stopped</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">postgres-test-data:/var/lib/postgresql/data</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">POSTGRES_USER:</span> <span class="hljs-string">postgres</span>
      <span class="hljs-attr">POSTGRES_DB:</span> <span class="hljs-string">test_db</span>
      <span class="hljs-attr">POSTGRES_PASSWORD:</span> <span class="hljs-string">postgres</span>
  <span class="hljs-comment">###### END ADDED SECTION</span>
<span class="hljs-attr">volumes:</span>
  <span class="hljs-attr">postgres-data:</span>
  <span class="hljs-string">postgres-test-data:####</span> <span class="hljs-string">ADDED</span> <span class="hljs-string">LINE</span>
</code></pre>
<p>Now let's open up your <code>.devcontainer.json</code> file. This file is specifically for VSCode. It tells it how to run our development container. More specially, the docker-compose file to use, which container we will use to do development inside, and the extension we would like to install by default. The extension configuration is super lovely, or anyone who works with you will have the same extension automatically installed to make their dev life more effortless. Finally, you can copy and paste the code below into the file.</p>
<pre><code class="lang-yaml">{
    <span class="hljs-attr">"name":</span> <span class="hljs-string">"RedwoodJS &amp; Postgres (Community)"</span>,

    <span class="hljs-string">//</span> <span class="hljs-string">Update</span> <span class="hljs-string">the</span> <span class="hljs-string">'dockerComposeFile'</span> <span class="hljs-string">list</span> <span class="hljs-string">if</span> <span class="hljs-string">you</span> <span class="hljs-string">have</span> <span class="hljs-string">more</span> <span class="hljs-string">compose</span> <span class="hljs-string">files</span> <span class="hljs-string">or</span> <span class="hljs-string">use</span> <span class="hljs-string">different</span> <span class="hljs-string">names.</span>
    <span class="hljs-attr">"dockerComposeFile":</span> <span class="hljs-string">"docker-compose.yml"</span>,

    <span class="hljs-string">//</span> <span class="hljs-string">The</span> <span class="hljs-string">'service'</span> <span class="hljs-string">property</span> <span class="hljs-string">is</span> <span class="hljs-string">the</span> <span class="hljs-string">name</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">service</span> <span class="hljs-string">for</span> <span class="hljs-string">the</span> <span class="hljs-string">container</span> <span class="hljs-string">that</span> <span class="hljs-string">VS</span> <span class="hljs-string">Code</span> <span class="hljs-string">should</span>
    <span class="hljs-string">//</span> <span class="hljs-string">use.</span> <span class="hljs-string">Update</span> <span class="hljs-string">this</span> <span class="hljs-string">value</span> <span class="hljs-string">and</span> <span class="hljs-string">.devcontainer/docker-compose.yml</span> <span class="hljs-string">to</span> <span class="hljs-string">the</span> <span class="hljs-string">real</span> <span class="hljs-string">service</span> <span class="hljs-string">name.</span>
    <span class="hljs-attr">"service":</span> <span class="hljs-string">"app"</span>,

    <span class="hljs-string">//</span> <span class="hljs-string">The</span> <span class="hljs-string">optional</span> <span class="hljs-string">'workspaceFolder'</span> <span class="hljs-string">property</span> <span class="hljs-string">is</span> <span class="hljs-string">the</span> <span class="hljs-string">path</span> <span class="hljs-string">VS</span> <span class="hljs-string">Code</span> <span class="hljs-string">should</span> <span class="hljs-string">open</span> <span class="hljs-string">by</span> <span class="hljs-string">default</span> <span class="hljs-string">when</span>
    <span class="hljs-string">//</span> <span class="hljs-string">connected.</span> <span class="hljs-string">This</span> <span class="hljs-string">is</span> <span class="hljs-string">typically</span> <span class="hljs-string">a</span> <span class="hljs-string">volume</span> <span class="hljs-string">mount</span> <span class="hljs-string">in</span> <span class="hljs-string">.devcontainer/docker-compose.yml</span>
    <span class="hljs-attr">"workspaceFolder":</span> <span class="hljs-string">"/workspace"</span>,

    <span class="hljs-string">//</span> <span class="hljs-string">Configure</span> <span class="hljs-string">tool-specific</span> <span class="hljs-string">properties.</span>
    <span class="hljs-attr">"customizations":</span> {
        <span class="hljs-string">//</span> <span class="hljs-string">Configure</span> <span class="hljs-string">properties</span> <span class="hljs-string">specific</span> <span class="hljs-string">to</span> <span class="hljs-string">VS</span> <span class="hljs-string">Code.</span>
        <span class="hljs-attr">"vscode":</span> {
            <span class="hljs-string">//</span> <span class="hljs-string">Add</span> <span class="hljs-string">the</span> <span class="hljs-string">IDs</span> <span class="hljs-string">of</span> <span class="hljs-string">extensions</span> <span class="hljs-string">you</span> <span class="hljs-string">want</span> <span class="hljs-string">installed</span> <span class="hljs-string">when</span> <span class="hljs-string">the</span> <span class="hljs-string">container</span> <span class="hljs-string">is</span> <span class="hljs-string">created.</span>
            <span class="hljs-attr">"extensions":</span> [
                <span class="hljs-string">"redwoodjs.redwood"</span>,
                <span class="hljs-string">"dbaeumer.vscode-eslint"</span>,
                <span class="hljs-string">"ofhumanbondage.react-proptypes-intellisense"</span>,
                <span class="hljs-string">"mgmcdermott.vscode-language-babel"</span>,
                <span class="hljs-string">"editorconfig.editorconfig"</span>,
                <span class="hljs-string">"prisma.prisma"</span>,
                <span class="hljs-string">"graphql.vscode-graphql"</span>
            ]
        }
    },

    <span class="hljs-string">//</span> <span class="hljs-string">Uncomment</span> <span class="hljs-string">the</span> <span class="hljs-string">next</span> <span class="hljs-string">line</span> <span class="hljs-string">if</span> <span class="hljs-string">you</span> <span class="hljs-string">want</span> <span class="hljs-string">to</span> <span class="hljs-string">keep</span> <span class="hljs-string">your</span> <span class="hljs-string">containers</span> <span class="hljs-string">running</span> <span class="hljs-string">after</span> <span class="hljs-string">VS</span> <span class="hljs-string">Code</span> <span class="hljs-string">shuts</span> <span class="hljs-string">down.</span>
    <span class="hljs-string">//</span> <span class="hljs-attr">"shutdownAction":</span> <span class="hljs-string">"none"</span>,

    <span class="hljs-string">//</span> <span class="hljs-string">Uncomment</span> <span class="hljs-string">the</span> <span class="hljs-string">next</span> <span class="hljs-string">line</span> <span class="hljs-string">to</span> <span class="hljs-string">use</span> <span class="hljs-string">'postCreateCommand'</span> <span class="hljs-string">to</span> <span class="hljs-string">run</span> <span class="hljs-string">commands</span> <span class="hljs-string">after</span> <span class="hljs-string">the</span> <span class="hljs-string">container</span> <span class="hljs-string">is</span> <span class="hljs-string">created.</span>
    <span class="hljs-string">//</span> <span class="hljs-attr">"postCreateCommand":</span> <span class="hljs-string">"uname -a"</span>,

    <span class="hljs-string">//</span> <span class="hljs-string">Comment</span> <span class="hljs-string">out</span> <span class="hljs-string">to</span> <span class="hljs-string">connect</span> <span class="hljs-string">as</span> <span class="hljs-string">root</span> <span class="hljs-string">instead.</span> <span class="hljs-string">To</span> <span class="hljs-string">add</span> <span class="hljs-string">a</span> <span class="hljs-string">non-root</span> <span class="hljs-string">user</span>, <span class="hljs-attr">see:</span> <span class="hljs-string">https://aka.ms/vscode-remote/containers/non-root.</span>
    <span class="hljs-attr">"remoteUser":</span> <span class="hljs-string">"node"</span>
}
</code></pre>
<h2 id="heading-postgres-in-development">Postgres In Development</h2>
<p>The last phase of our changes is going to be with RedwoodJS. Since we're setting up our local development to run Postgres, we need to update some things in our RedwoodJS.</p>
<p>First, let's update our <code>schema.prisma</code> file to run Postgres. Then, update your provider, like in the example below.</p>
<pre><code class="lang-jsx">datasource db {
  provider = <span class="hljs-string">"postgresql"</span> <span class="hljs-comment">// Switched from MySQL to Postgres for local development</span>
  url      = env(<span class="hljs-string">"DATABASE_URL"</span>)
}

generator client {
  provider      = <span class="hljs-string">"prisma-client-js"</span>
  binaryTargets = <span class="hljs-string">"native"</span>
}

<span class="hljs-comment">// Define your own datamodels here and run `yarn redwood prisma migrate dev`</span>
<span class="hljs-comment">// to create migrations for them and apply to your dev DB.</span>
<span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> Please remove the following example:</span>
model UserExample {
  id    Int     @id @<span class="hljs-keyword">default</span>(autoincrement())
  email <span class="hljs-built_in">String</span>  @unique
  name  <span class="hljs-built_in">String</span>?
}
</code></pre>
<p>Next, we need to open and update our <code>.env</code> file. Since we changed to using Postgres, we need to change <code>DATABASE_URL</code> and <code>TEST_DATABASE_URL</code> to point to our Postgres database containers and use the credentials that are set in the <code>docker-compose.yml</code>. Looking back at the <code>docker-compose.yml</code> file, you will see where we got the username, password, and database name. Another important bit is to set the <code>RWJS_DEV_API_URL</code> variable so routing happens properly from within our container to the GQL server.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># THIS FILE SHOULD NOT BE CHECKED INTO YOUR VERSION CONTROL SYSTEM</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment"># Environment variables set here will override those in .env.defaults.</span>
<span class="hljs-comment"># Any environment variables you need in production you will need to setup with</span>
<span class="hljs-comment"># your hosting provider. For example in Netlify you can add environment</span>
<span class="hljs-comment"># variables in Settings &gt; Build &amp; Deploy &gt; environment</span>
<span class="hljs-comment">#</span>
<span class="hljs-string">DATABASE_URL=postgres://postgres:postgres@localhost:5432/development_db</span>
<span class="hljs-string">TEST_DATABASE_URL=postgres://postgres:postgres@localhost:5432/test_db</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment"># Sets an app-specific secret used to sign and verify your own app's webhooks.</span>
<span class="hljs-comment"># For example if you schedule a cron job with a signed payload that later will</span>
<span class="hljs-comment"># then invoke your api-side webhook function you will use this secret to sign and the verify.</span>
<span class="hljs-comment"># Important: Please change this default to a strong password or other secret</span>
<span class="hljs-comment"># WEBHOOK_SECRET=THIS_IS_NOT_SECRET_PLEASE_CHANGE</span>
<span class="hljs-comment"># Set API base URL to be localhost, not 0.0.0.0</span>
<span class="hljs-string">RWJS_DEV_API_URL=http://localhost</span>
</code></pre>
<h2 id="heading-start-it-up">Start It Up!</h2>
<p>Now we're in the home stretch. Let's get your project running in its Development Container. Bring up the command palette using <code>Command + Shift + P</code> on Mac and <code>Ctrl + Shift + P</code> on Windows. Type in "reopen in container" and select "Reopen in Container" option. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679633614120/0b0b7f46-59b3-4919-925f-120eea8021c9.png" alt="Command palette open with Reopen in Container typed in" /></p>
<p>This last step will take a little while. It will be downloading the Docker images for your development environment and Postgres, then building it. So sit tight, grab a cup of tea, and get a stretch break-in.</p>
<p>Once done, your VSCode should open up to what looks like a typical project, but with one secret. Your VSCode is connected to your docker container and running inside. The area you will notice the most significant difference will be the terminal.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679633615501/dc9efd5d-4e04-46e6-9cfb-d546a485c53d.png" alt="Preview of the terminal" /></p>
<p>It says you are inside the <code>workspace</code> folder, and your user is <code>node</code>. If you were to change directories and move up one, you would see completely different folders on your regular computer. You are now in your developer sandbox. Now run <code>yarn redwood dev</code> to start your server and see the magic happen. A bonus to all this work is that it will also work in Github Codespaces.</p>
<p>If there is anything still fuzzy or like me to elaborate further, let me know. I will make sure to do so.</p>
]]></content:encoded></item><item><title><![CDATA[Creating Your Own Custom StreamElements Widgets]]></title><description><![CDATA[{% embed https://youtu.be/4SxpNWEqGSQ %}
This is a video walk-through explaining how you can create your very own Custom Stream Element Widgets. 
Links:
https://streamelements.com
https://github.com/StreamElements/widgets
https://github.com/StreamEle...]]></description><link>https://blog.guzman.codes/creating-your-own-custom-streamelements-widgets</link><guid isPermaLink="true">https://blog.guzman.codes/creating-your-own-custom-streamelements-widgets</guid><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Tue, 21 Jun 2022 17:58:03 GMT</pubDate><content:encoded><![CDATA[<p>{% embed https://youtu.be/4SxpNWEqGSQ %}
This is a video walk-through explaining how you can create your very own Custom Stream Element Widgets. </p>
<p>Links:
https://streamelements.com
https://github.com/StreamElements/widgets
https://github.com/StreamElements/widgets/blob/master/CustomCode.md
https://github.com/StreamElements/widgets/tree/master/CustomChat</p>
]]></content:encoded></item><item><title><![CDATA[Deploying Elixir: Creating Your Own Elixir Package]]></title><description><![CDATA[During your journey as an Elixir developer, there is going to come to a point where you might want to publish your own package. This has happened to me a couple of times already, here are a couple of packages I have published.

https://hex.pm/package...]]></description><link>https://blog.guzman.codes/deploying-elixir-creating-your-own-elixir-package</link><guid isPermaLink="true">https://blog.guzman.codes/deploying-elixir-creating-your-own-elixir-package</guid><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Sun, 06 Mar 2022 18:22:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1679633622815/e89ad280-8398-4f74-ba12-0b84ca296d19.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>During your journey as an Elixir developer, there is going to come to a point where you might want to publish your own package. This has happened to me a couple of times already, here are a couple of packages I have published.</p>
<ul>
<li><p><a target="_blank" href="https://hex.pm/packages/ueberauth_patreon">https://hex.pm/packages/ueberauth_patreon</a></p>
</li>
<li><p><a target="_blank" href="https://hex.pm/packages/ueberauth_twitch">https://hex.pm/packages/ueberauth_twitch</a></p>
</li>
</ul>
<p>Equip with the knowledge of what I needed to do to publish my own package, I thought I would take you on the same journey. Let's get started.</p>
<p>Our first stop is going to be the Hex website. Hex has a great guide for publishing your own package that we will be following.</p>
<p><a target="_blank" href="https://hex.pm/docs/publish">https://hex.pm/docs/publish</a></p>
<p><strong>Registering a Hex User</strong></p>
<p>Before we can do anything, we will need to register our own user. When registering a user, you will be prompted for a username, your email, and a password. The email is used to confirm your identity during signup, as well as to contact you in case there is an issue with one of your packages. The email will never be shared with a third party.</p>
<pre><code class="lang-plaintext">$ mix hex.user register
</code></pre>
<p>Now it’s time to decide on a name for your package. In this guide I will be creating a new Ueberauth package. If you were to go on <a target="_blank" href="http://hex.pm">http://hex.pm</a> and look at other Ueberauth packages, you notice there is a certain pattern followed. This will make the decision easy for us on what to call our Uberauth package that will implement the Patreon OAuth flow.</p>
<blockquote>
<p>uberauth_patreon</p>
</blockquote>
<h2 id="heading-generating-our-project">Generating Our Project</h2>
<p>Now that we know the name of our package let’s generate the project</p>
<p>If you’re curious how to generate a standard mix project, please visit the Elixir site here: <a target="_blank" href="https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html#our-first-project">https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html#our-first-project</a></p>
<pre><code class="lang-bash">mix new ueberauth_patreon
</code></pre>
<p>We should expect to see:</p>
<pre><code class="lang-bash">* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating lib
* creating lib/ueberauth_patreon.ex
* creating <span class="hljs-built_in">test</span>
* creating <span class="hljs-built_in">test</span>/test_helper.exs
* creating <span class="hljs-built_in">test</span>/ueberauth_patreon_test.exs

Your Mix project was created successfully.
You can use <span class="hljs-string">"mix"</span> to compile it, <span class="hljs-built_in">test</span> it, and more:

    <span class="hljs-built_in">cd</span> ueberauth_patreon
    mix <span class="hljs-built_in">test</span>

Run <span class="hljs-string">"mix help"</span> <span class="hljs-keyword">for</span> more commands.
</code></pre>
<p>Let’s change the directory to our new project directory and let’s open the code in your editor of choice. For me I’ll be using Visual Studio Code, this is important later in the guide when I am making a test app.</p>
<p>Next, we will update the mix.exs file to have all the settings we know to start things off. I follow this section of the Hex publishing guide <a target="_blank" href="https://hex.pm/docs/publish#adding-metadata-to-code-classinlinemixexscode">https://hex.pm/docs/publish#adding-metadata-to-code-classinlinemixexscode</a></p>
<pre><code class="lang-elixir"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">project</span></span> <span class="hljs-keyword">do</span>
    [
      <span class="hljs-symbol">app:</span> <span class="hljs-symbol">:ueberauth_patreon</span>,
      <span class="hljs-symbol">description:</span> <span class="hljs-string">"Ueberauth strategy for Patreon OAuth."</span>,
      <span class="hljs-symbol">version:</span> <span class="hljs-string">"1.0.0"</span>,
      <span class="hljs-symbol">elixir:</span> <span class="hljs-string">"~&gt; 1.13"</span>,
      <span class="hljs-symbol">source_url:</span> <span class="hljs-string">"https://github.com/talk2MeGooseman/ueberauth_patreon"</span>,
      <span class="hljs-symbol">homepage_url:</span> <span class="hljs-string">"https://github.com/talk2MeGooseman/ueberauth_patreon"</span>,
      <span class="hljs-symbol">start_permanent:</span> Mix.env() == <span class="hljs-symbol">:prod</span>,
      <span class="hljs-symbol">deps:</span> deps(),
      <span class="hljs-symbol">package:</span> [
        <span class="hljs-symbol">links:</span> %{<span class="hljs-string">"GitHub"</span> =&gt; <span class="hljs-string">"https://github.com/talk2MeGooseman/ueberauth_patreon"</span>},
        <span class="hljs-symbol">licenses:</span> [<span class="hljs-string">"MIT"</span>],
      ]
    ]
  <span class="hljs-keyword">end</span>
</code></pre>
<p>I decided to set my package version at <code>1.0.0</code> but feel free to start it at <code>0.0.1</code> since its your first version ever.</p>
<h2 id="heading-adding-documentation-generation">Adding Documentation Generation</h2>
<p>If you follow along in the Hex guide, they will suggest using <code>ex_doc</code> and there is no reason not to. It generates some beautiful documentation.</p>
<p>Following the HexDoc’s guide <a target="_blank" href="https://hexdocs.pm/ex_doc/readme.html">https://hexdocs.pm/ex_doc/readme.html</a>. Let’s add it to our project dependencies in our <code>mix.exs</code> file.</p>
<pre><code class="lang-elixir"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">deps</span></span> <span class="hljs-keyword">do</span>
  [
        <span class="hljs-comment">## Other deps ...</span>
    {<span class="hljs-symbol">:ex_doc</span>, <span class="hljs-string">"~&gt; 0.27"</span>, <span class="hljs-symbol">only:</span> <span class="hljs-symbol">:dev</span>, <span class="hljs-symbol">runtime:</span> <span class="hljs-keyword">false</span>},
  ]
<span class="hljs-keyword">end</span>
</code></pre>
<p>Dont forget to install our new dependency.</p>
<pre><code class="lang-bash">mix deps.get
</code></pre>
<p>Now let's generate our docs to make sure everything is set up correctly</p>
<pre><code class="lang-bash">&gt; mix docs
Generating docs...
View <span class="hljs-string">"html"</span> docs at <span class="hljs-string">"doc/index.html"</span>
View <span class="hljs-string">"epub"</span> docs at <span class="hljs-string">"doc/ueberauth_patreon.epub"</span>
</code></pre>
<p>If you have the latest NPM installed you can run the following command to see your docs in the browser:</p>
<pre><code class="lang-bash">npx serve doc/
</code></pre>
<p>This will kick off a little webserver without having to go install anything locally.</p>
<h2 id="heading-lets-get-coding">Let’s Get Coding</h2>
<p>When creating a Ueberauth package, the project needs to be structured in a particular way. If you click on any of the listed strategies Ueberauth has you will get a lot of good examples of what you need to do. You can structure your project any way you like but in the case of Ueberauth it’s best we follow their expected structure.</p>
<p><a target="_blank" href="https://github.com/ueberauth/ueberauth/wiki/List-of-Strategies">https://github.com/ueberauth/ueberauth/wiki/List-of-Strategies</a></p>
<p>Inside of <code>lib</code>, we need to create several files and folders.</p>
<pre><code class="lang-bash">mkdir lib/ueberauth
mkdir lib/ueberauth/strategy
touch lib/ueberauth/strategy/patreon.ex
mkdir lib/ueberauth/strategy/patreon
touch lib/ueberauth/strategy/patreon/oauth.ex
</code></pre>
<p>Since we're making an Ueberauth strategy we need to make sure to install Ueberauth and OAuth2 packages. The <code>mix.exs</code> file should start looking like this now.</p>
<pre><code class="lang-elixir"><span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">deps</span></span> <span class="hljs-keyword">do</span>
    [
      {<span class="hljs-symbol">:ueberauth</span>, <span class="hljs-string">"~&gt; 0.7"</span>},
      {<span class="hljs-symbol">:oauth2</span>, <span class="hljs-string">"~&gt; 2.0"</span>},
      {<span class="hljs-symbol">:ex_doc</span>, <span class="hljs-string">"~&gt; 0.27"</span>, <span class="hljs-symbol">only:</span> <span class="hljs-symbol">:dev</span>, <span class="hljs-symbol">runtime:</span> <span class="hljs-keyword">false</span>}
    ]
<span class="hljs-keyword">end</span>
</code></pre>
<p>Now let’s install those new dependencies.</p>
<pre><code class="lang-bash">mix deps.get
</code></pre>
<p>Now it's time to implement what we want our package to do. I am not going to explain how to implement an Uberauth package in this article, you can read my tutorial <a target="_blank" href="https://dev.to/talk2megooseman/creating-your-own-ueberauth-strategy-3351"><strong>Creating Your Own Ueberauth Strategy</strong></a> on how. Instead, we'll just be copying and pasting some code in.</p>
<p>Inside <code>lib/ueberauth/strategy/patreon.ex</code> add the following:</p>
<pre><code class="lang-elixir"><span class="hljs-class"><span class="hljs-keyword">defmodule</span> <span class="hljs-title">Ueberauth.Strategy.Patreon</span></span> <span class="hljs-keyword">do</span>
  <span class="hljs-keyword">use</span> Ueberauth.Strategy,
    <span class="hljs-symbol">oauth2_module:</span> Ueberauth.Strategy.Patreon.OAuth

  <span class="hljs-keyword">alias</span> Ueberauth.Auth.Info
  <span class="hljs-keyword">alias</span> Ueberauth.Auth.Credentials
  <span class="hljs-keyword">alias</span> Ueberauth.Auth.Extra

  <span class="hljs-variable">@doc</span> <span class="hljs-string">"""
  Handles the initial redirect to the patreon authentication page.

  To customize the scope (permissions) that are requested by patreon include
  them as part of your url:

      "https://www.patreon.com/oauth2/authorize"
  """</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">handle_request!</span></span>(conn) <span class="hljs-keyword">do</span>
    scopes = conn.params[<span class="hljs-string">"scope"</span>] || option(conn, <span class="hljs-symbol">:default_scope</span>)

    params =
      [<span class="hljs-symbol">scope:</span> scopes]
      |&gt; with_state_param(conn)

    <span class="hljs-keyword">module</span> = option(conn, <span class="hljs-symbol">:oauth2_module</span>)
    redirect!(conn, apply(<span class="hljs-keyword">module</span>, <span class="hljs-symbol">:authorize_url!</span>, [params]))
  <span class="hljs-keyword">end</span>

  <span class="hljs-variable">@doc</span> <span class="hljs-string">"""
  Handles the callback from Patreon.

  When there is a failure from Patreon the failure is included in the
  `ueberauth_failure` struct. Otherwise the information returned from Patreon is
  returned in the `Ueberauth.Auth` struct.
  """</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">handle_callback!</span></span>(%Plug.Conn{<span class="hljs-symbol">params:</span> %{<span class="hljs-string">"code"</span> =&gt; code}} = conn) <span class="hljs-keyword">do</span>
    <span class="hljs-keyword">module</span> = option(conn, <span class="hljs-symbol">:oauth2_module</span>)
    token = apply(<span class="hljs-keyword">module</span>, <span class="hljs-symbol">:get_token!</span>, [[<span class="hljs-symbol">code:</span> code]])

    if token.access_token == <span class="hljs-keyword">nil</span> <span class="hljs-keyword">do</span>
      set_errors!(conn, [
        error(token.other_params[<span class="hljs-string">"error"</span>], token.other_params[<span class="hljs-string">"error_description"</span>])
      ])
    else
      fetch_user(conn, token)
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>

  <span class="hljs-variable">@doc</span> <span class="hljs-keyword">false</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">handle_callback!</span></span>(conn) <span class="hljs-keyword">do</span>
    set_errors!(conn, [error(<span class="hljs-string">"missing_code"</span>, <span class="hljs-string">"No code received"</span>)])
  <span class="hljs-keyword">end</span>

  <span class="hljs-variable">@doc</span> <span class="hljs-string">"""
  Cleans up the private area of the connection used for passing the raw Notion
  response around during the callback.
  """</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">handle_cleanup!</span></span>(conn) <span class="hljs-keyword">do</span>
    conn
    |&gt; put_private(<span class="hljs-symbol">:patreon_token</span>, <span class="hljs-keyword">nil</span>)
    |&gt; put_private(<span class="hljs-symbol">:patreon_user</span>, <span class="hljs-keyword">nil</span>)
  <span class="hljs-keyword">end</span>

  <span class="hljs-variable">@doc</span> <span class="hljs-string">"""
  Fetches the uid field from the Twitch response. This defaults to the option `uid_field` which in-turn defaults to `id`
  """</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">uid</span></span>(conn) <span class="hljs-keyword">do</span>
    %{<span class="hljs-string">"data"</span> =&gt; user} = conn.private.patreon_user
    user[<span class="hljs-string">"id"</span>]
  <span class="hljs-keyword">end</span>

  <span class="hljs-variable">@doc</span> <span class="hljs-string">"""
  Includes the credentials from the Patreon response.
  """</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">credentials</span></span>(conn) <span class="hljs-keyword">do</span>
    token = conn.private.patreon_token

    %Credentials{
      <span class="hljs-symbol">token:</span> token.access_token,
      <span class="hljs-symbol">token_type:</span> token.token_type,
      <span class="hljs-symbol">refresh_token:</span> token.refresh_token,
      <span class="hljs-symbol">expires_at:</span> token.expires_at
    }
  <span class="hljs-keyword">end</span>

  <span class="hljs-variable">@doc</span> <span class="hljs-string">"""
  Fetches the fields to populate the info section of the `Ueberauth.Auth`
  struct.
  """</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">info</span></span>(conn) <span class="hljs-keyword">do</span>
    %{ <span class="hljs-string">"data"</span> =&gt; %{
      <span class="hljs-string">"attributes"</span> =&gt; %{
        <span class="hljs-string">"full_name"</span> =&gt; full_name,
        <span class="hljs-string">"first_name"</span> =&gt; first_name,
        <span class="hljs-string">"last_name"</span> =&gt; last_name,
        <span class="hljs-string">"about"</span> =&gt; about,
        <span class="hljs-string">"image_url"</span> =&gt; image_url,
        <span class="hljs-string">"url"</span> =&gt; url,
        <span class="hljs-string">"email"</span> =&gt; email
      }
    }} = conn.private.patreon_user

    %Info{
      <span class="hljs-symbol">email:</span> email,
      <span class="hljs-symbol">name:</span> full_name,
      <span class="hljs-symbol">first_name:</span> first_name,
      <span class="hljs-symbol">last_name:</span> last_name,
      <span class="hljs-symbol">description:</span> about,
      <span class="hljs-symbol">image:</span> image_url,
      <span class="hljs-symbol">urls:</span> %{
        <span class="hljs-symbol">profile:</span> url
      }
    }
  <span class="hljs-keyword">end</span>

  <span class="hljs-variable">@doc</span> <span class="hljs-string">"""
  Stores the raw information (including the token) obtained from the Patreon
  callback.
  """</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">extra</span></span>(conn) <span class="hljs-keyword">do</span>
    %Extra{
      <span class="hljs-symbol">raw_info:</span> conn.private.patreon_user
    }
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">fetch_user</span></span>(conn, token) <span class="hljs-keyword">do</span>
    conn = put_private(conn, <span class="hljs-symbol">:patreon_token</span>, token)

    <span class="hljs-keyword">case</span> Ueberauth.Strategy.Patreon.OAuth.get(
           token.access_token,
           <span class="hljs-string">"https://www.patreon.com/api/oauth2/v2/identity?fields%5Buser%5D=full_name,email,first_name,last_name,about,image_url,url"</span>
         ) <span class="hljs-keyword">do</span>
      {<span class="hljs-symbol">:ok</span>, %OAuth2.Response{<span class="hljs-symbol">status_code:</span> <span class="hljs-number">401</span>, <span class="hljs-symbol">body:</span> _body}} -&gt;
        set_errors!(conn, [error(<span class="hljs-string">"token"</span>, <span class="hljs-string">"unauthorized"</span>)])

      {<span class="hljs-symbol">:ok</span>, %OAuth2.Response{<span class="hljs-symbol">status_code:</span> status_code, <span class="hljs-symbol">body:</span> user}}
      <span class="hljs-keyword">when</span> status_code <span class="hljs-keyword">in</span> <span class="hljs-number">200</span>..<span class="hljs-number">399</span> -&gt;
        put_private(conn, <span class="hljs-symbol">:patreon_user</span>, user)

      {<span class="hljs-symbol">:error</span>, %OAuth2.Error{<span class="hljs-symbol">reason:</span> reason}} -&gt;
        set_errors!(conn, [error(<span class="hljs-string">"OAuth2"</span>, reason)])

      {<span class="hljs-symbol">:error</span>, %OAuth2.Response{<span class="hljs-symbol">body:</span> %{<span class="hljs-string">"message"</span> =&gt; reason}}} -&gt;
        set_errors!(conn, [error(<span class="hljs-string">"OAuth2"</span>, reason)])

      {<span class="hljs-symbol">:error</span>, _} -&gt;
        set_errors!(conn, [error(<span class="hljs-string">"OAuth2"</span>, <span class="hljs-string">"uknown error"</span>)])
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">option</span></span>(conn, key) <span class="hljs-keyword">do</span>
    Keyword.get(options(conn), key, Keyword.get(default_options(), key))
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>Inside <code>lib/ueberauth/strategy/patreon/oauth.ex</code> paste the following code:</p>
<pre><code class="lang-elixir"><span class="hljs-class"><span class="hljs-keyword">defmodule</span> <span class="hljs-title">Ueberauth.Strategy.Patreon.OAuth</span></span> <span class="hljs-keyword">do</span>
  <span class="hljs-variable">@moduledoc</span> <span class="hljs-string">"""
  An implementation of OAuth2 for Patreon.

  To add your `:client_id` and `:client_secret` include these values in your
  configuration:

      config :ueberauth, Ueberauth.Strategy.Patreon.OAuth,
        client_id: System.get_env("PATREON_CLIENT_ID"),
        client_secret: System.get_env("PATREON_CLIENT_SECRET")

  """</span>
  <span class="hljs-keyword">use</span> OAuth2.Strategy

  <span class="hljs-variable">@defaults</span> [
    <span class="hljs-symbol">strategy:</span> __MODULE__,
    <span class="hljs-symbol">site:</span> <span class="hljs-string">"https://www.patreon.com/"</span>,
    <span class="hljs-symbol">authorize_url:</span> <span class="hljs-string">"https://www.patreon.com/oauth2/authorize"</span>,
    <span class="hljs-symbol">token_url:</span> <span class="hljs-string">"https://www.patreon.com/api/oauth2/token"</span>,
    <span class="hljs-symbol">token_method:</span> <span class="hljs-symbol">:post</span>
  ]

  <span class="hljs-variable">@doc</span> <span class="hljs-string">"""
  Construct a client for requests to Patreon.

  Optionally include any OAuth2 options here to be merged with the defaults:

      Ueberauth.Strategy.Patreon.OAuth.client(
        redirect_uri: "http://localhost:4000/auth/patreon/callback"
      )

  This will be setup automatically for you in `Ueberauth.Strategy.Patreon`.

  These options are only useful for usage outside the normal callback phase of
  Ueberauth.
  """</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">client</span></span>(opts \\ []) <span class="hljs-keyword">do</span>
    config =
      <span class="hljs-symbol">:ueberauth</span>
      |&gt; Application.fetch_env!(Ueberauth.Strategy.Patreon.OAuth)
      |&gt; check_credential(<span class="hljs-symbol">:client_id</span>)
      |&gt; check_credential(<span class="hljs-symbol">:client_secret</span>)
      |&gt; check_credential(<span class="hljs-symbol">:redirect_uri</span>)

    client_opts =
      <span class="hljs-variable">@defaults</span>
      |&gt; Keyword.merge(config)
      |&gt; Keyword.merge(opts)

    json_library = Ueberauth.json_library()

    OAuth2.Client.new(client_opts)
    |&gt; OAuth2.Client.put_serializer(<span class="hljs-string">"application/json"</span>, json_library)
    |&gt; OAuth2.Client.put_serializer(<span class="hljs-string">"application/vnd.api+json"</span>, json_library)
  <span class="hljs-keyword">end</span>

  <span class="hljs-variable">@doc</span> <span class="hljs-string">"""
  Provides the authorize url for the request phase of Ueberauth.

  No need to call this usually.
  """</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">authorize_url!</span></span>(params \\ [], opts \\ []) <span class="hljs-keyword">do</span>
    opts
    |&gt; client
    |&gt; OAuth2.Client.authorize_url!(params)
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get</span></span>(token, url, headers \\ [], opts \\ []) <span class="hljs-keyword">do</span>
    client()
    |&gt; put_header(<span class="hljs-string">"authorization"</span>, <span class="hljs-string">"Bearer "</span> &lt;&gt; token)
    |&gt; put_header(<span class="hljs-string">"accept"</span>, <span class="hljs-string">"application/json"</span>)
    |&gt; OAuth2.Client.get(url, headers, opts)
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_token!</span></span>(params \\ [], options \\ []) <span class="hljs-keyword">do</span>
    headers = Keyword.get(options, <span class="hljs-symbol">:headers</span>, [])
    options = Keyword.get(options, <span class="hljs-symbol">:options</span>, [])

    client_options = Keyword.get(options, <span class="hljs-symbol">:client_options</span>, [])

    client = OAuth2.Client.get_token!(client(client_options), params, headers, options)

    client.token
  <span class="hljs-keyword">end</span>

  <span class="hljs-comment"># Strategy Callbacks</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">authorize_url</span></span>(client, params) <span class="hljs-keyword">do</span>
    OAuth2.Strategy.AuthCode.authorize_url(client, params)
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_token</span></span>(client, params, headers) <span class="hljs-keyword">do</span>
    client =
      client
      |&gt; put_param(<span class="hljs-string">"grant_type"</span>, <span class="hljs-string">"authorization_code"</span>)
      |&gt; put_header(<span class="hljs-string">"Accept"</span>, <span class="hljs-string">"application/json"</span>)

    OAuth2.Strategy.AuthCode.get_token(client, params, headers)
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">check_credential</span></span>(config, key) <span class="hljs-keyword">do</span>
    check_config_key_exists(config, key)

    <span class="hljs-keyword">case</span> Keyword.get(config, key) <span class="hljs-keyword">do</span>
      value <span class="hljs-keyword">when</span> is_binary(value) -&gt;
        config

      {<span class="hljs-symbol">:system</span>, env_key} -&gt;
        <span class="hljs-keyword">case</span> System.get_env(env_key) <span class="hljs-keyword">do</span>
          <span class="hljs-keyword">nil</span> -&gt;
            raise <span class="hljs-string">"<span class="hljs-subst">#{inspect(env_key)}</span> missing from environment, expected in config :ueberauth, Ueberauth.Strategy.Patreon"</span>

          value -&gt;
            Keyword.put(config, key, value)
        <span class="hljs-keyword">end</span>
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">check_config_key_exists</span></span>(config, key) <span class="hljs-keyword">when</span> is_list(config) <span class="hljs-keyword">do</span>
    <span class="hljs-keyword">unless</span> Keyword.has_key?(config, key) <span class="hljs-keyword">do</span>
      raise <span class="hljs-string">"<span class="hljs-subst">#{inspect(key)}</span> missing from config :ueberauth, Ueberauth.Strategy.Patreon"</span>
    <span class="hljs-keyword">end</span>

    config
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">check_config_key_exists</span></span>(_, _) <span class="hljs-keyword">do</span>
    raise <span class="hljs-string">"Config :ueberauth, Ueberauth.Strategy.Patreon is not a keyword list, as expected"</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>It’s always a great idea to add doc headers to all your public functions you expect your package users to be using. This will help them immensely in figuring out what each function does and seeing examples of input and output. For this package, a user won't be using it directly but instead following specific setup steps, so our documentation reflects that.</p>
<p>Generate our documentation to see how things look now.</p>
<pre><code class="lang-elixir">mix docs
</code></pre>
<p>And spin up a server to see how things.</p>
<pre><code class="lang-elixir">npx serve docs/
</code></pre>
<h3 id="heading-bonus-add-the-readme">Bonus: Add the README</h3>
<p>This is more of a personal preference than anything else. I'm not sure if you noticed when browsing the Hex Docs for various packages. But sometimes you will come across a project that has pretty nice docs but the setup steps will be missing. If you actually visit the packages repository, you will find a great README with all the information on how to setup up the project.</p>
<p>GAHHHH I also need those steps in those docs! Well, you easily can tell <code>ex_doc</code> to include the README. Let’s update our project settings to do just that.</p>
<p>In our <code>mix.exs</code> file add the <code>docs</code> field, it should look something like this now.</p>
<pre><code class="lang-elixir"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">project</span></span> <span class="hljs-keyword">do</span>
    [
      <span class="hljs-symbol">app:</span> <span class="hljs-symbol">:ueberauth_patreon</span>,
      <span class="hljs-symbol">description:</span> <span class="hljs-string">"Ueberauth strategy for Patreon OAuth."</span>,
      <span class="hljs-symbol">version:</span> <span class="hljs-string">"1.0.0"</span>,
      <span class="hljs-symbol">elixir:</span> <span class="hljs-string">"~&gt; 1.13"</span>,
      <span class="hljs-symbol">source_url:</span> <span class="hljs-string">"https://github.com/talk2MeGooseman/ueberauth_patreon"</span>,
      <span class="hljs-symbol">homepage_url:</span> <span class="hljs-string">"https://github.com/talk2MeGooseman/ueberauth_patreon"</span>,
      <span class="hljs-symbol">start_permanent:</span> Mix.env() == <span class="hljs-symbol">:prod</span>,
      <span class="hljs-symbol">deps:</span> deps(),
      <span class="hljs-symbol">package:</span> [
        <span class="hljs-symbol">links:</span> %{<span class="hljs-string">"GitHub"</span> =&gt; <span class="hljs-string">"https://github.com/talk2MeGooseman/ueberauth_patreon"</span>},
        <span class="hljs-symbol">licenses:</span> [<span class="hljs-string">"MIT"</span>],
      ],
      <span class="hljs-symbol">docs:</span> [ <span class="hljs-comment">#### New docs field</span>
        <span class="hljs-symbol">extras:</span> [<span class="hljs-string">"README.md"</span>] <span class="hljs-comment">### This is going to include the readme in the docs</span>
      ]
    ]
  <span class="hljs-keyword">end</span>
</code></pre>
<p>Now let’s go to our README and update it with the project setup steps.</p>
<pre><code class="lang-elixir"><span class="hljs-comment"># Überauth Patreon</span>

[![Hex Version](<span class="hljs-symbol">https:</span>/<span class="hljs-regexp">/cdn.hashnode.com/res</span><span class="hljs-regexp">/hashnode/image</span><span class="hljs-regexp">/upload/v</span>1679633621626/fe2dc713<span class="hljs-number">-2811-4631</span><span class="hljs-number">-8d98</span><span class="hljs-number">-6</span>abd29a2d38b.svg)](<span class="hljs-symbol">https:</span>/<span class="hljs-regexp">/hex.pm/packages</span><span class="hljs-regexp">/ueberauth_patreon)

&gt; Patreon OAuth2 strategy for Überauth.

## Installation

1. Setup your application in Patreon Development Dashboard https:/</span><span class="hljs-regexp">/www.patreon.com/portal</span><span class="hljs-regexp">/registration/register</span>-clients

<span class="hljs-number">1</span>. Add `<span class="hljs-symbol">:ueberauth_patreon`</span> to your list of dependencies <span class="hljs-keyword">in</span> `mix.exs`:

    ```elixir
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">deps</span></span> <span class="hljs-keyword">do</span>
      [{<span class="hljs-symbol">:ueberauth_patreon</span>, <span class="hljs-string">"~&gt; 1.0"</span>}]
    <span class="hljs-keyword">end</span>
</code></pre>
<ol>
<li><p>Add Patreon to your Überauth configuration:</p>
<pre><code class="lang-elixir"> config <span class="hljs-symbol">:ueberauth</span>, Ueberauth,
   <span class="hljs-symbol">providers:</span> [
     <span class="hljs-symbol">patreon:</span> {Ueberauth.Strategy.Patreon, [<span class="hljs-symbol">default_scope:</span> <span class="hljs-string">"identity[email] identity"</span>]},
   ]
</code></pre>
</li>
<li><p>Update your provider configuration:</p>
<pre><code class="lang-elixir">config <span class="hljs-symbol">:ueberauth</span>, Ueberauth.Strategy.Patreon.OAuth,
  <span class="hljs-symbol">client_id:</span> System.get_env(<span class="hljs-string">"PATREON_CLIENT_ID"</span>),
  <span class="hljs-symbol">client_secret:</span> System.get_env(<span class="hljs-string">"PATREON_CLIENT_SECRET"</span>),
  <span class="hljs-symbol">redirect_uri:</span> System.get_env(<span class="hljs-string">"PATREON_REDIRECT_URI"</span>)
</code></pre>
</li>
<li><p>Include the Überauth plug in your router pipeline:</p>
<pre><code class="lang-elixir"><span class="hljs-class"><span class="hljs-keyword">defmodule</span> <span class="hljs-title">TestPatreonWeb.Router</span></span> <span class="hljs-keyword">do</span>
  <span class="hljs-keyword">use</span> TestPatreonWeb, <span class="hljs-symbol">:router</span>

  pipeline <span class="hljs-symbol">:browser</span> <span class="hljs-keyword">do</span>
    plug Ueberauth
    ...
   <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
</li>
<li><p>Add the request and callback routes:</p>
<pre><code class="lang-elixir">scope <span class="hljs-string">"/auth"</span>, TestPatreonWeb <span class="hljs-keyword">do</span>
  pipe_through <span class="hljs-symbol">:browser</span>

  get <span class="hljs-string">"/:provider"</span>, AuthController, <span class="hljs-symbol">:request</span>
  get <span class="hljs-string">"/:provider/callback"</span>, AuthController, <span class="hljs-symbol">:callback</span>
<span class="hljs-keyword">end</span>
</code></pre>
</li>
<li><p>Create a new controller or use an existing controller that implements callbacks to deal with <code>Ueberauth.Auth</code> and <code>Ueberauth.Failure</code> responses from Patreon.</p>
<pre><code class="lang-elixir">   <span class="hljs-class"><span class="hljs-keyword">defmodule</span> <span class="hljs-title">TestPatreonWeb.AuthController</span></span> <span class="hljs-keyword">do</span>
     <span class="hljs-keyword">use</span> TestPatreonWeb, <span class="hljs-symbol">:controller</span>

     <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">callback</span></span>(%{<span class="hljs-symbol">assigns:</span> %{<span class="hljs-symbol">ueberauth_failure:</span> _fails}} = conn, _params) <span class="hljs-keyword">do</span>
       conn
       |&gt; put_flash(<span class="hljs-symbol">:error</span>, <span class="hljs-string">"Failed to authenticate."</span>)
       |&gt; redirect(<span class="hljs-symbol">to:</span> <span class="hljs-string">"/"</span>)
     <span class="hljs-keyword">end</span>

     <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">callback</span></span>(%{<span class="hljs-symbol">assigns:</span> %{<span class="hljs-symbol">ueberauth_auth:</span> auth}} = conn, _params) <span class="hljs-keyword">do</span>
       <span class="hljs-keyword">case</span> UserFromAuth.find_or_create(auth) <span class="hljs-keyword">do</span>
         {<span class="hljs-symbol">:ok</span>, user} -&gt;
           conn
           |&gt; put_flash(<span class="hljs-symbol">:info</span>, <span class="hljs-string">"Successfully authenticated."</span>)
           |&gt; put_session(<span class="hljs-symbol">:current_user</span>, user)
           |&gt; configure_session(<span class="hljs-symbol">renew:</span> <span class="hljs-keyword">true</span>)
           |&gt; redirect(<span class="hljs-symbol">to:</span> <span class="hljs-string">"/"</span>)

         {<span class="hljs-symbol">:error</span>, reason} -&gt;
           conn
           |&gt; put_flash(<span class="hljs-symbol">:error</span>, reason)
           |&gt; redirect(<span class="hljs-symbol">to:</span> <span class="hljs-string">"/"</span>)
       <span class="hljs-keyword">end</span>
     <span class="hljs-keyword">end</span>
   <span class="hljs-keyword">end</span>
</code></pre>
</li>
</ol>
<h2 id="heading-calling">Calling</h2>
<p>Once your setup, you can initiate auth using the following URL, unless you changed the routes from the guide:</p>
<p>    /auth/patreon</p>
<h2 id="heading-documentation">Documentation</h2>
<p>The docs can be found at <a target="_blank" href="https://hexdocs.pm/ueberauth_patreon">ueberauth_patreon</a> on <a target="_blank" href="https://hexdocs.pm">Hex Docs</a>.</p>
<pre><code>
Generate our documentation to see how things look now.

<span class="hljs-string">``</span><span class="hljs-string">`bash
mix docs</span>
</code></pre><p>And spin up a server to see how things.</p>
<pre><code class="lang-bash">npx serve docs/
</code></pre>
<p>Our new package is code complete but we don't know if it actually works.</p>
<h2 id="heading-testing-our-package-in-a-project">Testing Our Package In A Project</h2>
<p>I think I write half-decent code, but not enough to trust that it just works so I want to try testing it in an actual Phoenix project. Let’s spin one up to test it LIVE.</p>
<pre><code class="lang-bash">mix phx.new test_patreon
</code></pre>
<h3 id="heading-short-detour-using-vs-code-run-our-project">Short Detour: Using VS Code Run Our Project</h3>
<p>We will need a database for our project, the easiest way I like to do this is using Visual Studio Code’s <strong>Remote - Containers</strong> feature. Using Docker and an existing template, I can spin up a dev environment that all the fixings I need to work on a Phoenix project.</p>
<ol>
<li><p>Start VS Code, run the <strong>Remote-Containers: Open Folder in Container...</strong> command from the Command Palette (F1) or quick actions Status bar item, and select the project folder you would like to set up the container for.</p>
</li>
<li><p>Now select <strong>Show All Definitions...</strong> &gt; <strong>Elixir, Phoenix, Node.js &amp; PostgresSQL (Community)</strong></p>
</li>
<li><p>After picking the starting point for your container, VS Code will add the dev container configuration files to your project (<code>.devcontainer/devcontainer.json</code>).</p>
</li>
<li><p>The VS Code window will reload and start building the dev container. A progress notification provides status updates. You only have to build a dev container the first time you open it; opening the folder after the first successful build will be much quicker.</p>
</li>
<li><p>After the build completes, VS Code will automatically connect to the container.</p>
</li>
</ol>
<p>Now your project is all set up with everything you need to run the project. You will most likely need to go into the <code>docker-compose.yml</code> file and update the database name to the name your project will be using.</p>
<h2 id="heading-resuming-testing-our-package-in-a-project">Resuming: Testing Our Package In A Project</h2>
<p>Let make sure our project setup works.</p>
<pre><code class="lang-bash">mix setup
</code></pre>
<p>In order to test our project, we don’t need to worry about publishing it to Hex. We can install it locally or install it through git. I have already pushed my project to Github so I want to show you how you can install any package hosted on Git or Github really easily.</p>
<p>Add the following to your deps. Your name or URL would be different if your project.</p>
<pre><code class="lang-elixir">{<span class="hljs-symbol">:ueberauth_patreon</span>, <span class="hljs-symbol">github:</span> <span class="hljs-string">"talk2MeGooseman/ueberauth_patreon"</span>}
</code></pre>
<p>As a personal rule of thumb, make sure to go through the setup guide you have for your package VERBATIM. This ensures you didn't miss any steps as part of the setup guide, because were the author sometimes we can accidentally gloss over an important.</p>
<p>Now install your package and test it out.</p>
<pre><code class="lang-bash">mix deps.get
</code></pre>
<p>Here is to hoping your project just works during testing it!</p>
<h2 id="heading-uh-oh-my-package-has-a-bug">UH OH, My Package Has A Bug!</h2>
<p>When the inevitable and your find a bug in your code there are a couple of things you can do.</p>
<ol>
<li>Make updates to your package project code and push the changes to Git. If you do this, in your test project make sure to run the following command to install the latest updates.</li>
</ol>
<pre><code class="lang-bash">mix deps.update ueberauth_patreon
</code></pre>
<ol>
<li>Modify the package code directly inside your test project. All your installed packages can be found inside your <code>deps/</code> folder and any changes you make to them can be recompiled so you can see how they affect your project. This can save you the trouble of having to go back to your package project, make code changes, and then push the code. Just run the following command to recompile your package.</li>
</ol>
<pre><code class="lang-bash">mix deps.compile ueberauth_patreon
</code></pre>
<p><strong>Make sure you copy the fixes you make back to your package project and commit them!</strong></p>
<h2 id="heading-submitting-the-package">Submitting The Package</h2>
<p>Now a package is complete and is ready to publish. Hex again does a great job detailing the steps you need to go through here <a target="_blank" href="https://hex.pm/docs/publish#submitting-the-package">https://hex.pm/docs/publish#submitting-the-package</a></p>
<p>Just run the command and confirm when you verify everything looks good.</p>
<pre><code class="lang-bash">&gt; mix hex.publish
Building ueberauth_patreon 1.0.0
  Dependencies:
    ueberauth ~&gt; 0.7 (app: ueberauth)
    oauth2 ~&gt; 2.0 (app: oauth2)
  App: ueberauth_patreon
  Name: ueberauth_patreon
  Files:
    lib
    lib/ueberauth
    lib/ueberauth/strategy
    lib/ueberauth/strategy/patreon
    lib/ueberauth/strategy/patreon/oauth.ex
    lib/ueberauth/strategy/patreon.ex
    lib/ueberauth_patreon.ex
    .formatter.exs
    mix.exs
    README.md
  Version: 1.0.0
  Build tools: mix
  Description: Ueberauth strategy <span class="hljs-keyword">for</span> Patreon OAuth.
  Licenses: MIT
  Links: 
    GitHub: https://github.com/talk2MeGooseman/ueberauth_patreon
  Elixir: ~&gt; 1.13
Before publishing, please <span class="hljs-built_in">read</span> the Code of Conduct: https://hex.pm/policies/codeofconduct

Publishing package to public repository hexpm.
</code></pre>
<h2 id="heading-oh-no-i-messed-up-my-docs">Oh No I Messed Up My Docs!</h2>
<p>Free not, for Hex has a way to directly publish doc updates. No need to make a new release just to update docs. Once you have made your docs updates just run the following command and you are good.</p>
<pre><code class="lang-bash">mix hex.publish docs
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Playing with GitHub Co-Pilot]]></title><description><![CDATA[{% youtube I41wc24XVlA %}
This video is an abbreviated version of my Twitch live stream where we were playing with Github Co-Pilot.
Check out Github Co-Pilot at https://copilot.github.com
Give me a follow on Twitch to watch me live: https://twitch.tv...]]></description><link>https://blog.guzman.codes/playing-with-github-co-pilot</link><guid isPermaLink="true">https://blog.guzman.codes/playing-with-github-co-pilot</guid><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Sun, 04 Jul 2021 16:45:36 GMT</pubDate><content:encoded><![CDATA[<p>{% youtube I41wc24XVlA %}</p>
<p>This video is an abbreviated version of my Twitch live stream where we were playing with Github Co-Pilot.</p>
<p>Check out Github Co-Pilot at <a target="_blank" href="https://copilot.github.com/?WT.mc_id=AZ-MVP-5003399">https://copilot.github.com</a></p>
<p>Give me a follow on Twitch to watch me live: https://twitch.tv/talk2megooseman</p>
]]></content:encoded></item><item><title><![CDATA[GitHub Codespaces: Managing Environment Variables]]></title><description><![CDATA[This is a video about using GitHub Codespaces and having your Environment Variables included each time you launch your Codespace for easier development.
{% youtube G9BXeZcjy5E %}]]></description><link>https://blog.guzman.codes/github-codespaces-managing-environment-variables</link><guid isPermaLink="true">https://blog.guzman.codes/github-codespaces-managing-environment-variables</guid><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Wed, 23 Jun 2021 06:33:58 GMT</pubDate><content:encoded><![CDATA[<p>This is a video about using GitHub Codespaces and having your Environment Variables included each time you launch your Codespace for easier development.</p>
<p>{% youtube G9BXeZcjy5E %}</p>
]]></content:encoded></item><item><title><![CDATA[Deploying Elixir: Package Problems]]></title><description><![CDATA[{% youtube HBncLmsogdA %}
Transcript of Video Below:
How's it going, folks. And thanks for checking out my video on lessons learned deploying elixir. So in this video, I want to talk about a situation that occurred shortly after I deployed my first e...]]></description><link>https://blog.guzman.codes/deploying-elixir-package-problems</link><guid isPermaLink="true">https://blog.guzman.codes/deploying-elixir-package-problems</guid><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Mon, 14 Jun 2021 21:27:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1679633629833/5bf541f4-dc24-4a85-9ba2-6e4cda06de03.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>{% youtube HBncLmsogdA %}</p>
<p>Transcript of Video Below:</p>
<p>How's it going, folks. And thanks for checking out my video on lessons learned deploying elixir. So in this video, I want to talk about a situation that occurred shortly after I deployed my first elixir Phoenix application. So the site  I deployed is called stream closed captioner. And it is a solution to add closed captions to a Twitch stream, to zoom meeting,  OBS via OBS web sockets. This Phoenix application relies on web sockets to send messages from the client over to the backend, and then that gets sent over to Twitch.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679633629833/5bf541f4-dc24-4a85-9ba2-6e4cda06de03.png" alt="Screenshot of stream closed captioner" /></p>
<p>It is a fairly straightforward pipeline, nothing too sophisticated. There isn't really much in the way of CPU-intensive calculations or anything like that. All it does is a query for user information regarding what it should do in terms of authentication, user settings, additional filtering to happen for the text to speech.</p>
<p>So I want to talk about a situation that happened shortly after deployment for this elixir application. For my hosting solution, I am using Gigalixir, and Gigalixir comes with a very nice and simple interface to see memory usage and CPU cores.  This is the current implementation I have, and it's fairly straightforward. It is just two replicas and using one gigabyte of memory.  After the deployment, I didn't see a very consistent graph. Instead, I saw something like this. So this is a screenshot of what was happening roughly two weeks ago; right after deployment, I had deployed the application at 11:00 PM, and I saw huge spikes in CPU usage.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679633631267/11ae759a-d3e0-4524-a926-bef09ee59f1c.jpeg" alt="Gigalixir Process graphs" /></p>
<p>This is at 11:00 PM, so there's not a lot of traffic. Again, there's not anything computationally heavy with the application. And I also saw huge spikes in memory. So much so that I saw out-of-memory errors, and that's not good. And Elixir is supposed to be fast, memory efficient.</p>
<p>This is my first elixir application. So I was like, what the heck is going on?   This is fine, it's running, but it's not really fine. I'm in the middle of a fire. Things are running, but  I'm hitting out of memory errors.  I don't know what's going on. Locally, my Elixir application runs fine. It doesn't look like there's anything crazy going on, but there's obviously something very wrong. Something that doesn't fit with exactly what a Phoenix Elixir application is supposed to be. I'm not doing anything. Memory, CPU intensive. I'm not doing anything crazy with it. It is just a straightforward web socket application that's sending messages and posting API requests, nothing crazy. So I needed to dig in and figure out what the heck is going on because code-wise, everything looked straightforward. Luckily, I had instrumented my application to use new Relic.</p>
<p>So here's the new Relic dashboard, and New Relic is awesome because it provides a lot of great information about your application, the web requests, the throughput. One of the really awesome things about it also has, for elixir applications, a beam profiler so it can connect to the beam process and give you information about what is going on in the application. So I went into here, and I checked things out. I specifically looked at the processes, and nothing out of the ordinary was happening. So I went to the memory tab. So right now, this is the current application running in production.</p>
<p>As of today, today is June 6th. This is an example of fairly consistent memory usage for the application. The table sizes, kind of increasing, decreasing as traffic happens and as garbage collection occurs, but there is a pretty consistent, memory usage across the board. Now at the time where I was facing, out of memory errors, CPU spikes.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679633632561/a52bca7a-524e-475e-9b5d-e10a053236b9.png" alt="New Relic total memory graph" /></p>
<p>This is what I was saying. Unfortunately, with new Relic, the timeline to go back and look at memory and processes doesn't go back too far. It only goes about two weeks, but luckily I took some screenshots of what I saw, and I saw huge memory spikes.  Ever since the deployment,  you could see like here there are memory spikes here. There's a huge memory spike here, and it just wasn't making any sense what the heck is going on out of the total memory. So I kept on digging in, and I took a look at the process memory, and you can see what the process memory I was. I saw spikes again, huge, huge memory spikes there. But the cool thing about the process memory is that these lines represent the processes running in elixir, and everything's a process in elixir.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679633634386/67bc1b24-50ff-4ac1-b19b-ddc01e6a2553.png" alt="new relix process memory graph" /></p>
<p>So I could see right here that boom notifier. This process is the thing that seems to be taking up lots of memory.  It's always taking up memory. Regardless of whether I restarted the application, redeployed any changes it's, it's taking up memory. It's always spiking up pretty high. And I was like, Hmm.</p>
<p>Okay. Well, I know what boom notifier is, it's a notification package that I wanted to use to get emailed exceptions that happen in the application because it's a similar plugin that I was using in a Ruby on rails application  I had been using. So I just wanted to receive emails whenever an exception happens, so I can quickly track those.</p>
<p>Here's the package itself, boom, notifier. And I followed all the guides. It was fairly straightforward to use and configure. But as we saw on this chart right here, it looks like boom notifier is showing some memory peaks. So okay, well, I don't need boom notifier because new Relic offers error tracking in case there are 500 errors, exceptions being thrown.</p>
<p>So I don't need boom notifier. So let me try and get rid of boom notifier. And see what happens.  I had taken it out of the mix.exs, committed the changes, and deployed them. And so what happened. Now, if we go back to this graph over here and we see these spikes,  we see a huge drop off right here at the end, this right here is after boom notifier was removed and the application was deployed. So I'm not trying to knock boom notifier or anything.  It's a solid plugin for your application, but in my situation, it was causing some huge CPU and memory usage problems. And you can see right here that after I removed it, it went down significantly.</p>
<p>And now I was below 1.0 CPU usage. And also memory dropped down to a very, very consistent level.</p>
<p>Which was awesome. So after removing this package, I saw a significant gain and performance. And if we go back here, we had the spikes here and after. The deployment, removing boom notifier. You see a very consistent level of memory here and the same thing over here, the process memory drops down by a lot.</p>
<p>So what is the lesson that I learned? Right out of the gate with Elixir and packages. It's to be careful, be very careful of what you put into your application because if you don't need it, don't use it.</p>
<p>So after that, now I have a very consistent graph of memory usage across the board and my application no longer runs out of memory and no longer has drastic CPU spikes. It is very consistent, very efficient, and it's very much what an elixir application should be.  If there's anything I want you to take away from this video, it's that just be careful with your packages and use the tools necessary to keep track of your application, to see what the performance is, what the memory usage is. So you can identify things that could be hindrances, could be bottlenecks for your application in production. I got lucky that I had set up New Relic ahead of time.</p>
<p>I know there's another tooling that you can use for elixir, but New Relic was the easiest thing for me since I am fairly new to Elixir itself and running it in production.</p>
<p>So hopefully you learn something new from this, and I will see you in the next video. Thanks, everyone.</p>
]]></content:encoded></item><item><title><![CDATA[TIL: Bundle install with a specific version of Bundler]]></title><description><![CDATA[The challenge with working on multiple Ruby on Rails projects is coming across issues conflicting dependencies between projects. One issue I have been having has been with the version of bundler. One project I am working on depends on bundler v2 and ...]]></description><link>https://blog.guzman.codes/til-bundle-install-with-a-specific-version-of-bundler</link><guid isPermaLink="true">https://blog.guzman.codes/til-bundle-install-with-a-specific-version-of-bundler</guid><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Mon, 26 Oct 2020 18:12:51 GMT</pubDate><content:encoded><![CDATA[<p>The challenge with working on multiple Ruby on Rails projects is coming across issues conflicting dependencies between projects. One issue I have been having has been with the version of <code>bundler</code>. One project I am working on depends on <code>bundler v2</code> and another depends on <code>bundler v1</code> running on the same Ruby version.</p>
<p>Upgrading <code>bundler</code> version for the project using <code>bundler v1</code> is not an option right now and trying to do any <code>bundle installs</code> can get very annoying since it will be using the wrong version of <code>bundler</code> for the <code>Gemlock</code>.</p>
<p>After doing some web searching for a potential solution I found out about this funky annotation for telling <code>bundle</code> what version of the <code>bundler</code> gem you would like to use:  </p>
<pre><code class="lang-bash">bundle _1.13_ install
</code></pre>
<p>It's a weird argument but it works. So next time you have a different major <code>bundler</code> versions across projects try using this.</p>
]]></content:encoded></item><item><title><![CDATA[The Importance Of The .Dockerfile File]]></title><description><![CDATA[Cross Post From Coding Zeal Blog
For a while now, I have been using Docker for local development on my Ruby on Rails applications. This has worked out great and I've spent plenty of time tweaking Docker to suit my needs, as things have changed. I rec...]]></description><link>https://blog.guzman.codes/the-importance-of-the-dockerfile-file</link><guid isPermaLink="true">https://blog.guzman.codes/the-importance-of-the-dockerfile-file</guid><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Thu, 28 May 2020 21:06:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1679633640829/123570a2-b5bf-43f2-a0b3-479516df3804.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h4 id="heading-cross-post-from-coding-zeal-bloghttpswwwcodingzealcompostthe-importance-of-the-dockerignore-file"><a target="_blank" href="https://www.codingzeal.com/post/the-importance-of-the-dockerignore-file">Cross Post From Coding Zeal Blog</a></h4>
<p>For a while now, I have been using Docker for local development on my Ruby on Rails applications. This has worked out great and I've spent plenty of time tweaking Docker to suit my needs, as things have changed. I recently started a new initiative to use Docker in a production environment, not just for local development. Unfortunately, I hit a little hiccup.</p>
<h2 id="heading-setting-up-docker-the-problem">Setting Up Docker: The Problem</h2>
<p>If you are like me, the first time you configured your <code>Dockerfile</code> for Ruby on Rails, you found a guide to set up your <code>Dockerfile</code>. It was great for setting up a local development environment. It probably had you set up a <code>Dockerfile</code>, create a <code>docker-compose.yml file</code>, and set up a build and run process.
Here is a very abbreviated example of my Dockerfile</p>
<pre><code class="lang-bash">FROM ruby:2.5.3
<span class="hljs-comment">###############################################################################</span>
<span class="hljs-comment"># Base Software Install</span>
<span class="hljs-comment">###############################################################################</span>
RUN curl -sL https://deb.nodesource.com/setup_<span class="hljs-variable">$RAILSDOCK_NODE_VERSION</span>.x | bash -
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - &amp;&amp; \
 <span class="hljs-built_in">echo</span> <span class="hljs-string">"deb https://dl.yarnpkg.com/debian/ stable main"</span> | tee /etc/apt/sources.list.d/yarn.list
.
.
.
<span class="hljs-comment">###############################################################################</span>
<span class="hljs-comment"># Ruby, Rubygems, and Bundler Defaults</span>
<span class="hljs-comment">###############################################################################</span>
ENV LANG C.UTF-8
.
.
.
ENV RAILS_ENV production
<span class="hljs-comment">###############################################################################</span>
<span class="hljs-comment"># Final Touches</span>
<span class="hljs-comment">###############################################################################</span>
.
.
.
RUN bundle config build.nokogiri --use-system-libraries
RUN bundle check || bundle install
<span class="hljs-comment"># Copy for package.json and yarn.lock to do install to save layer size</span>
COPY package.json yarn.lock ./
RUN yarn install --check-files --production=<span class="hljs-literal">true</span>
<span class="hljs-comment"># Finally copy over rest of app</span>
COPY . /app
<span class="hljs-comment"># Precompile assets for production</span>
RUN bundle <span class="hljs-built_in">exec</span> rake assets:precompile
.
.
.
</code></pre>
<p>If you just do the standard tutorials, everything works great in development. But, most of these tutorials don't cover the .dockerignore file. This file is very important and not having one becomes a problem when Docker runs the following line in the above Dockerfile:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Finally copy over rest of app</span>
COPY . /app
</code></pre>
<h2 id="heading-locking-down-your-docker-image-for-production-or-sharing">Locking Down Your Docker Image For Production or Sharing</h2>
<p>By default, when you run a Docker build and copy your directory into the image, it will grab every single file in your directory: the <code>node_modules</code>, those weird <code>.DS_Store</code> files, and even your <code>.env</code> files that could contain sensitive information. Why is this a problem? If you happen to make your Docker image publicly available after it's built, people can look inside and see those extra files that were included inside. Most Dockerize your Rails app guides don't cover.
The solution is to use a <code>.dockerignore</code> file. It functions exactly like a <code>.gitignore</code> file. Put the files and folder paths that you don't want committed to your Docker image.
Here is an example of my .dockerignore file.</p>
<pre><code class="lang-bash">**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
README.md
.DS_Store
.bin
.git
.gitignore
.bundleignore
.bundle
.byebug_history
.rspec
tmp
<span class="hljs-built_in">log</span>
<span class="hljs-built_in">test</span>
config/deploy
config/master.key
public/packs
public/packs-test
node_modules
yarn-error.log
coverage/
</code></pre>
<p>This keeps a lot of development cruft out of my Docker image. Reviewing it, I could probably update it again 😁. Once you add your <code>.dockerignore</code> file to your project, try to build things again.
Now, your Docker container should be leaner (no more extra files you don't need in there) and more secure!</p>
]]></content:encoded></item><item><title><![CDATA[Azure Functions + GraphQL + Twitch, Making One Endpoint to Rule Them All Part 1]]></title><description><![CDATA[Objective
On May 1st, Twitch made a drastic change to their new API version, "Helix." All API consumers MUST authenticate when using the API, gone are the days of just needing a Client-ID to fetch general data. For my applications to continue working...]]></description><link>https://blog.guzman.codes/azure-functions-graphql-twitch-making-one-endpoint-to-rule-them-all-part-1</link><guid isPermaLink="true">https://blog.guzman.codes/azure-functions-graphql-twitch-making-one-endpoint-to-rule-them-all-part-1</guid><dc:creator><![CDATA[Erik Guzman]]></dc:creator><pubDate>Mon, 25 May 2020 18:04:22 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-objective">Objective</h2>
<p>On May 1st, Twitch made a drastic change to their new API version, "Helix." All API consumers MUST authenticate when using the API, gone are the days of just needing a Client-ID to fetch general data. For my applications to continue working, I wanted to use Azure Functions to authenticate with Twitch and bring the data I needed.</p>
<h2 id="heading-first-attempt">First Attempt</h2>
<p>Now that I knew my objective, it was time to get busy. I find the easiest way to get started with Azure Functions is to use Visual Studio Code with the Azure Function extension. I created a new Azure Functions project and set my new function to be an HTTP trigger.
The first set of data I needed from Twitch is from Get Users endpoint. Instead of reinventing the wheel with fetching data from Twitch, I decided to use the <a target="_blank" href="https://d-fischer.github.io/twitch/docs/basic-usage/getting-started.html">npm Twitch package</a>. It comes with easy with to authenticate with Twitch using the Clients Credentials flow.</p>
<pre><code class="lang-typescript">TwitchClient.withClientCredentials(clientId, clientSecret);
</code></pre>
<p>It also is built to support virtually all the different API endpoints Twitch offers. As I mentioned before, I need to fetch User's information, so the HelixUserAPI is what I need.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> client.helix.users.getUserById(<span class="hljs-string">'125328655'</span>);
</code></pre>
<p>Now that I have the package to make interfacing with Twitch easier, all that's left was to write a function to get a requested user's information, which ends up being very easy to do. It looked something like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> httpTrigger: AzureFunction = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">context: Context, req: HttpRequest</span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">void</span>&gt; </span>{

    <span class="hljs-keyword">const</span> id = req.query.id
    <span class="hljs-keyword">const</span> name = req.query.name
    <span class="hljs-keyword">if</span> (!id &amp;&amp; !name) {
        context.res = {
            status: <span class="hljs-number">400</span>,
            body: <span class="hljs-string">"You must provide and id or name to query by"</span>
        };
    <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">const</span> twitchClient = TwitchClient.withClientCredentials(TwitchCredentials.clientId, TwitchCredentials.clientSecret);
    <span class="hljs-keyword">let</span> user: HelixUser | <span class="hljs-literal">null</span>;
    <span class="hljs-keyword">if</span> (name) {
    user = <span class="hljs-keyword">await</span> twitchClient.helix.users.getUserByName(name);
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (id) {
    user = <span class="hljs-keyword">await</span> twitchClient.helix.users.getUserById(id);
    }

    context.res = {
        body: User
    };
};
</code></pre>
<p>Great! This function does what I needed to do, get me a user's information by ID or name. This solves my first use case, getting a user. Now on to the next one, Get a Users Clips! But right before I just started coding things up, I started to struggle with how to implement this new functionality.</p>
<h2 id="heading-the-problem">The Problem</h2>
<p>Right now, I have an Azure function to Get a User. This works great, but now I want to Get a Users Clips. Should I just implement a new Azure function to fetch this information from Twitch? Or should I add it to the already existing Get a User function I just created?
If I add to my existing Get a User function, the logic will start to get messy and complex with all these weird optional parameters existing.
Adding a new Azure function is simple enough, but thinking about the long term, I will have to add many more functions to fetch any other set of data I want from Twitch. Yuck! Mo' Azure Function mo' problems for maintaining.
Okay, both ways have some of their flaws, and I have to choose one, right? Nope! I realized, why don't I try using GraphQL? I use it at work for a clients project, why not see if I can use it with Azure Functions.</p>
<h2 id="heading-why-graphql">Why GraphQL?</h2>
<p>When stepping away from what I am trying to build, at a high level, I want a flexible API that will fetch me information from Twitch that I want. In the standard RESTful approach, this would mean loads or different API endpoints for the different entities that Twitch has to offer. If you look at any API documentation to will see this, just like Twitch's API. But GraphQL's objective to simplify this.
GraphQL is an alternative to building RESTful APIs. Using a single endpoint, all I have to do is "ask" it for what I need with all the proper parameters, and it will return it back. I am not going to detail GraphQL, there are plenty of other articles that will do a much better job doing a detailed explanation. But, to give a simple explanation. GraphQL is a query language for your API. Think of your API as a database; formulate a query based on the "schema" of your database, and once it is executed, it will return the results. There is a lot of implementation detail I am glossing over, but that's how on think of GraphQL in a nutshell.
Using GraphQL, I can rewrite my current implementation to reduce the complexity of having to expand my API when I need information from Twitch. Now I can just update my single GraphQL implementation and request only the information I need using the query language.</p>
<h2 id="heading-how-to-graphql-on-azure-functions">How to GraphQL on Azure Functions?</h2>
<p>After all of that thinking, I have decided to try to use GraphQL for my Azure Function. Cool! But is it easy? Time to find out and see if GraphQL and be easily integrated with Azure Functions. After a quick search, I find a golden ticket.</p>
<p><a target="_blank" href="https://www.apollographql.com/docs/apollo-server/deployment/azure-functions/">Apollo GraphQL has Azure Function wrapper for their Apollo serve library!</a></p>
<p>The best part about this, the tutorial is super easy to follow. It looks like I will be able to change my implementation to use GraphQL easily enough.</p>
<h2 id="heading-implementing-get-users-with-graphql">Implementing Get Users with GraphQL</h2>
<p>After following along with the apollo-server-azure-functions tutorial, I had the example "Hello World" running and was able to send a query for the response. Next was to build the schema for Get a User from Twitch.</p>
<pre><code class="lang-graphql">  <span class="hljs-keyword">type</span> Query {
    <span class="hljs-symbol">helix:</span> Helix
  }

  <span class="hljs-keyword">type</span> Helix {
    userById(<span class="hljs-symbol">id:</span> ID!): HelixUser
    userByName(<span class="hljs-symbol">name:</span> String!): HelixUser
  }

  <span class="hljs-keyword">type</span> HelixUser {
    <span class="hljs-symbol">id:</span> ID
    <span class="hljs-symbol">broadcasterType:</span> BroadcasterType
    <span class="hljs-symbol">description:</span> String
    <span class="hljs-symbol">displayName:</span> String
    <span class="hljs-symbol">name:</span> String
    <span class="hljs-symbol">profilePictureUrl:</span> String
    <span class="hljs-symbol">offlineImageUrl:</span> String
    <span class="hljs-symbol">views:</span> Int
  }
</code></pre>
<p>As a design choice, I decided to put all "Helix" API endpoints under the helix field. This is so that there are no type collisions in the future if I end up extending my GraphQL implementation to include Twitch's older kind of sort of deprecated API called "Kraken." Next, I added two fields with arguments to get a user by ID or name. The last little bit is defining the HelixUser type and the fields that should exist.
After designing the schema, next came the resolver to fetch the data.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> resolvers = {
  Query: {
    helix() {
      <span class="hljs-keyword">return</span> {
        userById: <span class="hljs-function">(<span class="hljs-params">{ id }: { id: <span class="hljs-built_in">string</span> }</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> twitchClient = TwitchClient.withClientCredentials(TwitchCredentials.clientId, TwitchCredentials.clientSecret);
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> twitchClient.helix.users.getUserById(id);
    },
        userByName: <span class="hljs-function">(<span class="hljs-params">{ name }: { name: <span class="hljs-built_in">string</span> }</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> twitchClient = TwitchClient.withClientCredentials(TwitchCredentials.clientId, TwitchCredentials.clientSecret);
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> twitchClient.helix.users.getUserByName(name);
    }),
      };
    }
  },
};
</code></pre>
<p>Nothing too complex going on here. First I needed to implement the helix field implementation, in this case it just a function return an object with userById and userByName subfields implementations. Now focusing on the subfields, all I have to do is fetch the User information based off the ID or name like in the old implementation and return that. GraphQL will handle the rest of the response fields since it knows what data is returned by the user object from the schema I had created.</p>
<p>The whole thing might look something like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> { ApolloServer, gql } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'apollo-server-azure-functions'</span>);
<span class="hljs-keyword">import</span> TwitchClient <span class="hljs-keyword">from</span> <span class="hljs-string">'twitch'</span>;
<span class="hljs-keyword">import</span> HelixUser <span class="hljs-keyword">from</span> <span class="hljs-string">"twitch/lib/API/Helix/User/HelixUser"</span>;

<span class="hljs-comment">// Construct a schema, using GraphQL schema language</span>
<span class="hljs-keyword">const</span> typeDefs = gql<span class="hljs-string">`
  type Query {
    helix: Helix
  }

  type Helix {
    userById(id: ID!): HelixUser
    userByName(name: String!): HelixUser
  }

  type HelixUser {
    id: ID
    broadcasterType: BroadcasterType
    description: String
    displayName: String
    name: String
    profilePictureUrl: String
    offlineImageUrl: String
    views: Int
  }
`</span>;

<span class="hljs-comment">// Provide resolver functions for your schema fields</span>
<span class="hljs-keyword">const</span> resolvers = {
  Query: {
    helix() {
      <span class="hljs-keyword">return</span> {
        userById: <span class="hljs-function">(<span class="hljs-params">{ id }: { id: <span class="hljs-built_in">string</span> }</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> twitchClient = TwitchClient.withClientCredentials(TwitchCredentials.clientId, TwitchCredentials.clientSecret);
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> twitchClient.helix.users.getUserById(id);
    },
        userByName: <span class="hljs-function">(<span class="hljs-params">{ name }: { name: <span class="hljs-built_in">string</span> }</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> twitchClient = TwitchClient.withClientCredentials(TwitchCredentials.clientId, TwitchCredentials.clientSecret);
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> twitchClient.helix.users.getUserByName(name);
    }),
      };
    }
  },
};

<span class="hljs-keyword">const</span> server = <span class="hljs-keyword">new</span> ApolloServer({ typeDefs, resolvers });

<span class="hljs-built_in">exports</span>.graphqlHandler = server.createHandler();
</code></pre>
<p>Now if I want to get a users ID and display name from Twitch, a request will look something like this.</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">query</span> getUser(<span class="hljs-variable">$name</span>: String!) {
  helix {
    userByName(<span class="hljs-symbol">name:</span> <span class="hljs-variable">$name</span>) {
      id
      displayName
    }
  }
}

<span class="hljs-symbol">Variables:</span>
{
  <span class="hljs-string">"name"</span>: <span class="hljs-string">"talk2megooseman"</span>
}
</code></pre>
<p>With my response being:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"data"</span>: {
    <span class="hljs-attr">"helix"</span>: {
      <span class="hljs-attr">"userByName"</span>: {
        <span class="hljs-attr">"id"</span>: <span class="hljs-string">"120750024"</span>,
        <span class="hljs-attr">"displayName"</span>: <span class="hljs-string">"Talk2meGooseman"</span>,
      }
   }
}
</code></pre>
<p>If I want more information for the user entity, I just need to add the other fields I will like.</p>
<h2 id="heading-next-time">Next Time</h2>
<p>In the next article I will go over how this new implementation with GraphQL will be it super easy to extend the API to include clips of the user. So until next time I hope you found this article a little helpful and don't be afraid to ask questions!</p>
]]></content:encoded></item></channel></rss>