<?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: Mark Townsend</title>
    <description>The latest articles on DEV Community by Mark Townsend (@mtownsend5512).</description>
    <link>https://dev.to/mtownsend5512</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%2F134497%2Ff7f46d6e-3715-40a0-9027-02418c1f2aa6.jpg</url>
      <title>DEV Community: Mark Townsend</title>
      <link>https://dev.to/mtownsend5512</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mtownsend5512"/>
    <language>en</language>
    <item>
      <title>Working with JWTs in Laravel (Without the Magic)</title>
      <dc:creator>Mark Townsend</dc:creator>
      <pubDate>Mon, 30 Mar 2026 18:36:33 +0000</pubDate>
      <link>https://dev.to/mtownsend5512/working-with-jwts-in-laravel-without-the-magic-36oh</link>
      <guid>https://dev.to/mtownsend5512/working-with-jwts-in-laravel-without-the-magic-36oh</guid>
      <description>&lt;p&gt;If you've worked with APIs, authentication, or third-party integrations, you've almost certainly run into JWTs (JSON Web Tokens).&lt;/p&gt;

&lt;p&gt;JWT (pronounced: JOT) is a compact string used to pass data between systems. It has three base64url-encoded parts separated by dots: header, payload, and signature. The header defines metadata like the algorithm, the payload contains claims such as user id and expiration, and the signature ensures integrity. The header and payload can be decoded into JSON for quick inspection.&lt;/p&gt;

&lt;p&gt;Let's create a simple trait you can use on any class in your Laravel application to read the contents of a JWT:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Quartzy\Illuminate\Support\Traits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Str&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;trait&lt;/span&gt; &lt;span class="nc"&gt;InspectsJwt&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;inspectJwt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$jwt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&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="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;str_contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$jwt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&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="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="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;explode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$jwt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$segment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nv"&gt;$decoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;base64_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nc"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$segment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'_'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'+'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;__toString&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="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$decoded&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="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// removes empty payload elements from the array&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$carry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;array_merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$carry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$item&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;Now you can inspect the contents of JWT with ease! Let's look at an example where a user is making a request to our app and their JWT token is passed as a header in the request. This would be our example JWT:&lt;/p&gt;

&lt;p&gt;(You can copy the following JWT and examine it on &lt;a href="https://www.jwt.io/" rel="noopener noreferrer"&gt;JWT.io&lt;/a&gt;&lt;br&gt;
&lt;code&gt;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2Nzg5MCIsInV1aWQiOiI1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDAiLCJuYW1lIjoiTWFyayBUb3duc2VuZCIsImVtYWlsIjoibWFyay50b3duc2VuZEBleGFtcGxlLmNvbSIsInJvbGUiOiJkZXZlbG9wZXIiLCJpYXQiOjE3MTAwMDAwMDAsImV4cCI6MTcxMDAwMzYwMH0.signatureplaceholder&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Http\Middleware&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Closure&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Http\Request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Quartzy\Illuminate\Support\Traits\InspectsJwt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthenticateWithJwtUuid&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;InspectsJwt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Closure&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$authorization&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Authorization'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&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="nf"&gt;str_starts_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$authorization&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Bearer '&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$jwt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$authorization&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;inspectJwt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$jwt&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="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'uuid'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Pull user by UUID instead of numeric ID&lt;/span&gt;
                &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'uuid'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'uuid'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;first&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="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"JWT authenticated user &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; with role &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'role'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&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="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all there is to it! Hopefully this helps demystify JWTs and simplifies working with them in your application!&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>security</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Learn all Eloquent lifecycle hooks in less than 5 minutes</title>
      <dc:creator>Mark Townsend</dc:creator>
      <pubDate>Mon, 22 Apr 2024 15:44:49 +0000</pubDate>
      <link>https://dev.to/mtownsend5512/a-quick-intro-to-eloquent-observers-1hmp</link>
      <guid>https://dev.to/mtownsend5512/a-quick-intro-to-eloquent-observers-1hmp</guid>
      <description>&lt;p&gt;Laravel's Eloquent Observers are powerful tools that allow you to attach actions to model events that occur throughout your app. These events include creating, updating, deleting, and more, providing a convenient way to respond to changes in your application's data without having to scatter code all over your app's codebase.&lt;/p&gt;

&lt;p&gt;Implementing observers involves creating a dedicated class for each model, usually within the app/Observers directory, and defining methods that correspond to the desired model events. This separation of concerns enhances code readability and makes it easier to manage application logic, especially as it grows in complexity.&lt;/p&gt;

&lt;p&gt;Here is a list of all the Laravel model events you can listen for, utilizing &lt;code&gt;Post&lt;/code&gt; as our example model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;
&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Observers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\Post&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostObserver&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;retrieved&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Logic to be executed just after a Post is retrieved from the DB&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;creating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Logic to be executed just before creating a new Post is being created for the first time&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Logic to be executed after creating a new Post is created for the first time&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;updating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Logic to be executed just before updating a Post&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;updated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Logic to be executed after updating a Post&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;saving&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Logic to be executed just before saving a Post (fires for both create and update)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Logic to be executed after saving a Post (fires for both create and update)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;deleting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Logic to be executed just before deleting a Post&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;deleted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Logic to be executed after deleting a Post&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;forceDeleting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Logic to be executed before force deleting a Post&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;forceDeleted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Logic to be executed after force deleting a Post&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;restoring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Logic to be executed just before restoring a soft-deleted Post&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;restored&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Logic to be executed after restoring a soft-deleted Post&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;replicating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Logic to be executed after replicating a Post&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>laravel</category>
      <category>eloquent</category>
      <category>observers</category>
      <category>events</category>
    </item>
    <item>
      <title>Creating a FAQ accordion WITHOUT any Javascript!?</title>
      <dc:creator>Mark Townsend</dc:creator>
      <pubDate>Mon, 23 May 2022 02:35:30 +0000</pubDate>
      <link>https://dev.to/mtownsend5512/creating-a-faq-accordion-without-any-javascript-2llc</link>
      <guid>https://dev.to/mtownsend5512/creating-a-faq-accordion-without-any-javascript-2llc</guid>
      <description>&lt;p&gt;You've got a product or platform and you've anticipated the questions that are commonly asked. Time to build an FAQ (frequently asked questions) page to save you the time of answering the same questions repeatedly.&lt;/p&gt;

&lt;p&gt;Did you know you don't even need to touch Javascript to make a beautiful accordion FAQ? Sounds crazy, right?! All we need is HTML and CSS. For this demonstration I'm going to be using Tailwind 3+ (because of its integration with JIT) to show you how easy it is. Let's get started!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you want the full source code skip to the bottom.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's start with the most basic markup:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;section&amp;gt;
  &amp;lt;details&amp;gt;
    &amp;lt;summary&amp;gt;
      Question 1
    &amp;lt;/summary&amp;gt;
    &amp;lt;p&amp;gt;Answer 1.&amp;lt;/p&amp;gt;
  &amp;lt;/details&amp;gt;

  &amp;lt;details&amp;gt;
    &amp;lt;summary&amp;gt;
      Question 2
    &amp;lt;/summary&amp;gt;
    &amp;lt;p&amp;gt;Answer 2.&amp;lt;/p&amp;gt;
  &amp;lt;/details&amp;gt;

  &amp;lt;details&amp;gt;
    &amp;lt;summary&amp;gt;
      Question 3
    &amp;lt;/summary&amp;gt;
    &amp;lt;p&amp;gt;Answer 3.&amp;lt;/p&amp;gt;
  &amp;lt;/details&amp;gt;
&amp;lt;/section&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;details&lt;/code&gt; tag automatically gives us the ability to show and hide content outside of the summary by default. It's where all the magic happens that allows us to completely skip using any Javascript.&lt;/p&gt;

&lt;p&gt;The first thing we want to do is get rid of the browser's basic arrow character so we can style everything ourselves. Tailwind has a handy pseudo element selector called &lt;code&gt;marker:&lt;/code&gt; which lets us style this. You may think that &lt;code&gt;marker:hidden&lt;/code&gt; would do the trick to get rid of those pesky arrows, but surprisingly, it doesn't! However, because the browser treats them as inline text we can simply make them disappear by setting their font size to 0. Adding the following onto each &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; tag will do the trick: &lt;code&gt;class="marker:[font-size:0px]"&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;section&amp;gt;
  &amp;lt;details&amp;gt;
    &amp;lt;summary class="marker:[font-size:0px]"&amp;gt;
      Question 1
    &amp;lt;/summary&amp;gt;
    &amp;lt;p&amp;gt;Answer 1.&amp;lt;/p&amp;gt;
  &amp;lt;/details&amp;gt;

  &amp;lt;details&amp;gt;
    &amp;lt;summary class="marker:[font-size:0px]"&amp;gt;
      Question 2
    &amp;lt;/summary&amp;gt;
    &amp;lt;p&amp;gt;Answer 2.&amp;lt;/p&amp;gt;
  &amp;lt;/details&amp;gt;

  &amp;lt;details&amp;gt;
    &amp;lt;summary class="marker:[font-size:0px]"&amp;gt;
      Question 3
    &amp;lt;/summary&amp;gt;
    &amp;lt;p&amp;gt;Answer 3.&amp;lt;/p&amp;gt;
  &amp;lt;/details&amp;gt;
&amp;lt;/section&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now that we got rid of those ugly arrows we can add our own. Let's make the summary a flex box and add svgs so we can make it look better.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;details&amp;gt;
&amp;lt;summary class="flex flex-row items-center justify-between marker:[font-size:0px]"&amp;gt;
  Question 1
  &amp;lt;svg class="h-6 w-6 rotate-0 transform text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"&amp;gt;
    &amp;lt;path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"&amp;gt;&amp;lt;/path&amp;gt;
  &amp;lt;/svg&amp;gt;
  &amp;lt;/summary&amp;gt;
  &amp;lt;p&amp;gt;Answer 1.&amp;lt;/p&amp;gt;
&amp;lt;/details&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Awesome! We've got our nice arrow now. Looks a lot better. But immediately you'll notice a problem. It doesn't rotate when we open it to show that the content has been expanded.&lt;/p&gt;

&lt;p&gt;If you click on the summary tag you'll notice an html attribute of &lt;code&gt;open&lt;/code&gt; being added. We can target this with CSS to apply css to the svg and make it transform and rotate accordingly. To do this, we're going to have to apply Tailwind's &lt;code&gt;group&lt;/code&gt; class on the &lt;code&gt;details&lt;/code&gt; element so we can make the svg react when it changes. We'll add a class of &lt;code&gt;group-open:rotate-180&lt;/code&gt; to the svg itself.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;details class="group"&amp;gt;
&amp;lt;summary class="flex flex-row items-center justify-between marker:[font-size:0px]"&amp;gt;
  Question 1
  &amp;lt;svg class="h-6 w-6 rotate-0 transform text-gray-400 group-open:rotate-180" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"&amp;gt;
    &amp;lt;path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"&amp;gt;&amp;lt;/path&amp;gt;
  &amp;lt;/svg&amp;gt;
  &amp;lt;/summary&amp;gt;
  &amp;lt;p&amp;gt;Answer 1.&amp;lt;/p&amp;gt;
&amp;lt;/details&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It's really starting to come together now and we still haven't needed Javascript. Let's add a little polish with colors, font size, padding and make the cursor change to a pointer on hover.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;section class="grid grid-cols-1 gap-y-3 divide-y"&amp;gt;
  &amp;lt;details open class="group py-1 text-lg"&amp;gt;
    &amp;lt;summary class="flex cursor-pointer flex-row items-center justify-between py-1 font-semibold text-gray-800 marker:[font-size:0px]"&amp;gt;
      Question 1
      &amp;lt;svg class="h-6 w-6 rotate-0 transform text-gray-400 group-open:rotate-180" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"&amp;gt;
        &amp;lt;path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"&amp;gt;&amp;lt;/path&amp;gt;
      &amp;lt;/svg&amp;gt;
    &amp;lt;/summary&amp;gt;
    &amp;lt;p class="text-gray-500"&amp;gt;Answer 1.&amp;lt;/p&amp;gt;
  &amp;lt;/details&amp;gt;
  ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's looking &lt;code&gt;really&lt;/code&gt; close to finished. You can just add your personal touches and it's ready to answer all those questions you don't have time to respond to!&lt;/p&gt;

&lt;p&gt;One final thing to know about this is if you add an &lt;code&gt;open&lt;/code&gt; attribute onto the &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; tag it will automatically be expanded on page load. You can use this with a snippet of Javascript or to simply have the first item already opened for the visitor. If you need to apply any more styling to the &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; tag when it is expanded, just use the &lt;code&gt;marker:&lt;/code&gt; pseudo selector in Tailwind.&lt;/p&gt;

&lt;h3&gt;
  
  
  You can view the full source code at &lt;a href="https://play.tailwindcss.com/I9nwaTQdoX"&gt;this Tailwind Playground&lt;/a&gt;.
&lt;/h3&gt;

&lt;p&gt;If you liked this article, please give it a like or share it on Twitter!&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>accordion</category>
      <category>faq</category>
      <category>toggle</category>
    </item>
    <item>
      <title>Revealing hidden elements when hovering a parent with Tailwind CSS</title>
      <dc:creator>Mark Townsend</dc:creator>
      <pubDate>Tue, 31 Aug 2021 01:48:02 +0000</pubDate>
      <link>https://dev.to/mtownsend5512/revealing-hidden-elements-when-hovering-a-parent-with-tailwind-css-159a</link>
      <guid>https://dev.to/mtownsend5512/revealing-hidden-elements-when-hovering-a-parent-with-tailwind-css-159a</guid>
      <description>&lt;p&gt;&lt;strong&gt;&lt;em&gt;This article was written for Tailwind 2.0. As of 3.0 variants are available by default thanks to the new JIT engine. Please refer to the Tailwind 3.0 docs for further inquiry. However, &lt;code&gt;group&lt;/code&gt; should still be the solution to this particular CSS issue.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tailwind CSS makes it almost &lt;em&gt;too&lt;/em&gt; easy these days. Add two classes: &lt;code&gt;text-blue-500 hover:text-blue-700&lt;/code&gt; and you have a perfectly styled link.&lt;/p&gt;

&lt;p&gt;But what if you want to hide elements until the user hovers the mouse over a parent element? That type of UI requirement is fairly common for dashboard tables when you want to provide quick access to things without overwhelming the user with a lot of visual clutter.&lt;/p&gt;

&lt;p&gt;Tailwind is capable of doing that with &lt;a href="https://tailwindcss.com/docs/hover-focus-and-other-states#group-hover"&gt;group hover&lt;/a&gt;. For this example, we're going to reach for a lesser used css attribute: &lt;code&gt;visibility&lt;/code&gt;. The difference between &lt;code&gt;visibility: hidden;&lt;/code&gt; and &lt;code&gt;display: none;&lt;/code&gt; is the browser treats elements with their visibility set to hidden as still being present, but simply...invisible. Their space and padding are still respected, though, as if they were still present. Elements with their display set to &lt;code&gt;none&lt;/code&gt; are essentially ignored from the markup. None of their spacing or padding impacts the page until they are visible again. We don't want elements jumping around when the user hovers over our element, so we'll be using &lt;code&gt;visibility: hidden&lt;/code&gt; instead of &lt;code&gt;display: none;&lt;/code&gt; for this example.&lt;/p&gt;

&lt;p&gt;Consider the following HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"group"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Some content that is always visible.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"invisible group-hover:visible"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;I am hidden until my parent is hovered!&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You would probably expect this to work - but it won't. That's because Tailwind's &lt;code&gt;group-hover&lt;/code&gt; doesn't support &lt;code&gt;display&lt;/code&gt; types out of the box. But, with one line of code, we can easily make it work!&lt;/p&gt;

&lt;p&gt;You can add this to your &lt;code&gt;tailwind.config.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;visibility&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;group-hover&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, it will work!&lt;/p&gt;

&lt;p&gt;One thing to note: the &lt;code&gt;visibility&lt;/code&gt; attribute cannot have transitions applied to it since it is an "on" or "off" field. If transitions is something you're after, consider using Tailwind's &lt;a href="https://tailwindcss.com/docs/opacity"&gt;opacity&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check out this &lt;a href="https://play.tailwindcss.com/M410phgTM3"&gt;Tailwind playground link&lt;/a&gt; to see group hover working with CSS's visibility class.
&lt;/h3&gt;

</description>
      <category>tailwindcss</category>
      <category>hover</category>
      <category>display</category>
      <category>group</category>
    </item>
    <item>
      <title>Creating powerful tabs with Alpine.js in less than 5 minutes</title>
      <dc:creator>Mark Townsend</dc:creator>
      <pubDate>Sat, 30 May 2020 02:02:41 +0000</pubDate>
      <link>https://dev.to/mtownsend5512/creating-powerful-tabs-with-alpine-js-in-less-than-5-minutes-367d</link>
      <guid>https://dev.to/mtownsend5512/creating-powerful-tabs-with-alpine-js-in-less-than-5-minutes-367d</guid>
      <description>&lt;p&gt;If you haven't heard of a little Javascript framework called, "Alpine.js" until now, you're about to discover something awesome.&lt;/p&gt;

&lt;p&gt;Its creator, &lt;a href="https://twitter.com/calebporzio"&gt;Caleb Porzio&lt;/a&gt; describes it in his own words:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Alpine.js offers you the reactive and declarative nature of big frameworks &amp;gt; like Vue or React at a much lower cost. You get to keep your DOM, and &amp;gt; sprinkle in behavior as you see fit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Alpine.js is the definitive middle ground between vanilla Javascript and big frameworks. It effectively does away with the headache of having to write a bunch of event listeners and functions to handle your script functionality. It isn't meant to replace big robust frameworks like Vue and React. Instead, it's meant for those smaller use cases when you want to accomplish something smaller without having to fire up an entire framework.&lt;/p&gt;

&lt;p&gt;Got it? Cool.&lt;/p&gt;

&lt;p&gt;On to the point, then. Let's make a simple, yet robust tab system with Alpine.js!&lt;/p&gt;

&lt;p&gt;To start, let's import alpine.js via the specified CDN. At the time of this post, alpine.js is currently at version 2.XX. Put the alpine script at the end of your &lt;code&gt;head&lt;/code&gt; tag: &lt;code&gt;&amp;lt;script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;. Next, we'll write out some basic skeleton html. For our example, let's say we're coding a tab section for the product page of an e-commerce web site. We'll keep it simple with just a description and ratings tab.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"tab_wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- The tabs navigation --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Description&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Reviews&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- The tabs content --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    Lorem ipsum description.
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    Lorem ipsum reviews.
   &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basic stuff, a wrapper to hold the tab navigation and and content. Now, let's start sprinkling in alpine.js - you're going to love how easy the syntax is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-data=&lt;/span&gt;&lt;span class="s"&gt;"{ tab: 'description' }"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"tab_wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- The tabs navigation --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;:class=&lt;/span&gt;&lt;span class="s"&gt;"{ 'active': tab === 'description' }"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click.prevent=&lt;/span&gt;&lt;span class="s"&gt;"tab = 'description'"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Description&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;:class=&lt;/span&gt;&lt;span class="s"&gt;"{ 'active': tab === 'reviews' }"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click.prevent=&lt;/span&gt;&lt;span class="s"&gt;"tab = 'reviews'"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Reviews&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To start, we're strapping an &lt;code&gt;x-data&lt;/code&gt; attribute onto the tab wrapper that contains all of our tab navigation and content. We're using a plain ol' Javascript object for its content. Defining a key of &lt;code&gt;tab&lt;/code&gt; and giving it a string value of &lt;code&gt;description&lt;/code&gt;. This is how we will tell our tab which one we want activate on the initial page load.&lt;/p&gt;

&lt;p&gt;Next, we're strapping 2 attributes onto each navigation link. First, we're leveraging alpine's easy class handling and telling it that we want a class of &lt;code&gt;active&lt;/code&gt; applied to the element any time the tab is equal to the string value representation of that tab. We want our users able to see which tab they're currently viewing. Next, we're essentially attaching a click event listener that says, "when this anchor tag is clicked, change the value of tab to &lt;code&gt;X&lt;/code&gt;." Take note, we're adding &lt;code&gt;.prevent&lt;/code&gt; to the click listener. That is the same as using &lt;code&gt;preventDefault()&lt;/code&gt; inside the event listener. We do this just so we're not adding a hash to the url bar.&lt;/p&gt;

&lt;p&gt;Let's wire up the tab content to make it actually work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-show=&lt;/span&gt;&lt;span class="s"&gt;"tab === 'description'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   Lorem ipsum description.
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-show=&lt;/span&gt;&lt;span class="s"&gt;"tab === 'reviews'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   Lorem ipsum reviews.
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Bam&lt;/em&gt;. That's all we needed to add to wire up our content to show when it is triggered. Crazy simple, right?! We didn't even have to write any custom CSS or apply classes. We're just telling the element to show itself when the value of &lt;code&gt;tab&lt;/code&gt; matches its corresponding string value.&lt;/p&gt;

&lt;p&gt;This will all work perfectly fine, but what if we wanted to make our tabs share-friendly? Naturally, if you were reading the reviews on the product and wanted to send the link to your friend, they would load the page and have the description tab open by default. What if we could make the tabs SEO and share friendly?&lt;/p&gt;

&lt;p&gt;It's pretty easy to do using the browser's native anchor tags and it doesn't require changing very much of our code, either. Let's start by making the default tag dynamic based on the anchor tag in the url.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-data=&lt;/span&gt;&lt;span class="s"&gt;"{ tab: window.location.hash ? window.location.hash.substring(1) : 'description' }"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"tab_wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since alpine.js works wonderfully with vanilla Javascript, we'll simply use Javascript's native &lt;code&gt;window.location.hash&lt;/code&gt; to grab the hash out of the url. We'll approach it by using a ternary operator. If you're not familiar with what a ternary operator is, it's just a condensed if/else statement where the first part is the truth test (window.location.hash), the second part, which is followed by a &lt;code&gt;?&lt;/code&gt;, the if-true code, and the last part, followed by &lt;code&gt;:&lt;/code&gt; which is the if-false code. tl;dr: if there's a hash, grab the hash and chop off the first character (.substring(1)) because we don't want the full hash (#description). Otherwise, simply default to the description tag.&lt;/p&gt;

&lt;p&gt;Next, all we need to do is adjust the click event listener on each navigation link to push the tab hash into the url.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;:class=&lt;/span&gt;&lt;span class="s"&gt;"{ 'active': tab === 'description' }"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click.prevent=&lt;/span&gt;&lt;span class="s"&gt;"tab = 'description'; window.location.hash = 'description'"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Description&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;:class=&lt;/span&gt;&lt;span class="s"&gt;"{ 'active': tab === 'reviews' }"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click.prevent=&lt;/span&gt;&lt;span class="s"&gt;"tab = 'reviews'; window.location.hash = 'reviews'"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Reviews&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all there is to it! Now our tabs will change the url bar and respect the anchor tag within the link. Super SEO and share friendly. And that's just scratching the surface of what alpine.js is capable of doing.&lt;/p&gt;

&lt;p&gt;Here's the complete code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-data=&lt;/span&gt;&lt;span class="s"&gt;"{ tab: window.location.hash ? window.location.hash.substring(1) : 'description' }"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"tab_wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- The tabs navigation --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;:class=&lt;/span&gt;&lt;span class="s"&gt;"{ 'active': tab === 'description' }"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click.prevent=&lt;/span&gt;&lt;span class="s"&gt;"tab = 'description'; window.location.hash = 'description'"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Description&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;:class=&lt;/span&gt;&lt;span class="s"&gt;"{ 'active': tab === 'reviews' }"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click.prevent=&lt;/span&gt;&lt;span class="s"&gt;"tab = 'reviews'; window.location.hash = 'reviews'"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Reviews&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- The tabs content --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-show=&lt;/span&gt;&lt;span class="s"&gt;"tab === 'description'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Lorem ipsum description.
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-show=&lt;/span&gt;&lt;span class="s"&gt;"tab === 'reviews'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Lorem ipsum reviews.
   &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of the best things about alpine.js is that you can copy and paste this code several times on the same page and each section will continue functioning independently because the &lt;code&gt;tab&lt;/code&gt; data is scoped to the wrapper container. This makes alpine.js extremely powerful and component-based.&lt;/p&gt;

&lt;p&gt;Give it a try. I highly recommend you check out the full &lt;a href="https://github.com/alpinejs/alpine"&gt;alpine.js documentation over on Github&lt;/a&gt; and consider using it in your next project.&lt;/p&gt;

</description>
      <category>alpinejs</category>
      <category>tabs</category>
    </item>
    <item>
      <title>A beginner's introduction to REST</title>
      <dc:creator>Mark Townsend</dc:creator>
      <pubDate>Tue, 12 May 2020 20:19:09 +0000</pubDate>
      <link>https://dev.to/mtownsend5512/a-beginner-s-introduction-to-rest-4ffd</link>
      <guid>https://dev.to/mtownsend5512/a-beginner-s-introduction-to-rest-4ffd</guid>
      <description>&lt;p&gt;You're breaking into web development, terms are flying and your head is spinning. &lt;em&gt;What's a REST API?!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For some of the younger generation who are reading this, believe it or not, there was a time when web development was a lot more simple. Write some HTML, tell the user, "welcome to my site!" and copy and paste a visitor counter - &lt;strong&gt;done&lt;/strong&gt; 🎉&lt;/p&gt;

&lt;p&gt;Now-a-days web sites need to do more than just connect to a database and display information. They need to connect to other web apps to display information, sync information and perform a myriad of automated and supplementary tasks. It's inevitable, so let's just say it: you need to learn how REST APIs work so you can create and consume them.&lt;/p&gt;

&lt;p&gt;If you're just being introduced to REST this post is for you.&lt;/p&gt;

&lt;p&gt;Web development practices, including REST, are like politics: there are some elements that everyone can agree on and a lot that are contested and debated. Usually these cordial disagreements end up looking like this...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hj-IxY4p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4k4i179e0eckbvam6zv3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hj-IxY4p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4k4i179e0eckbvam6zv3.png" alt="Two developers calmly discussing their contrary opinions on API versioning"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;...Yeah, developers are a special kind of gregarious folk - but we mean well.&lt;/p&gt;

&lt;p&gt;I'm not going to delve deeply into the theological aspects of REST here, because this is meant to give you a simple look at some of the most basic principles of REST APIs. So, let's get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTP methods
&lt;/h2&gt;

&lt;p&gt;You can't really begin to understand RESTful architecture until you know your HTTP methods. Let's start with the fab-five. These 5 HTTP methods are the most commonly used in modern day web apps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GET&lt;/strong&gt; - Show me information (a list of products, a specific blog post).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;POST&lt;/strong&gt; - Create a new resource (order, register a user).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PUT&lt;/strong&gt; - A method that will either &lt;strong&gt;create&lt;/strong&gt; a new resource or &lt;strong&gt;update&lt;/strong&gt; an existing resource. It should contain &lt;em&gt;all&lt;/em&gt; of a resource's required fields. I.e. if your user resource has 8 required fields, all 8 need to be present.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PATCH&lt;/strong&gt; - When you need to update a resource, but are not providing &lt;em&gt;all&lt;/em&gt; of the resource's required fields. This HTTP method will always be targeting an existing resource where you know the identifier (/users/&lt;strong&gt;1482&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DELETE&lt;/strong&gt; - When you want to delete a resource entirely.&lt;/p&gt;

&lt;p&gt;Now, the straggling four. These HTTP methods are not always used in many web APIs, but they do exist and you should know what they do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OPTIONS&lt;/strong&gt; - This method is useful for telling developers what HTTP methods a given resource supports. Say I want to know what I can do with the /products endpoint. I send an OPTIONS request and the endpoint will basically tell me, "you can retrieve a list of products ([GET] /products) or a specific product ([GET] /products/32) and update a product ([PATCH] /products/42)". Now I know that resource does not support creating (POST) or deleting (DELETE) products. Responses to this method are not to be cached.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HEAD&lt;/strong&gt; - This method is very close to a GET request but with one clear difference: there is no request body returned. If you made a GET request to /products and a HEAD request to /products, the GET request would return the normal response headers plus a response body containing products. The HEAD request would only return the response headers with no response body.&lt;/p&gt;

&lt;p&gt;Why is HEAD useful? There's a few reasons. First, you might want to check if the resource exists before trying to do anything with it. Second, you might want to check the resource's cache state, which should be returned in the response headers. HEAD is perfect for these use cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CONNECT&lt;/strong&gt; - This method establishes a connection between the client and host, usually for the purposes of creating an SSL tunnel. Usually this method does not contain any data in the request body or response body and favors simply telling whether the connection was successful or not in the response header.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TRACE&lt;/strong&gt; - This method is used primarily during debugging purposes. It will return the trail of where the HTTP request was sent which could include redirects by the server. This is probably the least used HTTP method of the list.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does REST stand for?
&lt;/h2&gt;

&lt;p&gt;Quite simply: Representational State Transfer. Got that? It's okay, nobody does when they first hear it.&lt;/p&gt;

&lt;p&gt;REST was coined by Roy Fielding in 2000 when he wrote his doctoral dissertation. It was not without good reason that it was picked up and achieved wide range adoption across the web. Previously, developers were stuck with architectural patterns like SOAP &lt;em&gt;(gag)&lt;/em&gt;, which was &lt;strong&gt;not at all&lt;/strong&gt; simple. The web was the wild west. Roy advocated that developers were not utilizing HTTP as it was originally intended and had a solution: REST.&lt;/p&gt;

&lt;p&gt;You see, REST has a few core concepts. We won't take time to review them all, but here's a few important ones:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Statelessness&lt;/strong&gt; - When you're making API calls (requests) to the server, it doesn't save anything about who you are. Every request you make requires you to provide your authentication information. Usually that comes in the form of tokens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proper request methods&lt;/strong&gt; - In some obvious circumstances the HTTP method you choose to make an API call is not up for debate. If you want to be shown information you use GET. If you want to create information you use POST. You would &lt;em&gt;never&lt;/em&gt; perform an action that updates or creates a resource using a GET request.&lt;/p&gt;

&lt;p&gt;I once visited a web site while logged in and clicked a link in my dashboard only to be surprised that my account was upgraded to a trial of their premium plan. &lt;strong&gt;WHAT?&lt;/strong&gt; The concept is easy to understand. I clicked a link expecting to see some information and instead they modified my account. The same is true in REST. The GET HTTP method means, "show me information" nothing else.&lt;/p&gt;

&lt;p&gt;GET, HEAD, OPTIONS and TRACE are &lt;em&gt;never&lt;/em&gt; to be used in directly changing the resource on the server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Separation of concerns&lt;/strong&gt; - The client (you, or your web app) has its duties and the server has its own duties. Don't try to force one side to do more than it was intended. Sometimes this means making more separate API calls.&lt;/p&gt;

&lt;p&gt;For example, I may want to retrieve data from a server so I have to make several API calls to the server to traverse the various pages, then perform several more separate update API calls to change the data. Your first instinct might be to say, "why not just lump all of that into one endpoint and make one API call?" It seems easier at first, but you'll quickly realize it creates more problems down the road. Not only that, but you've just forced way more work onto the server and absolved the client of its responsibilities to interpret and handle the data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caching&lt;/strong&gt; - It's entirely reasonable to expect the client (your site or person) to cache the data it receives from an API. It's also equally reasonable for the server to cache its response in the interest of speed. Usually the freshness of data is communicated from the server through response headers so your application can know if the response is cached and when it expires. There are also ways for the client to request the server not to return a cached response and fetch fresh data, but the means varies per API application.&lt;/p&gt;

&lt;h2&gt;
  
  
  A few things I wish somebody told me when I was starting
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;When working with RESTful APIs, request methods are not a concrete one-to-one connection with your code. REST is a way of thinking which shapes the way you structure and write your code.  Your programming language may or may not go out of its way to make that clear to beginners.&lt;/p&gt;

&lt;p&gt;My first introduction to REST APIs came by way of PHP. I read through the various HTTP methods and got confused, "why is there no super global corresponding to &lt;code&gt;$_PUT&lt;/code&gt;, &lt;code&gt;$_PATCH&lt;/code&gt; and &lt;code&gt;$_DELETE&lt;/code&gt;?!" The answer is rather obvious now, looking back. GET, POST, PUT, PATCH and DELETE are HTTP methods. It's up to you to find out how your language will allow you to read and send using these HTTP methods. The good news is that many language frameworks handle this for you and make it easy.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You need to start viewing your data as resources. A resource can be anything. Here are some examples of resources: users, orders, products, posts, order statuses, tracking numbers, etc. You get the idea, right?&lt;/p&gt;

&lt;p&gt;Now that you're seeing data as a resource, you should be thinking about what operations can be applied to a resource. Say my resource is books:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get a list of books:&lt;/strong&gt; [GET] /books/&lt;br&gt;
  &lt;strong&gt;Get a specific book:&lt;/strong&gt; [GET] /books/13&lt;br&gt;
  &lt;strong&gt;Create a book:&lt;/strong&gt; [POST] /books/&lt;br&gt;
  &lt;strong&gt;Update some of the books attributes:&lt;/strong&gt; [PATCH] /books/13&lt;br&gt;
  &lt;strong&gt;Update every attribute of the book:&lt;/strong&gt; [PUT] /books/13&lt;br&gt;
  &lt;strong&gt;Delete the book:&lt;/strong&gt; [DELETE] /books/13&lt;/p&gt;

&lt;p&gt;Notice how the url structure didn't really change much between the operations? The difference was the HTTP method! My API was reading the method and deciding what to do with the resource based on the method and the data supplied (if any). &lt;em&gt;That's a RESTful approach!&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Separate resources are the goal. For example, let's say you were designing an API that had countries and their states/provinces. When a request was made for the United States should I send back all 50 states that belong to it? You could, but I would recommend separating them. Make one API call to get the country ([GET] /countries/US), take the country's &lt;code&gt;id&lt;/code&gt; and make a separate API call to get the states ([GET] /states?country_id=1).&lt;/p&gt;

&lt;p&gt;You're going to find that as your API grows the separation of resources will make it easier to maintain. Does this means you never want to group resources and their children in API calls? No, not necessarily. But don't let your default answer be, "yes" without thinking about it. By making your resources separate you are introducing simplicity into your API and thats a &lt;strong&gt;good&lt;/strong&gt; thing. Each resource endpoint now only needs to worry about itself instead of its children resources.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When creating your REST API make a determination to follow best practices and stick with it consistently. Here are a few examples:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use JSON for your request body. It is simpler and smaller compared to XML.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use nouns instead of verbs for your uris. Your HTTP methods should be specifying what the operation does, not the url.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Correct:&lt;/strong&gt; [POST] /books&lt;br&gt;
&lt;strong&gt;Incorrect:&lt;/strong&gt; [POST] /add-books&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use plural names for your resource uris. E.g. /products or /products/my-product-slug. Many well-established APIs follow the plural rule because the base endpoint indicates you want a list of that resource.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use resource nesting to denote ownership - but don't go crazy with deep nesting. Keep it at 2, maybe 3 at the most. E.g. /users/1482/orders - will give me all of user 1482's orders.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Standardize your error messages. Nobody wants to work with an API that gives vague errors or sometimes shows errors in the response headers while other times showing errors in the response body.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Now you know the basics and some best practices
&lt;/h2&gt;

&lt;p&gt;REST really isn't that hard to grasp. I think a lot of people just struggle separating the principles from the implementation. I hope you found this article useful in helping you begin to grasp the basic concepts of REST - I know I wish someone had told me these things when I was getting started.&lt;/p&gt;

&lt;p&gt;Did you learn anything valuable? Did I miss anything? Let me know down in the comments. Thanks for reading!&lt;/p&gt;

</description>
      <category>api</category>
      <category>rest</category>
      <category>httpmethods</category>
      <category>resources</category>
    </item>
    <item>
      <title>Learning Javascript, barcode scanning and wiggling into private grocery store APIs</title>
      <dc:creator>Mark Townsend</dc:creator>
      <pubDate>Sat, 06 Apr 2019 03:38:46 +0000</pubDate>
      <link>https://dev.to/mtownsend5512/learning-javascript-barcode-scanning-and-wiggling-into-private-grocery-store-apis-30bh</link>
      <guid>https://dev.to/mtownsend5512/learning-javascript-barcode-scanning-and-wiggling-into-private-grocery-store-apis-30bh</guid>
      <description>&lt;p&gt;I've been a backend web developer for several years now. Swimming in the waters of databases, object oriented programming, and beautiful frameworks like Laravel to create some fairly robust web software for the company I work for, DieselCore. But here I was, realizing that learning a new programming language was well overdue. Sure, I had worked with Javascript plugins and packages before, I considered myself to be capable enough to figure the implementation part out even if I didn't really understand the language itself.&lt;/p&gt;

&lt;p&gt;I bit the bullet and signed up for &lt;a href="https://twitter.com/wesbos" rel="noopener noreferrer"&gt;Wes Bos&lt;/a&gt;' free 30 day javascript course, &lt;a href="https://javascript30.com" rel="noopener noreferrer"&gt;Javascript 30&lt;/a&gt;. From the day I write this post to when I signed up has been exactly 14 days, and I'm still only halfway through. That has roughly been the extent of my javascript training, in truth, with only a few other small introductions to the language in passing.&lt;/p&gt;

&lt;p&gt;That's why I feel proud to have created what I did in such a short time frame. A working web application that scans barcodes, talks to the grocery chain's non-public API, adds products to a list, calculates the total and factors in sales tax.&lt;/p&gt;

&lt;h4&gt;
  
  
  First-world problems
&lt;/h4&gt;

&lt;p&gt;Let me back up a few steps to a couple of days ago. My wife, Kaitlynn, and I are having dinner while our kids play and she drops a real first-world problem on me: "I &lt;em&gt;hate&lt;/em&gt; going to &lt;a href="https://www.heb.com" rel="noopener noreferrer"&gt;HEB&lt;/a&gt; (our local grocery store) because I can never tell what our total will be until I'm at the checkout. By then I may decide something in my basket isn't worth it." I grin, "oh, that's terrible." She laughs and continues, "but if I use the app to order, I feel cheated because we end up focusing on the price and eating the same food over and over. I just miss out on the experience to see new foods that spark fresh meal ideas if I'm not there."&lt;/p&gt;

&lt;p&gt;I'll admit, I have a personal love for web APIs, having built several, myself. I begin to think of solutions. "I'm sure there's an app for what you want", I say. I look it up, and there is, but it's limited to locations that are nowhere near ours. Even if it's a first-world problem, I like to problem-solve, and my wife is a fantastic homemaker. If I can make her work even a little bit easier with what I do, I'm hooked.&lt;/p&gt;

&lt;p&gt;I check out the HEB app and sure enough: they have barcode scanning. I look up a barcode and try it run it through HEB's web site search - no dice, UPC codes don't return results. But I &lt;strong&gt;know&lt;/strong&gt; there is an endpoint out there serving up product results for UPC codes. But how do I find out where it is?&lt;/p&gt;

&lt;h4&gt;
  
  
  A clever solution
&lt;/h4&gt;

&lt;p&gt;I remember something my father-in-law always talked about: WiFi honeypots. With the right software you can view network traffic. So, I hatched a plan. I would monitor my home network traffic while using the app and try to catch the outgoing API call.&lt;/p&gt;

&lt;p&gt;After awhile I finally settled on the simplest solution: an iOS app called &lt;a href="https://itunes.apple.com/us/app/thor-http-sniffer-capture/id1210562295?mt=8" rel="noopener noreferrer"&gt;Thor&lt;/a&gt;. I started the network sniffer, opened the app and scanned. I crossed my fingers and began scrolling through the network logs hoping I would strike gold.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fb4928Mp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fb4928Mp.png" title="Thor, an iOS network sniffer helped me discover HEB's api endpoint" alt="alt text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bingo!&lt;/strong&gt; I had an endpoint, and more importantly, I had an api key. This was starting to look like a real possibility now.&lt;/p&gt;

&lt;p&gt;I made a few test calls and it worked perfectly. I got to work building the interface. I opted to use the fantastic CSS framework, &lt;a href="https://tailwindcss.com" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt;, for my UI. I hooked up all the Javascript components using what I had learned in the prior 14 days and things started looking good.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FfD2whud.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FfD2whud.png" title="Tailwind CSS and FontAwesome create some effortlessly stunning web apps" alt="alt text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I did encounter some issues while attempting to utilize Javascript's &lt;code&gt;fetch&lt;/code&gt; function, namely &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" rel="noopener noreferrer"&gt;CORs&lt;/a&gt; errors. So, for the remote API calls I whipped up a quick PHP script to send cURL calls on behalf of my javascript application. That was the extent of PHP's involvement in this project!&lt;/p&gt;

&lt;h4&gt;
  
  
  Smile for the camera
&lt;/h4&gt;

&lt;p&gt;Now all I had to do was find a way to use a device's camera to convert a barcode and pass it to the API. I had been avoiding this part of the project as much as possible because I knew the limitations of my knowledge well enough to know barcode scanning was more advanced than I could handle right now.&lt;/p&gt;

&lt;p&gt;Luckily, there are some really solid JS frameworks out there that handle this very thing. After some digging I found &lt;a href="https://serratus.github.io/quaggaJS" rel="noopener noreferrer"&gt;Quagga JS&lt;/a&gt;. I had a little bit of trouble getting the configuration right, mainly because the examples they provided didn't 100% match the source code they used, but eventually I got it working.&lt;/p&gt;

&lt;p&gt;Here's a fun fact for anyone who has never worked with a device's camera permissions with Javascript: &lt;strong&gt;You need an SSL certificate&lt;/strong&gt;. The browser won't even request access if your script is running on an unprotected protocol. For me, fixing this problem was a cinch because I use &lt;a href="https://laravel.com/docs/5.8/valet" rel="noopener noreferrer"&gt;Valet&lt;/a&gt; to run my local development server. &lt;code&gt;valet secure &amp;lt;folder&amp;gt;&lt;/code&gt; and done. I was running https protocol. It was just that easy.&lt;/p&gt;

&lt;p&gt;In less than 2 full days I had a working beta version of the web app which promised to cure my wife's shopping woes. See for yourself. Click the image below to watch it in action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vimeo.com/328779418" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiu9c11p396rgxzw5dbh3.jpg" alt="View the HEB barcode scanning web app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for taking the time to share in my story. If you're a developer, I hope this inspires you to go out and make something great.&lt;/p&gt;

&lt;h4&gt;
  
  
  Technologies used
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://itunes.apple.com/us/app/thor-http-sniffer-capture/id1210562295?mt=8" rel="noopener noreferrer"&gt;Thor&lt;/a&gt; - iOS network sniffer app&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://tailwindcss.com" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt; - css framework for ui development&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fontawesome.com/" rel="noopener noreferrer"&gt;Font Awesome&lt;/a&gt; - icons&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://serratus.github.io/quaggaJS" rel="noopener noreferrer"&gt;Quagga JS&lt;/a&gt; - camera access and barcode scanning&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://laravel.com/docs/5.8/valet" rel="noopener noreferrer"&gt;Valet&lt;/a&gt; - local web server w/ ssl support&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.heb.com" rel="noopener noreferrer"&gt;HEB&lt;/a&gt; - for their kind understanding that I borrowed their api 😅&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  By the way...
&lt;/h4&gt;

&lt;p&gt;HEB, if you're reading this, I'm open for contract web development work, I'm great with APIs 😉&lt;/p&gt;

&lt;h5&gt;
  
  
  I love to share
&lt;/h5&gt;

&lt;p&gt;I have several free open source packages available for download on my &lt;a href="https://github.com/mtownsend5512?tab=repositories" rel="noopener noreferrer"&gt;Github profile&lt;/a&gt;. Check them out and see if any of them can save you time in your next project!&lt;/p&gt;

</description>
      <category>api</category>
      <category>javascript</category>
      <category>barcodescanner</category>
      <category>networksniffer</category>
    </item>
  </channel>
</rss>
