<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Drew Town</title>
    <description>The latest articles on DEV Community by Drew Town (@drewtownchi).</description>
    <link>https://dev.to/drewtownchi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F164639%2Fb9d69904-b0c9-401f-8392-ab934441eb63.jpg</url>
      <title>DEV Community: Drew Town</title>
      <link>https://dev.to/drewtownchi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/drewtownchi"/>
    <language>en</language>
    <item>
      <title>5 Tips For Asking Better Technical Questions And Getting Faster Answers</title>
      <dc:creator>Drew Town</dc:creator>
      <pubDate>Wed, 20 May 2020 16:41:15 +0000</pubDate>
      <link>https://dev.to/drewtownchi/asking-better-technical-questions-pm7</link>
      <guid>https://dev.to/drewtownchi/asking-better-technical-questions-pm7</guid>
      <description>&lt;p&gt;Read more of my articles on &lt;a href="https://www.drewtown.dev"&gt;drewtown.dev&lt;/a&gt; or follow me on &lt;a href="https://twitter.com/drewtown_chi"&gt;Twitter 🐤&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After 12 years of working professionally in systems administration and development I have learned some helpful ways of asking technical or development questions that will get you better answers, faster.&lt;/p&gt;

&lt;p&gt;When asking a question most of the time you are looking for an answer, unless you are waxing philosophically.  So, set yourself and the person you are posing the question up for success. Or, as the venerable Dr. Cox would say, "help me, help you.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Set The Tone
&lt;/h2&gt;

&lt;p&gt;No matter who it is, if it is the first time today you are talking to this person, start with a greeting.  Simply starting your conversation with, "Hey Susan, how are you today?” Always keep in mind that this person will be taking time out of their day to help you with your problem. So, treat them with empathy, professional courtesy, and be friendly out of the gate.&lt;/p&gt;

&lt;p&gt;Opening with your question or hitting them with a wall of text before saying hello is a surefire way to make sure this person won't be starting off on the right foot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Provide Context
&lt;/h2&gt;

&lt;p&gt;Assume the person you are asking your question to has absolutely no context about what you are asking them about. This is by far the most common mistake I see when people are asking questions. &lt;/p&gt;

&lt;p&gt;When you are deep into a problem, you have built up a lot of mental models and context about the current situation.  So, when you ask "How do I fix this?" to a colleague they do not understand that you are working on a problem regarding slow network latency between the app server and the Redis server in the staging environment.&lt;/p&gt;

&lt;p&gt;When you are posing your questions you must help the person you are asking of the question of quickly build up the necessary context to help you.&lt;/p&gt;

&lt;p&gt;Being able to provide this information is a skill that must be practiced.  Before you hit send in your chat app look at your message and ask yourself, if someone sent me this message would I even know what they are talking about? Would I be able to solve this problem with the information I provided?&lt;/p&gt;

&lt;p&gt;Save yourself time by thinking ahead to the information they will need to help you solve the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't Bury The Lede
&lt;/h2&gt;

&lt;p&gt;So far we have discussed setting the tone and providing relevant context, but it is also important to not surround your question with useless details. Don't whine, or try to stir up sympathy by providing a long unnecessary story.&lt;/p&gt;

&lt;p&gt;Your question should only include relevant details, necessary background and the actual question you are seeking an answer to. Don't make the person try to find your question in a wall of text or interpret your story as a question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't Propose Solutions, Tell Them What You Want To Do
&lt;/h2&gt;

&lt;p&gt;When asking a question it is best not to propose a solution as a question.  When you propose a solution to your own question, you are limiting the scope of thought to your solution rather than allowing alternative solutions.&lt;/p&gt;

&lt;p&gt;Instead of saying, “How can I cache my query in the database?” Ask, “I working on a slow query that is causing pages to load slow. How can I speed this up my page?”  Maybe the solution is to better tune database caching. But the conversation might instead flow to storing full page caches in Varnish, or using a CDN, or questioning the query, or studying the indices in the database.&lt;/p&gt;

&lt;p&gt;When you propose a solution as a specific question you limit the potential solutions.  Instead, ask your question as something you are trying to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Show Your Homework
&lt;/h2&gt;

&lt;p&gt;Always take the time to discuss potential solutions you have already tried.  When you do this, you eliminate potential solutions that you've already tried but haven't panned out. Also, you show the person you are asking that you've put a bit of time and effort into the issue.&lt;/p&gt;

&lt;p&gt;You don't need to put an excessive amount of detail into this. But just showing that you tried a solution may eliminate potential solutions or help the other person better direct their thoughts.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>career</category>
      <category>tips</category>
      <category>learning</category>
    </item>
    <item>
      <title>Display External User Avatars Using Strapi's Custom Routes as a Proxy</title>
      <dc:creator>Drew Town</dc:creator>
      <pubDate>Thu, 14 May 2020 16:41:00 +0000</pubDate>
      <link>https://dev.to/drewtownchi/display-external-user-avatars-using-strapi-s-custom-routes-as-a-proxy-2m58</link>
      <guid>https://dev.to/drewtownchi/display-external-user-avatars-using-strapi-s-custom-routes-as-a-proxy-2m58</guid>
      <description>&lt;p&gt;When using Strapi you may run across a scenario where you need to return data not stored in Strapi's database. Suppose you need to fetch a social media user avatar, a stock quote, or other data available from a third party API. We will use Strapi as a proxy to request the data and return the information to the client app, avoiding any potential CORS problems.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While we will be using a user avatar as an example, the implementation presented in this article will also be application to any data that needs to be requested via a third-party API.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When &lt;a href="https://picke.rs/register"&gt;registering&lt;/a&gt; a user on another one of my sites, picke.rs, I wanted to give the user an option to use an avatar they had registered with another service as their picke.rs avatar without requiring the user to find and upload an image from their device. We can use their email address and the &lt;a href="https://unavatar.now.sh/"&gt;unavatar&lt;/a&gt; service to try and locate a social image for the user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z1kylrGr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rotg1bg63yfx3iweijno.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z1kylrGr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rotg1bg63yfx3iweijno.png" alt="Registration form showing email address returns a user avatar for the user to optionally use"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Custom?
&lt;/h2&gt;

&lt;p&gt;You may have noticed the Strapi guide about &lt;a href="https://strapi.io/documentation/3.0.0-beta.x/guides/external-data.html"&gt;fetching external data&lt;/a&gt;. If your data does not need to be real-time or stored in Strapi's database, that will be a better route to follow.&lt;/p&gt;

&lt;p&gt;In this example we want the results to be real-time and requested only when necessary. Therefore, we have no need of a model to store the data in the database and we can simplify the structure presented in Strapi guide.&lt;/p&gt;

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

&lt;p&gt;Create the file &lt;code&gt;api/avatar/config/routes.json&lt;/code&gt; in your Strapi project. You will need to create the &lt;code&gt;avatar&lt;/code&gt; and &lt;code&gt;config&lt;/code&gt; folders within the &lt;code&gt;api&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;routes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;method&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/avatars/:id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;handler&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;avatar.find&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;policies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This route defines the HTTP method, the path, and which method will handle the request.&lt;/p&gt;

&lt;p&gt;The path has a dynamic &lt;code&gt;id&lt;/code&gt; defined which will be passed to the handling method via the &lt;code&gt;ctx&lt;/code&gt;.  We'll use the &lt;code&gt;id&lt;/code&gt; set via the url submitted by the user in order to query the API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handle The Request
&lt;/h2&gt;

&lt;p&gt;Create the file &lt;code&gt;api/avatar/controllers/avatar.js&lt;/code&gt; in your Strapi project.  This is where we will create the &lt;code&gt;find&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * Retrieve a user avatar.
   *
   * @return {String}
   */&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;badRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid email address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://unavatar.now.sh/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?json`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fallback&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In this method we provide some very basic validation to return early if the requested email address does not appear it could be an email address.&lt;/p&gt;

&lt;p&gt;Next, we use Axios (you can use any method or library of your choice to make the API request) to make a request to &lt;a href="https://unavatar.now.sh/"&gt;unavatar&lt;/a&gt; which is a handy little service to query for avatars across multiple social media networks.&lt;/p&gt;

&lt;p&gt;For this case, if unavatar cannot find a social media avatar for the supplied email address, we return null instead of the fallback image.  If you'd prefer to use the unavatar provided fallback, then return the returned data without the fallback check.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ctx&lt;/code&gt; parameter is injected by Strapi and it is what contains information about the request such as the route ID we defined in the &lt;code&gt;routes.json&lt;/code&gt; file&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating The Roles &amp;amp; Permissions
&lt;/h2&gt;

&lt;p&gt;When the route and method have been created and the Strapi server is restarted Strapi will add the new &lt;code&gt;find&lt;/code&gt; method to the available permissions in the admin's Roles &amp;amp; Permissions section.  We'll want to enable this for all the necessary available roles.  If only unauthenticated users will call this method, then only enable it for &lt;code&gt;public&lt;/code&gt; if other roles will call this method then enable it for those roles as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Nf2G9oW4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/s93gu9hpkrra4y6wsp44.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Nf2G9oW4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/s93gu9hpkrra4y6wsp44.png" alt="Updating permissions for the new method"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Calling From The Client
&lt;/h2&gt;

&lt;p&gt;Now in our client we can call the route &lt;code&gt;/avatars/me@example.com&lt;/code&gt; in a Vue application the method would look something similar to the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;    
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;downloadAvatar&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;avatarLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useAvatar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/avatars/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;avatarLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useAvatar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>strapi</category>
      <category>javascript</category>
      <category>node</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Using Strapi Policies To Create Editable User Profiles </title>
      <dc:creator>Drew Town</dc:creator>
      <pubDate>Tue, 05 May 2020 13:18:41 +0000</pubDate>
      <link>https://dev.to/drewtownchi/using-strapi-policies-to-create-editable-user-profiles-16a</link>
      <guid>https://dev.to/drewtownchi/using-strapi-policies-to-create-editable-user-profiles-16a</guid>
      <description>&lt;p&gt;Strapi's &lt;a href="https://strapi.io/documentation/3.0.0-beta.x/plugins/users-permissions.html"&gt;roles &amp;amp; permissions&lt;/a&gt; plugin will get you a long way in registering, sign-in and managing users in your application.  Unfortunately, Strapi does not provide a built-in strategy for allowing users to manage their own personal information via a user profile, instead leaving those decisions to you, the developer.&lt;/p&gt;

&lt;p&gt;We will use Strapi's &lt;a href="https://strapi.io/documentation/3.0.0-beta.x/concepts/policies.html"&gt;policy&lt;/a&gt; customizations to extend the Strapi API. The policy will allow authenticated users to update their user profile in a secure manner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extending The User Content-Type
&lt;/h2&gt;

&lt;p&gt;Display names, subscription status, and user image are a few examples of the information we may want to allow the user to edit. In contrast, we will exclude e-mail address, role and other sensitive fields from user editing.  In this example we will tackle, display name and if they subscribe to the weekly newsletter.&lt;/p&gt;

&lt;p&gt;Using Strapi's "Content-Types Builder" select the &lt;code&gt;User&lt;/code&gt; collection type.  Select "Add another field", choose &lt;code&gt;Text&lt;/code&gt; and give it a name of &lt;code&gt;displayName&lt;/code&gt;. Next, add another field and this time choose &lt;code&gt;Boolean&lt;/code&gt; and give it a name of &lt;code&gt;newsletter&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Rr0iP_dz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/f6idutq961mbje5omam2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Rr0iP_dz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/f6idutq961mbje5omam2.png" alt="Content-Types Builder admin panel with display name of type text and newsletter of type boolean created"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing The Policy
&lt;/h2&gt;

&lt;p&gt;Create the file &lt;code&gt;extensions/users-permissions/config/policies/userUpdate.js&lt;/code&gt; in your Strapi project.  This file is where we will define the policy that Strapi will utilize when it receives a PUT request to the route &lt;code&gt;/users&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// If the user is an administrator we allow them to perform this action unrestricted&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Administrator&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentUserId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// If you are using MongoDB do not parse the id to an int!&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userToUpdate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentUserId&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;userToUpdate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unauthorized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unable to edit this user ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Extract the fields regular users should be able to edit&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newsletter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Provide custom validation policy here&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;displayName&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;badRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Display name is required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Setup the update object&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updateData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;newsletter&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// remove properties from the update object that are undefined (not submitted by the user in the PUT request)&lt;/span&gt;
  &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updateData&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;updateData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;updateData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updateData&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;badRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No data submitted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;updateData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Later on we will dig deeper into what each part of this policy is doing. But for now, let's continue setting up Strapi to use this new policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Permissions in the Strapi Admin
&lt;/h2&gt;

&lt;p&gt;Verify that an &lt;strong&gt;authenticated&lt;/strong&gt; user has access to the &lt;code&gt;me&lt;/code&gt; and &lt;code&gt;update&lt;/code&gt; actions via the roles &amp;amp; permissions plugin in the admin under the user permissions section. When checking the &lt;strong&gt;update&lt;/strong&gt; option select our newly create &lt;code&gt;userUpdate&lt;/code&gt; policy in the advanced settings. By selecting the policy from the policy select dropdown we'll ensure that each request made is checked by the policy before the controller receives the request.&lt;/p&gt;

&lt;p&gt;Selecting these actions will allow a user to to make GET requests to &lt;code&gt;/users/me&lt;/code&gt; and PUT requests to &lt;code&gt;/users&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MT_c6Tbs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zpysnzrf0a3dd49tdbjw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MT_c6Tbs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zpysnzrf0a3dd49tdbjw.png" alt="Strapi Roles &amp;amp; Permission's plugin with me and update methods checked for Authenticated user and user update policy selected in the advanced settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note: Authenticated, in this case, means that we sent the request with an authorization header that includes a valid bearer token returned by the login route.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:1337/users/me&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Retrieving The Current User
&lt;/h2&gt;

&lt;p&gt;The roles &amp;amp; permissions plugin includes a route &lt;code&gt;/users/me&lt;/code&gt; that allows an authenticated user to retrieve information about themselves.  If you are using a front-end with store such as Vuex or Redux, you may already have this information handy in your front-end application.  We will use the data from this route in order to pre-populate our form fields for editing.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Creating and submitting a form on your client application is outside the scope of this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that we've verified we can access information about the authenticated user, we can allow a user to change some information about themselves using a PUT request to the &lt;code&gt;update&lt;/code&gt; route that utilizes our new policy.  Let's take a closer look at what this policy does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Digging Deeper Into The Policy
&lt;/h2&gt;

&lt;p&gt;Let's break this policy into a few chunks to analyze what it is doing.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Parse The Request Data
&lt;/h3&gt;

&lt;p&gt;First, we verify who the user is, whether they are an admin, or they are a regular user trying to edit their own information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// If the user is an administrator we allow them to perform this action unrestricted&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Administrator&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentUserId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// If you are using MongoDB do not parse the id to an int!&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userToUpdate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentUserId&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;userToUpdate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unauthorized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unable to edit this user ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If the user is an administrator, let the request go through as we assume they have all the permissions to perform any action on any user.&lt;/p&gt;

&lt;p&gt;We are using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring"&gt;object destructing&lt;/a&gt; to extract the authenticated user's &lt;code&gt;id&lt;/code&gt; from the Strapi context and the ID parameter from the URL parameters.  The &lt;code&gt;ctx&lt;/code&gt; (context) variable passed into the policy is provide by the Roles &amp;amp; Permissions plugin and will include information about the currently authenticated user such as the &lt;code&gt;id\&lt;/code&gt; we are extracting.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are using MongoDB do not parse the ID to an int.  MongoDB uses a UID user ID pattern.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since we are using the plugin's existing controller, it is expecting a URL parameter for the user we are editing. Meaning a put request will need to go to the route &lt;code&gt;/users/1&lt;/code&gt; or &lt;code&gt;/users/23&lt;/code&gt; depending on the user being updated. Therefore, we need to verify the user is editing their own user information and not another user's information.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extract The Data
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// Extract the fields regular users should be able to edit&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newsletter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next, we extract the &lt;code&gt;displayName&lt;/code&gt; and &lt;code&gt;newsletter&lt;/code&gt; from the request body that the user submitted. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It is important to not blindly accept all parameters from the user as they could maliciously include additional fields such as role and elevate themselves to admin privileges.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Validation
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// Provide custom validation policy here&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;displayName&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;badRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Display name is required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Within the policy is an excellent time to perform any additional validation.  Even though Strapi has some validation built-in, such as string and boolean fields must match their respective types, you may not want to let users to have a display name of "Admin" or a series of spaces for example.  In the policy you can perform your own simple validation or pull in a validation library of your choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Massage The Update Data
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updateData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;newsletter&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// remove properties from the update object that are undefined (not submitted by the user in the PUT request)&lt;/span&gt;
  &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updateData&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;updateData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;updateData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updateData&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;badRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No data submitted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;updateData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We setup the &lt;code&gt;updateData&lt;/code&gt; variable using ES2015 object property shorthand.  Our variable names submitted by the user's request match the names we setup in the Strapi content builder so we can quickly initialize the update object.  If your names don't match, you will need to use the standard object assignment.  &lt;/p&gt;

&lt;p&gt;Filter out any values that are &lt;code&gt;undefined&lt;/code&gt; (not included in the PUT request), and if the user did not submit any valid data, we can short-circuit and return a &lt;code&gt;badRequest&lt;/code&gt; letting the user know.&lt;/p&gt;

&lt;p&gt;Finally, replace the &lt;code&gt;ctx.request.body&lt;/code&gt; with our sanitized &lt;code&gt;updateData&lt;/code&gt; and return &lt;code&gt;next()&lt;/code&gt; to let Strapi know the request has passed the policy test and the controller can proceed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending a request from the client
&lt;/h2&gt;

&lt;p&gt;We've now allowed authenticated users to request data about themselves and send an update request with a policy applied to the request. When you are ready to send a request from the client, you can send an update like the following example using Axios.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:1337/users/1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John Smith&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;newsletter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>strapi</category>
      <category>node</category>
    </item>
    <item>
      <title>Introducing Picke.rs - A Podcast Picks Explorer and Manager</title>
      <dc:creator>Drew Town</dc:creator>
      <pubDate>Fri, 03 Apr 2020 14:36:27 +0000</pubDate>
      <link>https://dev.to/drewtownchi/introducing-picke-rs-a-podcast-picks-explorer-and-manager-8bl</link>
      <guid>https://dev.to/drewtownchi/introducing-picke-rs-a-podcast-picks-explorer-and-manager-8bl</guid>
      <description>&lt;p&gt;&lt;a href="https://picke.rs"&gt;Picke.rs&lt;/a&gt; was born out of an idea I had when listening to a few podcasts. That section at the end where they talk about things they like or “picks.” Often I would hear something that sounded cool; a new podcast, some interesting article or a backpack which I can never seem to find one I like. But I, like I’m sure many others, listen to podcasts mainly on the go.&lt;/p&gt;

&lt;p&gt;I often would forget about what I just heard or even where I heard it.  A place where I can see all the recent picks, picks by podcast, or even the individual who made the pick would help me better track those cool and interesting things they were talking about.&lt;/p&gt;

&lt;p&gt;Thus, &lt;a href="https://picke.rs"&gt;Picke.rs&lt;/a&gt; was born.&lt;/p&gt;

&lt;h2&gt;
  
  
  Back-end
&lt;/h2&gt;

&lt;p&gt;I decided to not write my back-end for this project and instead try one of the many headless CMSs available today. I chose &lt;a href="https://strapi.io"&gt;Strapi&lt;/a&gt; for the task.  While still a young project it is progressing rapidly, has a well thought out admin portal, everything is extensible, and the documentation is thorough and complete.&lt;/p&gt;

&lt;p&gt;Another aspect I liked about Strapi is that it is self hosted.  While this might be a drawback for some. The hosting options are straightforward with one-click deployments for Digital Ocean, Heroku and others. The self-hosted option helps me keep costs down without adding too much of a burden.&lt;/p&gt;

&lt;p&gt;Am I glad I used a pre-built CMS instead of writing my own? You bet! I could have written the same thing in Rails, ASP.NET, Laravel or any other framework, but for what?  Instead of worrying about writing my own models, controllers, authentication and everything else that a flexible CMS gives me for free out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting The Data
&lt;/h2&gt;

&lt;p&gt;Getting the data has been the hardest part of this project.  Some podcasts use JSON to structure their episode data 🎉. Some podcasts use very structured markdown, which is ok.  While still podcasts use very laissez-faire markdown, that changes frequently.&lt;/p&gt;

&lt;p&gt;I’m use a Node.js script that fetches and parses the data that is specific to each podcast.  Because the format of episodes is different, I’ve found it more helpful to build a set of utilities and helpers that can be utilized in to each individual scraper.&lt;/p&gt;

&lt;p&gt;If you have a podcast that you would like featured on the &lt;a href="https://picke.rs/podcasts"&gt;site&lt;/a&gt; help me, help you. Provide clean, consistent JSON or markdown. It should be easily available and include the name of the picker, their Twitter handle if they have one, the name of the pick and a URL.&lt;/p&gt;

&lt;p&gt;If this interests you, get in contact with me, and we’ll chat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Front-end 💻
&lt;/h2&gt;

&lt;p&gt;For the front-end I used &lt;a href="https://nuxtjs.org/"&gt;Nuxt.js&lt;/a&gt;. Before this project, I had always been hesitant to use Nuxt.  A framework sitting on top of a framework with extra conventions always felt redundant to me. I shook off my preconceived notions and tried it for this project.&lt;/p&gt;

&lt;p&gt;So far the experience has been great.  Everything has a place, server-side rendering (SSR) works wonderfully and the module system is very help for getting up and running quickly.&lt;/p&gt;

&lt;p&gt;That’s not to say it isn’t without its quirks.  The error messages can be frustratingly obtuse.  On occasion there is an error that messes up SSR and the error message "Mismatching childNodes vs. VNodes” will leave you crawling through your code trying to figure out what went wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Style System
&lt;/h2&gt;

&lt;p&gt;If you have read any of my previous articles, you know I am a big &lt;a href="https://tailwindcss.com/"&gt;TailwindCSS&lt;/a&gt; fan.  Sensible defaults, built-in style system, perfect for a component based front-end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Images
&lt;/h2&gt;

&lt;p&gt;For each of the picks I am using &lt;a href="https://developers.google.com/web/tools/puppeteer/"&gt;Puppeteer&lt;/a&gt; to capture a screenshot of the page.  In order to more easily process and serve the images in various formats and sizes I am using &lt;a href="https://cloudinary.com/invites/lpov9zyyucivvxsnalc5/mhsl3bf7nlmbmfiaovrs"&gt;Cloudinary&lt;/a&gt; (referral link).  Strapi has a Cloudinary provider to upload the images via the CMS and serving them as the necessary size is as easy as change a URL parameter.&lt;/p&gt;

&lt;h2&gt;
  
  
  CDN
&lt;/h2&gt;

&lt;p&gt;For the rest of my CDN needs, I am using Cloudflare. It is free, convenient and ubiquitous. There isn’t much more I can say about Cloudflare that hasn’t already been said. &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>vue</category>
      <category>javascript</category>
    </item>
    <item>
      <title>What do you like about Laravel?</title>
      <dc:creator>Drew Town</dc:creator>
      <pubDate>Thu, 27 Feb 2020 18:05:39 +0000</pubDate>
      <link>https://dev.to/drewtownchi/what-do-you-like-about-laravel-2mef</link>
      <guid>https://dev.to/drewtownchi/what-do-you-like-about-laravel-2mef</guid>
      <description>&lt;p&gt;I moved away from PHP frameworks years ago and missed the whole rise of the now very popular Laravel framework.&lt;/p&gt;

&lt;p&gt;If you use Laravel what do you find most compelling about it? Community? Ecosystem? Other? Why Laravel over Rails, Keystone (or other based on Express), ASP.NET Core or others?&lt;/p&gt;

&lt;p&gt;I tried asking on &lt;a href="https://twitter.com/drewtown_chi/status/1233041493658624000"&gt;Twitter&lt;/a&gt; earlier but didn't get a response so I'm hoping the DEV community can give me their thoughts.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>php</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I wrote a Thank You Page and You Should Too</title>
      <dc:creator>Drew Town</dc:creator>
      <pubDate>Mon, 06 Jan 2020 13:16:25 +0000</pubDate>
      <link>https://dev.to/drewtownchi/i-wrote-a-thank-you-page-and-you-should-too-4gj4</link>
      <guid>https://dev.to/drewtownchi/i-wrote-a-thank-you-page-and-you-should-too-4gj4</guid>
      <description>&lt;p&gt;The number of personal blogs has skyrocketed in the last few years. Just a few years ago, Medium, Tumblr, Blogger, or Wordpress were the go-to blogging platforms. They were easy for anyone to pick up but frequently came with a few serious drawbacks.  In 2018 or 2019 we saw personal blogs get a breath of fresh air.  Everybody was setting up their own self-hosted blog and in many cases migrating existing content off of the popular blogging platforms. There are a few reasons that come to mind why this is the case.  &lt;/p&gt;

&lt;p&gt;Owning your own content has proven incredibly valuable.  Medium and other blogging services that create paywalls are frustrating to readers and create ambiguity about who owns that data.  There are a few high-profile blogs, such as &lt;a href="https://www.freecodecamp.org/forum/t/we-just-launched-developer-news-heres-how-you-can-use-it/279929"&gt;FreeCodeCamp&lt;/a&gt;, that have removed their content from a slow, reader-hostile platform and relaunched on their own platform. When you control the content and the experience, you can create your blog the best way for the writers and the readers. &lt;/p&gt;

&lt;p&gt;Another force driving the personal blogging scene is just how much easier it is to create a personal blog because of all the hard work put in by the open-source community and the services that support them.  What previously would have meant spinning up a LAMP stack on a virtual private server is now taken care of by pushing a Markdown file to a git repo which runs a webhook to tell Netlify to trigger a fresh build and redeploy static HTML, CSS and JS to a CDN.  They give this workflow to you and what it requires is &lt;a href="https://www.drewtown.dev/post/setting-up-gridsome-with-gitlab-netlifycms-and-netlify/"&gt;gluing some software together&lt;/a&gt; that already plays well together.&lt;/p&gt;

&lt;p&gt;So to all the library authors and service providers that make this blog and many more like it possible…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/86nJuFHH25Uu4/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/86nJuFHH25Uu4/giphy.gif" alt="Thank You"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I originally got this idea from &lt;a href="https://twitter.com/nickytonline"&gt;Nick Taylor&lt;/a&gt; who created a great &lt;a href="https://www.iamdeveloper.com/thanks"&gt;Thanks&lt;/a&gt; page on his personal blog. And like all great developers I borrowed his idea for myself.&lt;/p&gt;

&lt;p&gt;It’s not required, no one expects it, but it is a nice gesture to acknowledge the hard work done to make all developers’ lives easier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.drewtown.dev/thanks"&gt;Thank you!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Writing Better Technical Documentation</title>
      <dc:creator>Drew Town</dc:creator>
      <pubDate>Mon, 16 Dec 2019 13:23:10 +0000</pubDate>
      <link>https://dev.to/drewtownchi/writing-better-technical-documentation-gac</link>
      <guid>https://dev.to/drewtownchi/writing-better-technical-documentation-gac</guid>
      <description>&lt;p&gt;Find this post and more like it on &lt;a href="https://www.drewtown.dev"&gt;drewtown.dev&lt;/a&gt; or follow me on &lt;a href="https://www.twitter.com/drewtown_chi"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Writing technical documentation for a project is a task that everyone agrees is a good idea but often is skipped over for more exciting work. Or, if it gets completed, it is often of poor quality and rarely gets updated.  There is a way to fix these problems though; write clear, concise documentation that removes all the prose and gets to the point in clear and defined language.&lt;/p&gt;

&lt;p&gt;Oddly enough, some of the best technical documentation I’ve run across comes from board game rulebooks.  Let’s take some time to look at how &lt;a href="https://ncdn0.daysofwonder.com/tickettoride/en/img/tt_rules_2015_en.pdf"&gt;Ticket To Ride&lt;/a&gt; lays out an entire set of rules in 4 pages of text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Purpose and Objective
&lt;/h2&gt;

&lt;p&gt;A common part of technical writing I see that is often missing is the overall aim and purpose of the document.  A blurb or paragraph that defines what the document is attempting to accomplish is critical in preserving the context in which the author wrote the document. &lt;/p&gt;

&lt;p&gt;The purpose or objective is your introduction to the user and setting up the context so they know what they will read about.  Depending on the context of the document, this may be the reasoning behind a policy or a quick introduction to the topic. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The object of the game is to score the highest number of total points. Points can be scored by:[…]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For a game rule book, the objective will guide the users and set up the context for all the other rules.  Without providing an objective the other rules may lack clarity of meaning if the reader doesn’t know what they are working towards.&lt;/p&gt;

&lt;p&gt;For a technical document, let’s look at GitLab’s &lt;a href="https://about.gitlab.com/handbook/security/#gitlab-password-policy-guidelines"&gt;password policy&lt;/a&gt;. We can see what the authors were trying to accomplish when they laid out this document: Secure passwords that follow defined recommendations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Passwords are one of the primary mechanisms that protect GitLab information systems and other resources from unauthorized use. Constructing secure passwords and ensuring proper password management is essential. GitLab's password guidelines are based, in part, on the recommendations by &lt;a href="https://pages.nist.gov/800-63-3/sp800-63b.html"&gt;NIST 800-63B&lt;/a&gt;. To learn what makes a password truly secure, read this &lt;a href="https://medium.com/peerio/how-to-build-a-billion-dollar-password-3d92568d9277"&gt;article&lt;/a&gt; or watch this &lt;a href="https://www.youtube.com/watch?v=vudZnjp5Uq0&amp;amp;t=19183"&gt;conference presentation&lt;/a&gt; on password strength.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not every document will need such a verbose purpose statement.  Adjust the introduction to your article to define the required background for the document and prepare your users and your future self with the necessary context to know why the document was written how it was.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining language
&lt;/h2&gt;

&lt;p&gt;One of the most important parts of writing a technical doc is to use clear, unambiguous language that cannot leave the reader wondering about the meaning.  Defined in 1997, &lt;a href="https://tools.ietf.org/html/rfc2119"&gt;RFC2119&lt;/a&gt; has been used to provide clear, purposeful language for IETF documents and we can use the same concepts for other types of writing.&lt;br&gt;
Words such as &lt;strong&gt;must&lt;/strong&gt;, &lt;strong&gt;should&lt;/strong&gt;, and &lt;strong&gt;may&lt;/strong&gt; as outlined in the RFC leave no room for interpretation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;On his turn, a player &lt;strong&gt;must&lt;/strong&gt; perform one (and only one) of the following three actions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each player must take a turn when it is their turn.   There is no ambiguity. The rule leaves no room for questioning.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The player draws 3 Destination Tickets from the top of the deck. He &lt;strong&gt;must&lt;/strong&gt; keep at least one of them, but he &lt;strong&gt;may&lt;/strong&gt; keep two or all three if he chooses.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Again, the rule is explicit.  Any player of this game will know what they &lt;strong&gt;must&lt;/strong&gt; do and what they &lt;strong&gt;may&lt;/strong&gt; do based on these rules.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When the deck is exhausted, the discards are reshuffled into a new draw pile deck. The cards &lt;strong&gt;should&lt;/strong&gt; be shuffled thoroughly, since all the cards have been discarded in sets.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finally, the word &lt;strong&gt;should&lt;/strong&gt; can provide recommendations.  There is no requirement to do it, but you will want to.&lt;/p&gt;

&lt;p&gt;We should apply similar patterns to all technical documentation.  Must, may and should and their opposites must not, may not and should not are the best way I’ve found to convey clarity, intent and purpose from me the writer to you the reader of my technical documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep It Short
&lt;/h2&gt;

&lt;p&gt;I haven’t met many people who love to write technical documentation and even less that enjoy reading it.  Keep the prose to a minimum and focus on providing important information in as compact a format as possible.&lt;/p&gt;

&lt;p&gt;Writing long paragraphs or providing extraneous information will derail the readers. If you even get the thing written…&lt;/p&gt;

&lt;h2&gt;
  
  
  Organize The Document
&lt;/h2&gt;

&lt;p&gt;Hierarchical organization of documentation, like HTML, provides a natural outline for writers and readers. Headers, sub-headers, and sections make it easier for readers to find relevant information.&lt;/p&gt;

&lt;p&gt;Don’t make your users read huge walls of text to find the information they need. Lay out the document into clear, logical sections that are easy to scan and read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read Good Documentation
&lt;/h2&gt;

&lt;p&gt;As I mentioned in introducing this article, board games are often great examples of technical writing.  So grab your favorite board game, take out the rule back and study.&lt;/p&gt;

&lt;p&gt;Alternatively, there is no shame in stealing the format or techniques of another project’s documentation either.  &lt;a href="https://vuejs.org/v2/guide/index.html"&gt;Vue.js&lt;/a&gt; and &lt;a href="https://redis.io/documentation"&gt;Redis&lt;/a&gt; are both respected for their documentation but have different styles and tones.  Choose a project where you like the documentation and use that as a guide for writing your own documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use a Writing Aid
&lt;/h2&gt;

&lt;p&gt;If writing isn’t your strong suit, or even if it is, a writing tool can easily improve the overall clarity, structure and grammar of your documentation.&lt;/p&gt;

&lt;p&gt;My favorite tool is &lt;a href="https://prowritingaid.com/en/Account/Register2?twafid=1340117"&gt;ProWritingAid&lt;/a&gt; (referral link) for writing help.  Other free tools exist as well, such as &lt;a href="https://hemingwayapp.com/"&gt;Hemingway Editor&lt;/a&gt;, &lt;a href="https://grammarly.com"&gt;Grammarly&lt;/a&gt;, &lt;a href="https://www.grammarlookup.com/"&gt;Grammar Lookup&lt;/a&gt; and &lt;a href="https://languagetool.org/"&gt;LanguageTool&lt;/a&gt; just to name a few.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get feedback
&lt;/h2&gt;

&lt;p&gt;It is extremely difficult to write documentation in a vacuum.  We should consult users of different levels and abilities who are using the documentation to understand their clarity and understanding of our writing.  &lt;/p&gt;

&lt;p&gt;Often I will write documentation that is clear to me but skips steps or makes assumptions that would not be clear to a person coming into a project cold.  Finding someone who does not have all the built-up context around what you are writing about is crucial to make sure your document is understandable by everyone.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>documentation</category>
      <category>beginners</category>
      <category>sysadmin</category>
    </item>
    <item>
      <title>Explain Like I'm Five (ELI5) Vuex</title>
      <dc:creator>Drew Town</dc:creator>
      <pubDate>Fri, 25 Oct 2019 16:28:01 +0000</pubDate>
      <link>https://dev.to/drewtownchi/explain-like-i-m-five-eli5-vuex-33h3</link>
      <guid>https://dev.to/drewtownchi/explain-like-i-m-five-eli5-vuex-33h3</guid>
      <description>&lt;p&gt;A question that I see come up from new users to Vue is, “What is Vuex?” Or, “Can someone ELI5 (explain like I’m five) what Vuex is?” and usually the response is something like “Vuex is a flux pattern for storing and retrieving data from a centralized global object store”.  While technically correct, this answer always leaves me scratching my head and wondering what five-year-olds this person is hanging around?  When I was five, I was more excited by Tonka trucks or playing soccer than learning about programming patterns.&lt;/p&gt;

&lt;p&gt;When this question came up on Reddit, I spent some time to figure out about how I could explain Vuex to a five-year-old.  An answer without the jargon and straight to the point.  I feel a good analogy for Vuex is a vending machine.&lt;br&gt;
If you’ve ever used a vending machine, you know that a user can buy items using buttons. The machine also needs to be stocked; someone needs to deliver those goods to the machine, and it needs a mechanism to get the user’s choice into their hands.&lt;/p&gt;

&lt;p&gt;Let’s go over the different Vuex concepts and see how we can best relate them to the vending machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  State
&lt;/h2&gt;

&lt;p&gt;The Vuex concept of state is fundamentally what is in stock at the vending machine.  This might be anything for chips to cookies to strings.  Or even integers, arrays, or objects.&lt;/p&gt;

&lt;p&gt;Vending machines can seemingly carry any number of items under the sun the same holds true for Vuex.  Vuex can hold any JavaScript type.  Keep in mind though that Vue’s &lt;a href="https://vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats"&gt;reactivity caveats&lt;/a&gt; also hold true for Vuex.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getters
&lt;/h2&gt;

&lt;p&gt;Getters are how you access the contents of the vending machines.  If you would like to buy the cookies, you insert your money and punch the buttons to get your item.  &lt;/p&gt;

&lt;p&gt;In Vuex getters are more powerful than simple vending machine buttons.  Instead of getting back one item, you could select only the chocolate chip cookies.  It would be possible to combine various pieces of your state to create something that didn’t even exist before.  &lt;/p&gt;

&lt;p&gt;Getters are a powerful way to shape your data to retrieve only items you want or to build new items from the data that exists in state.&lt;br&gt;
If you are familiar with Vue’s computed properties, they are comparable to Vuex’s getters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mutations
&lt;/h2&gt;

&lt;p&gt;When a vending machine is new or getting low, someone needs to come around a stock it full of goods.  Mutations are how we fill up our Vuex vending machine.&lt;/p&gt;

&lt;p&gt;When we receive our Vuex vending machine, we don’t want to change the contents as that may break Vue’s reactivity.  You should make sure all of your fields, or items for sale, are set up ahead of time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Actions
&lt;/h2&gt;

&lt;p&gt;Sometimes when you go to fill your vending machine, you may not have all the items you need.  Instead of leaving the vending machine to go get more items to fill it up, you call a friend (async request) to retrieve the items and bring them to you.&lt;/p&gt;

&lt;p&gt;Once your friend arrives with the goods, you can now fill the vending machine with a mutation.&lt;/p&gt;

&lt;p&gt;You can find this post and many more over on my personal blog &lt;a href="https://www.drewtown.dev"&gt;drewtown.dev&lt;/a&gt; or consider following me on &lt;a href="https://www.twitter.com/drewtown_chi"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vue</category>
      <category>vuex</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Favorite Git Interactive Tutorials or Videos?</title>
      <dc:creator>Drew Town</dc:creator>
      <pubDate>Mon, 21 Oct 2019 19:10:58 +0000</pubDate>
      <link>https://dev.to/drewtownchi/favorite-git-interactive-tutorials-or-videos-5ff0</link>
      <guid>https://dev.to/drewtownchi/favorite-git-interactive-tutorials-or-videos-5ff0</guid>
      <description>&lt;p&gt;I'm looking for some well done interactive git tutorials or video series for a co-worker.  Does anyone have any they particularly liked?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>git</category>
    </item>
    <item>
      <title>Improve Blog Post SEO with Gridsome and Vue Meta</title>
      <dc:creator>Drew Town</dc:creator>
      <pubDate>Sat, 05 Oct 2019 06:05:39 +0000</pubDate>
      <link>https://dev.to/drewtownchi/improve-blog-post-seo-with-gridsome-and-vue-meta-469d</link>
      <guid>https://dev.to/drewtownchi/improve-blog-post-seo-with-gridsome-and-vue-meta-469d</guid>
      <description>&lt;p&gt;Gridsome generously includes &lt;a href="https://vue-meta.nuxtjs.org/"&gt;Vue Meta&lt;/a&gt; which has become the de-facto standard for updating the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; tag in Vue projects.  Combining the power of Vue Meta with various aspects of the Gridsome GraphQL API we can create relevant page titles, descriptions and create rich social media cards for blog posts that use Gridsome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;As long as you have a working Gridsome site there is nothing you have to do to get started!  From any of your Gridsome pages or templates&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding meta info to pages
&lt;/h2&gt;

&lt;p&gt;As Gridsome's pages are generally static we can use the basic Vue Meta syntax to add a title.  In our site's &lt;code&gt;pages/Index.vue&lt;/code&gt; we can add a &lt;code&gt;metaInfo&lt;/code&gt; object in our JavaScript.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;metaInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, world!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now when we visit the home page of our blog we will see the tile of our tab has changed to &lt;code&gt;Hello, world! - Drew Town Dev&lt;/code&gt;. By default, Gridsome will append your site's name from &lt;code&gt;gridsome.config.js&lt;/code&gt; file  using the &lt;code&gt;siteName&lt;/code&gt; value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;siteName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Drew Town Dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// ...other config&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It is possible to override this by setting Vue Meta's &lt;code&gt;titleTemplate&lt;/code&gt; property which could be set in your Layout &lt;code&gt;Default.vue&lt;/code&gt; if you prefer a different title template. For example, switching to a pipe instead of a dash would require &lt;code&gt;titleTemplate&lt;/code&gt; value of &lt;code&gt;%s - My Travel Blog&lt;/code&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Adding meta info to templates
&lt;/h2&gt;

&lt;p&gt;Templates are where the real power of Vue Meta comes into play.  We can use the method syntax of Vue Meta to dynamically change values.  The method syntax will allow us to dynamically set the title and the meta description of the page for starters.  Additionally, we can detect if a post has various properties, such as a cover image, which will allow us to add extra tags to inform Twitter or Facebook that they can display our links as rich cards.&lt;/p&gt;

&lt;p&gt;In this example, we will use information about our post from a Gridsome &lt;a href="https://gridsome.org/docs/querying-data/#query-data-in-page-components"&gt;page query&lt;/a&gt; to add extra information to the head meta tags.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;metaInfo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;excerpt&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;og:title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;twitter:card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;summary_large_image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;summary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;twitter:creator&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@drewtown_chi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;og:description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;cotent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;excerpt&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;og:image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The title tag can be set as a direct property on the returned object as it is expected by Vue Meta.  The other meta tags must be returned as an array of &lt;code&gt;meta&lt;/code&gt; objects via the meta property.&lt;/p&gt;

&lt;p&gt;The content can be set using the &lt;code&gt;this.$page.post&lt;/code&gt; values, static values, or event ternary expressions in the case of the &lt;code&gt;twitter:card&lt;/code&gt; tag.  In this example, we assess whether an image exists or not to help Twitter determine which type of card to use for the post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;There are many &lt;a href="https://ogp.me/"&gt;tags&lt;/a&gt; and &lt;a href="https://developers.google.com/search/docs/data-types/recipe"&gt;structured data types&lt;/a&gt; such as recipes, events, books and many more that can be used to help guide users to your content and boost SEO rankings.&lt;/p&gt;

&lt;p&gt;Using Vue Meta with Gridsome is the best way to make use of the various tags available with the least amount of effort required.&lt;/p&gt;

</description>
      <category>gridsome</category>
      <category>vue</category>
      <category>beginners</category>
      <category>javascript</category>
    </item>
    <item>
      <title>When To Use Vuex Getters In a Vue.js Project</title>
      <dc:creator>Drew Town</dc:creator>
      <pubDate>Tue, 27 Aug 2019 13:38:15 +0000</pubDate>
      <link>https://dev.to/drewtownchi/when-to-use-vuex-getters-in-a-vue-js-project-46b</link>
      <guid>https://dev.to/drewtownchi/when-to-use-vuex-getters-in-a-vue-js-project-46b</guid>
      <description>&lt;p&gt;If you enjoy this article please consider following me on &lt;a href="https://www.twitter.com/drewtown_chi"&gt;Twitter&lt;/a&gt; or visiting my &lt;a href="https://www.drewtown.dev"&gt;website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One of the features of &lt;a href="https://vuex.vuejs.org"&gt;Vuex&lt;/a&gt; is its ability to parse and cache values from the store's state for quick retrieval via getters. &lt;a href="https://vuex.vuejs.org/guide/getters.html"&gt;Vuex getters&lt;/a&gt; are equivalent to Vue's computed properties in that they both are reactive to changes in dependencies and cached for improved performance.  A question I often see from beginners when learning Vue and Vuex is when to use getters in their project.  There is often a misunderstanding that Vuex must be used for all data retrieval from the store and a tendency to overuse getters.  Often time retrieving the state from the store and performing operations on that state within components is a better solution with less boilerplate required.&lt;/p&gt;

&lt;p&gt;The line between when to use access the state directly and when to use getters is a thin gray line.&lt;/p&gt;

&lt;h2&gt;
  
  
  When accessing the state is good enough
&lt;/h2&gt;

&lt;p&gt;My general rule of thumb is that whenever I need the entire value of the variable from Vuex's state, I will retrieve it directly from the state. Some examples where you would want the entire value may be a boolean, a string, a whole object or an entire array. When you are retrieving the entire variable from the state it makes sense to retrieve the entire value by accessing it directly from the state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There is little value gained from wrapping the retrieval of these values in a getter.  The computed property in the component will cache the result and will also watch the dependency, in this case, the store's state, for any changes. You receive the benefits of the getter without the boilerplate required by Vuex.  By creating a getter in these cases you've essentially created two different access points into the store that returns the exact same value.&lt;/p&gt;

&lt;p&gt;The inclination of beginners starting to use Vuex is to use it for everything and use all of its features.  At first, it seems smart. All your state, mutations, actions, and getters in one centralized place which should make it easy to reason about. You'll quickly realize that adding unnecessary boilerplate is one of the great follies of the Flux pattern that Vuex is based on.  The excess code required is tiresome for the value extracted in certain cases such as the one outlined above.&lt;/p&gt;

&lt;p&gt;Use Vuex where it is the strongest and best option but avoid overusing it because it is available.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use getters then?
&lt;/h2&gt;

&lt;p&gt;Most of the time getters are the best option when you need to access state &lt;strong&gt;and&lt;/strong&gt; filter or manipulate the data in some way.  Getters are even better when you need to access state and use the same filtering and parsing in multiple components.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Getters provide a great interface for containing and reusing data logic in a centralized place.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's take a look at a to-do application that needs to provide different views.  Certain views may only need to show a list of all to-dos where others may need to sort them by the due date attached to the to-do item.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;getters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;openTodos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;sortedTodos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//remember sort mutates the original array so, copy it first with a spread.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localeCompare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Implementing the filtering and sorting logic in Vuex allows you to centralize and organize data manipulation across the application.  This helps keep components less concerned about how to manipulate the data and allows for easier refactoring. When logic is changed or data is updated there are fewer places to update reducing the work required to update the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using variables in getters
&lt;/h2&gt;

&lt;p&gt;Another good use for getters is to retrieve object items from an array by their id.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;getters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;getTodoById&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Because getters can accept a variable via the &lt;a href="https://vuex.vuejs.org/guide/getters.html#method-style-access"&gt;method style access&lt;/a&gt; getters are a convenient way to centralize and organize access to data even when you need to supply parameters.  Rather than having the find logic spread across many components and files a single method can be used to contain all the required logic.&lt;/p&gt;

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

&lt;p&gt;Getters are a great tool but not every problem is a nail.  Use them often when needing to extract parts of the state in the store or manipulate data before retrieving it. Centralizing the logic will reduce the amount of code reuse and duplication at the cost of adding some boilerplate.  In those cases, the benefit greatly exceeds the cost.&lt;/p&gt;

</description>
      <category>vuex</category>
      <category>vue</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Tailwindcss is My Favorite Coloring Book</title>
      <dc:creator>Drew Town</dc:creator>
      <pubDate>Sun, 11 Aug 2019 14:34:31 +0000</pubDate>
      <link>https://dev.to/drewtownchi/tailwindcss-is-my-favorite-coloring-book-42pe</link>
      <guid>https://dev.to/drewtownchi/tailwindcss-is-my-favorite-coloring-book-42pe</guid>
      <description>&lt;p&gt;I am, by nature, a tinkerer. When putting a design together rarely am I satisfied on my first attempt. Rarely, am I satisfied with my design until iteration 3, 4 or even 5. Let's see how a limited set of choices can help you create a cleaner page with less CSS and less mental overhead for designers and developers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tailwindcss.com/"&gt;Tailwindcss&lt;/a&gt; has a well-crafted design system of utility classes. If you are unaware of utility-first CSS it is a system of hyper-focused classes that do exactly 1 thing. &lt;code&gt;mx-1&lt;/code&gt; or &lt;code&gt;shadow-lg&lt;/code&gt; are responsible for doing 1 thing, increasing the horizontal margin or adding a large box-shadow. These classes can be combined to create cards or buttons or any other aspect of your page.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Design System
&lt;/h2&gt;

&lt;p&gt;Utility classes provide the guidelines to your design system. The design system allows you to tinker by changing values but only to a limited set of alternatives. The creators of Tailwindcss like to express this as a "constrained set of choices."&lt;/p&gt;

&lt;p&gt;A constrained set of choices limits your ability to adjust the margin from 1rem to 1.05rem as that is not a choice within the design system provided. Why is that good? If you need 1.05rem shouldn't you be able to use that value as your margin? Seldom has such fine-grained control over such small details lead to a better design. You've introduced a small detail that may only appear in 1 place, or worse still, may need to be remembered and used in other places.&lt;/p&gt;

&lt;p&gt;Instead, Tailwindcss provides a thoughtful design system of limited choices.  The choices provided are flexible but still force you to work within the lines. &lt;/p&gt;

&lt;h2&gt;
  
  
  Consistency is Key
&lt;/h2&gt;

&lt;p&gt;One key to a professional-looking design is consistency. Having to decide about spacing or shadows or font sizes while trying to build a new design will add a lot of mental overhead. Tailwindcss takes those decisions out of your hands and has created a design system that allows you to be flexible, tinker.&lt;/p&gt;

&lt;p&gt;The limited choices available helps both designers and developers be more consistent and agree on a design language without needing a full blown design system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remove Choices, Reduce File Size
&lt;/h2&gt;

&lt;p&gt;Having a guide such as Tailwindcss will always reduce your CSS bundle size. Taking the choices out of designing will prevent designers and developers from adding 9 versions of red, all which look the same. Or, adding 12 different box-shadow styles where 4 would do.&lt;/p&gt;

&lt;p&gt;It is a dual benefit, reduce cognitive overhead of making design system choices and also prevent CSS bloat. When there are only 4 well-known choices for a box-shadow it is harder for developers and designers to go rogue and introduce their own. Trust and use the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternatives
&lt;/h2&gt;

&lt;p&gt;The constrained design system isn't unique to Tailwindcss. A similar system could be implemented using SASS variables. This would be a workable alternative but would need an immense amount of work to recreate. Additionally this is something that Tailwindcss has already done very well and provides a ton of flexibility with the configuration system.&lt;/p&gt;

&lt;p&gt;Get out there and start coloring.&lt;/p&gt;

&lt;p&gt;You can check out my other posts about Tailwind here on Dev.to or on my personal blog &lt;a href="https://www.drewtown.dev"&gt;drewtown.dev&lt;/a&gt; or follow me on &lt;a href="https://twitter.com/drewtown_chi"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>css</category>
      <category>design</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
