<?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>Forem: Hiram</title>
    <description>The latest articles on Forem by Hiram (@eichgi).</description>
    <link>https://forem.com/eichgi</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%2F87036%2F26b754ea-5bc0-442b-94ab-d344a548b5ba.jpeg</url>
      <title>Forem: Hiram</title>
      <link>https://forem.com/eichgi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/eichgi"/>
    <language>en</language>
    <item>
      <title>Next.js middleware for supabase</title>
      <dc:creator>Hiram</dc:creator>
      <pubDate>Wed, 29 Mar 2023 15:10:51 +0000</pubDate>
      <link>https://forem.com/eichgi/nextjs-middleware-for-supabase-34f8</link>
      <guid>https://forem.com/eichgi/nextjs-middleware-for-supabase-34f8</guid>
      <description>&lt;p&gt;Mein kumpeln! It is time for another interesting snippet for Next.js v13. Lately I've been tinkering with this framework and Supabase and I have to say the integrations this BaaS offers are amazing. You definitely need to check out this firebase alternative.&lt;/p&gt;

&lt;p&gt;Now regarding with the snippet I want to share with you it is related with the admin section I am building inside the portal. This allows you to validate the access before rendering the pages or access the api functions Next.js offers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { NextRequest, NextResponse } from "next/server";
import { createMiddlewareSupabaseClient } from "@supabase/auth-helpers-nextjs";

export async function middleware(req: NextRequest) {
  const adminPath = "/admin";
  const apiAdminPath = "/api/admin";

  const res = NextResponse.next();
  const supabase = createMiddlewareSupabaseClient({ req, res });
  const {
    data: { session },
  } = await supabase.auth.getSession();

  if (!session || session?.user.user_metadata?.role !== "admin") {
    if (req.nextUrl.pathname.startsWith(apiAdminPath)) {
      return new NextResponse(
        JSON.stringify({ message: "authorization failed" }),
        { status: 403, headers: { "Content-Type": "application/json" } }
      );
    } else if (req.nextUrl.pathname.startsWith(adminPath)) {
      const redirectUrl = req.nextUrl.clone();
      redirectUrl.pathname = "/";
      return NextResponse.redirect(redirectUrl);
    }
  }
}

export const config = {
  matcher: ["/api/admin/:path*", "/admin/:path*"],
};

// src/middleware.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You probably got an idea of what is happening here, but let's explain it step by step:&lt;/p&gt;

&lt;p&gt;The matchers aka the folders (or pages/api): These are the routes on which this middleware is going to be applied. Any other routes under /user or /whatever paths are not going to be filtered. So you have the ability to define validations for every (sub)path you might have.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const config = {
  matcher: ["/api/admin/:path*", "/admin/:path*"],
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The validation, in my case I leveraged it from supabase and its helper library to retrieve the active session as shown here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const supabase = createMiddlewareSupabaseClient({ req, res });
  const {
    data: { session },
  } = await supabase.auth.getSession();

  if (!session || session?.user.user_metadata?.role !== "admin") {
    // put your validations here  
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is something you can easily swap with your custom auth provider.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The next() handler&lt;/strong&gt;, known as the next step in the middleware flow, here we are asking the type of route in order to response with a redirect or a json.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    if (req.nextUrl.pathname.startsWith(apiAdminPath)) {
          return new NextResponse(
            JSON.stringify({ message: "authorization failed" }),
            { status: 403, headers: { "Content-Type": "application/json" } }
      );
    } else if (req.nextUrl.pathname.startsWith(adminPath)) {
      const redirectUrl = req.nextUrl.clone();
      redirectUrl.pathname = "/";
      return NextResponse.redirect(redirectUrl);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is important to highlight that I am responding explicitly to what is causing failed validations. Redirect for browser navigation and JSON for API routes. The next() valid step is implicit when you are not returning it by using the res variable. By adding the return res; statement at the end of the function you will cause the same flow as if not defined.&lt;/p&gt;

&lt;p&gt;As usual, you should stick to the official docs, for next.js you have it here: &lt;a href="https://nextjs.org/docs/advanced-features/middleware"&gt;https://nextjs.org/docs/advanced-features/middleware&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For supabase and its helper library just follow this link: &lt;a href="https://supabase.com/docs/guides/auth/auth-helpers/nextjs"&gt;https://supabase.com/docs/guides/auth/auth-helpers/nextjs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope you find it useful and don't hesitate to leave your comments, happy coding pals! 😎&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>nextjs</category>
      <category>middleware</category>
    </item>
    <item>
      <title>Vue 3 is amazing!</title>
      <dc:creator>Hiram</dc:creator>
      <pubDate>Thu, 14 Jul 2022 17:06:16 +0000</pubDate>
      <link>https://forem.com/eichgi/vue-3-is-amazing-4df7</link>
      <guid>https://forem.com/eichgi/vue-3-is-amazing-4df7</guid>
      <description>&lt;p&gt;Guys, recently I had to take a &lt;a href="https://www.udemy.com/course/vue-js-3-composition-api/"&gt;Vue 3 course&lt;/a&gt; so I can start working with composition API among other tools and I had to say Vue is always surprising me. I liked the way you can build up your components with the composition API. I also got to know Pinia, the new state management for Vue and is amazing as well. Vuex was already easy to integrate and use but with Pinia is mindblowing 🤯.&lt;/p&gt;

&lt;p&gt;I think it is good you have the options and composition Apis available. I hope I can share more insights once I get used to work with the new API, for sure it should have caveats we should be aware of. Feel free to make suggestions or share your thoughts about Vue3.&lt;/p&gt;

&lt;p&gt;p.d. vue mugiwara's jolly roger looks terrific &lt;/p&gt;

</description>
      <category>vue3</category>
      <category>pinia</category>
      <category>compositionapi</category>
    </item>
    <item>
      <title>Getting ready for new ventures</title>
      <dc:creator>Hiram</dc:creator>
      <pubDate>Tue, 01 Feb 2022 06:06:34 +0000</pubDate>
      <link>https://forem.com/eichgi/getting-ready-for-new-ventures-25cg</link>
      <guid>https://forem.com/eichgi/getting-ready-for-new-ventures-25cg</guid>
      <description>&lt;p&gt;I remember the mix of feelings when I launched my first project. I made what I considered at the time enough promotion, sadly it didn't take off at the end. First lesson learned, having a decent product it's only the beginning of the journey (flashback ends).&lt;/p&gt;

&lt;p&gt;After more failures than successes, I am still here, getting ready for the the next rodeos. Getting ready for the ups and downs, exhausted at times when you feel your brain is going to explode processing new information. But pushing the boundaries it's always satisfying, the experiments are meant to enlighten you and shed some light to your path.&lt;/p&gt;

&lt;p&gt;If you are a self learner it's very important to stick to this. Sooner than later this process is going to payoff. You don't know when or how, but that's the way everything fits in the future (luck is a cheap description as well). Keep coding, keep doing, keep failing but keep learning.&lt;/p&gt;

&lt;p&gt;Let's hope for the better and get ready for the unknown...&lt;/p&gt;

</description>
      <category>keep</category>
      <category>going</category>
    </item>
    <item>
      <title>Generate server block (virtual hosts) for nginx dynamically</title>
      <dc:creator>Hiram</dc:creator>
      <pubDate>Mon, 17 Jan 2022 22:01:56 +0000</pubDate>
      <link>https://forem.com/eichgi/generate-server-block-virtual-hosts-for-nginx-dynamically-1fpp</link>
      <guid>https://forem.com/eichgi/generate-server-block-virtual-hosts-for-nginx-dynamically-1fpp</guid>
      <description>&lt;p&gt;Hey pals, I will show you how to create a basic shell script so you can dynamically generate nginx virtual hosts or "subdomains" for every site you want to put online. There are two examples, one for your node or whatever app that runs locally, the second is for static websites.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reversed proxy subdomain example
&lt;/h3&gt;

&lt;p&gt;If you are running a node app on a specific port this basic example will help you to create and connect your site. The following is the stub file we will use &lt;code&gt;subdomain.stub&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
        listen 80;
        listen [::]:80;

        index index.html index.htm;

        server_name {{DOMAIN}}.yoursite.com;

        location / {
                proxy_pass http://localhost:{{PORT}};
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection 'upgrade';
                proxy_set_header Host $host;
                proxy_cache_bypass $http_upgrade;
        }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's create the &lt;code&gt;generator.sh&lt;/code&gt; shell script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

SED=$(which sed)
CURRENT_DIR=$(dirname $0)

# check the domain is valid!
PATTERN="^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$"
if [[ "$1" =~ $PATTERN ]]; then
  DOMAIN=$(echo $1 | tr '[A-Z]' '[a-z]')
  echo "Creating hosting for:" $DOMAIN
else
  echo "invalid domain name"
  exit 1
fi

CONFIG="$CURRENT_DIR/$DOMAIN.yoursite.com"
cp $CURRENT_DIR/subdomain.stub $CONFIG
$SED -i "s/{{DOMAIN}}/$DOMAIN/g" $CONFIG
$SED -i "s/{{PORT}}/$2/g" $CONFIG

echo "The subdomain has been successfully generated"

cp $CONFIG "/etc/nginx/sites-available"
ln -s "/etc/nginx/sites-available/$DOMAIN.yoursite.com" "/etc/nginx/sites-enabled"

echo "The subdomain has been moved to nginx to sites-available and symlinked to sites-enabled"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your terminal you run the command as follows: &lt;code&gt;./generator.sh DOMAIN PORT&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In order to run this script you have to make it executable with this command: &lt;code&gt;chmod u+x generator.sh&lt;/code&gt; otherwise you won't be able to execute it.&lt;/p&gt;

&lt;p&gt;Now it's ready to run, let me explain what it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stores &lt;a href="https://www.geeksforgeeks.org/sed-command-in-linux-unix-with-examples/"&gt;SED&lt;/a&gt; as a variable&lt;/li&gt;
&lt;li&gt;Stores the current location &lt;/li&gt;
&lt;li&gt;Creates a domain pattern and the compares it with the 1st parameter (DOMAIN)&lt;/li&gt;
&lt;li&gt;Defines CONFIG location, then clones the subdomain.stub file into the previous definition&lt;/li&gt;
&lt;li&gt;Replaces the &lt;code&gt;{{DOMAIN}}&lt;/code&gt; value inside the cloned configuration&lt;/li&gt;
&lt;li&gt;Replaces the &lt;code&gt;{{PORT}}&lt;/code&gt; value inside the cloned configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point our virtual host is ready to be published&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copies the new virtual host file into "/etc/nginx/sites-available" which is the folder for every available site in nginx&lt;/li&gt;
&lt;li&gt;Then creates a symlink from the previous location to &lt;code&gt;/etc/nginx/sites-enabled&lt;/code&gt; which is the folder for every enabled site in nginx&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once this is done you need to ensure virtual hosts definitions are valid. You can do this by running &lt;code&gt;nginx -t&lt;/code&gt; in your terminal. If everything went well now you only need to reload/restart nginx so changes can be applied. &lt;code&gt;nginx service reload/restart&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Static site subdomain example
&lt;/h3&gt;

&lt;p&gt;This example is for static websites (not SSR)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;subdmain.stub&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
        listen 80;
        listen [::]:80;

        root /var/www/{{DOMAIN}};
        index index.html index.htm index.nginx-debian.html;

        server_name {{DOMAIN}}.yoursite.com;

        location / {
                try_files $uri $uri/ /index.html = 404; #This line redirects to index for correct react router usage
        }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we use basically the same &lt;code&gt;generator.sh&lt;/code&gt; script than before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

SED=$(which sed)
CURRENT_DIR=$(dirname $0)

# check the domain is valid!
PATTERN="^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$"
if [[ "$1" =~ $PATTERN ]]; then
  DOMAIN=$(echo $1 | tr '[A-Z]' '[a-z]')
  echo "Creating hosting for:" $DOMAIN
else
  echo "invalid domain name"
  exit 1
fi

CONFIG="$CURRENT_DIR/$DOMAIN.yoursite.com"
cp $CURRENT_DIR/subdomain.stub $CONFIG
$SED -i "s/{{DOMAIN}}/$DOMAIN/g" $CONFIG

echo "The subdomain has been successfully generated"

cp $CONFIG "/etc/nginx/sites-available"
ln -s "/etc/nginx/sites-available/$DOMAIN.yoursite.com" "/etc/nginx/sites-enabled"

echo "The subdomain has been moved to nginx to sites-available and symlinked to sites-enabled"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only difference is that we no longer require a PORT, so you will run the command like this: &lt;code&gt;./generator.sh ${DOMAIN}&lt;/code&gt; (Do not forget to make the script executable)&lt;/p&gt;

&lt;p&gt;From here the sky is the limit my friend, you could easily create your own stubs/snippets with many variables as needed. Also they only contain the minimum requirements to be served by nginx, have fun adding more specifications.&lt;/p&gt;

&lt;h2&gt;
  
  
  BONUS!! 🤯
&lt;/h2&gt;

&lt;p&gt;If you read my previous post &lt;a href="https://dev.to/eichgi/easy-node-app-deployment-with-pm2-4mpc"&gt;Easy node apps deployment with PM2&lt;/a&gt; let me show you how you could integrate this virtual host generator to a pm2 deployment set up file &lt;code&gt;ecosystem.config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
  deploy: {
    production: {
      user: USER,
      host: HOST,
      ref: BRANCH,
      repo: REPO,
      path: PATH,
      'post-setup': `npm install &amp;amp;&amp;amp; cd setup/ &amp;amp;&amp;amp; chmod u+x generator.sh &amp;amp;&amp;amp; ./generator.sh ${DOMAIN}`
    }
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you only have to create a &lt;code&gt;/setup&lt;/code&gt; folder inside your project with the subdomain stub as well as the generator script and that's it. Ready to be executed after setup.&lt;/p&gt;

</description>
      <category>nginx</category>
      <category>pm2</category>
      <category>devops</category>
    </item>
    <item>
      <title>Easy node apps deployment with PM2</title>
      <dc:creator>Hiram</dc:creator>
      <pubDate>Tue, 11 Jan 2022 20:03:28 +0000</pubDate>
      <link>https://forem.com/eichgi/easy-node-app-deployment-with-pm2-4mpc</link>
      <guid>https://forem.com/eichgi/easy-node-app-deployment-with-pm2-4mpc</guid>
      <description>&lt;p&gt;This is the first post of a serie of helpful snippets to deploy node and react apps.&lt;/p&gt;

&lt;p&gt;For this practice we will use &lt;em&gt;pm2&lt;/em&gt; which is a process manager for node.js&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The following are all the considerations you need to take in order to be able to deploy with pm2:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pm2.keymetrics.io/"&gt;Install pm2 dependency globally&lt;/a&gt; on the host you are running the deployment script &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://linuxhandbook.com/add-ssh-public-key-to-server/"&gt;Add your host's public SSH key into the remote server&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.github.com/es/developers/overview/managing-deploy-keys"&gt;Manage the connection to your repository&lt;/a&gt; from the remote server&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following is the actual code you will have to define in order to &lt;em&gt;install&lt;/em&gt; the node app on your remote server, PM2 calls it &lt;code&gt;ecosystem.config.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
  apps : [],
  // Deployment Configuration
  deploy : {
    production : {
       "user" : "root",
       "host" : ["my-remote-server.xyz", "...",],
       "ref"  : "origin/master",
       "repo" : "git@github.com:username/repository.git",
       "path" : "/var/www/my-repository",
       "post-setup" : "npm install"
    }
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you defined this ecosystem file you should be able to run it using the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pm2 deploy ecosystem.config.js production setup&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The previous command will download the &lt;em&gt;ref&lt;/em&gt; of your &lt;em&gt;repo&lt;/em&gt; into the specified &lt;em&gt;path&lt;/em&gt;, it will run as &lt;em&gt;user&lt;/em&gt; on the specified &lt;em&gt;host(s)&lt;/em&gt;, and finally it will run the &lt;em&gt;post-setup&lt;/em&gt; commands.&lt;/p&gt;

&lt;p&gt;And that's it, now you can install your projects wherever you need. There exists a &lt;em&gt;pre-setup&lt;/em&gt; command as well in case you need to make some actions before deployment. &lt;a href="https://pm2.keymetrics.io/docs/usage/deployment/"&gt;Here is the official docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;pm2 has many more features that we will discuss in next posts. Also, this script is not limited to node apps only, coming on examples will show you how to deploy a react app from deployment to serve, setting env parameters and building new releases after PR's using Github Actions, basically achieving zero downtime deployments pipelines.&lt;/p&gt;

</description>
      <category>node</category>
      <category>pm2</category>
      <category>deployment</category>
    </item>
    <item>
      <title>React FullCalendar snippet</title>
      <dc:creator>Hiram</dc:creator>
      <pubDate>Mon, 29 Nov 2021 02:58:15 +0000</pubDate>
      <link>https://forem.com/eichgi/react-fullcalendar-snippet-ka3</link>
      <guid>https://forem.com/eichgi/react-fullcalendar-snippet-ka3</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This was originally published on &lt;a href="https://eichgi.hashnode.dev/react-fullcalendar-snippet"&gt;https://eichgi.hashnode.dev/react-fullcalendar-snippet&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hey folks, the following snippet is a basic example of what you can achieve with FullCalendar library for React. I hope you find my appointments calendar interesting, so let's dive into.&lt;/p&gt;

&lt;p&gt;Here you have the react component full calendar docs: &lt;br&gt;
 &lt;a href="https://fullcalendar.io/docs/react"&gt;https://fullcalendar.io/docs/react&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Once you have installed the package let's focus on the component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;FullCalendar
              ref={calendar}
              fixedWeekCount={false}
              height={'auto'}
              locale={esLocale}
              plugins={[dayGridPlugin, interactionPlugin]}
              initialView={setCalendarViewByWidth()}
              headerToolbar={{
                start: 'prev today next',
                center: 'title',
                end: 'newAppointment'
              }}
              footerToolbar={{
                center: 'toggleMonth toggleWeek toggleDay',
              }}
              customButtons={{
                newAppointment: {
                  text: 'Nueva cita',
                  click: () =&amp;gt; {
                    dateClickHandler();
                  },
                },
                toggleDay: {
                  text: 'Hoy',
                  click: () =&amp;gt; {
                    calendar.current.getApi().changeView('dayGridDay');
                  }
                },
                toggleWeek: {
                  text: 'Semana',
                  click: () =&amp;gt; {
                    calendar.current.getApi().changeView('dayGridWeek');
                  }
                },
                toggleMonth: {
                  text: 'Mes',
                  click: () =&amp;gt; {
                    calendar.current.getApi().changeView('dayGridMonth')
                  }
                },
              }}
              dateClick={e =&amp;gt; dateClickHandler(e)}
              events={appointments}
              datesSet={async (dateInfo) =&amp;gt; {
                await getEvents(dateInfo.startStr.split('T')[0], dateInfo.endStr.split('T')[0]);
              }}
              eventsSet={(events =&amp;gt; {
                console.log('Events set: ', events);
              })}
              eventClick={e =&amp;gt; eventsHandler(e)}
            /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I will describe the props described in the snippet. These are the very basics functionalities you might need for a full dynamic calendar.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make a reference for you calendar, it might be handy for working directly with the API
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const calendar = useRef(null);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;I set my calendar in spanish by doing this, there are plenty of languages available, just dig into the docs to find the desired one.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import esLocale from '@fullcalendar/core/locales/es';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In order to interact with the events and have this dayGrid/monthGrid view it is important that you import the following plugins
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import dayGridPlugin from '@fullcalendar/daygrid';
import interactionPlugin from '@fullcalendar/interaction';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;You can customize the default buttons and their order by defining the following props. You can as well create your own buttons and defined them inside the toolbar as follows:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;headerToolbar={{
                start: 'prev today next',
                center: 'title',
                end: 'newAppointment'
              }}
footerToolbar={{
                center: 'toggleMonth toggleWeek toggleDay',
              }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;As mentioned previously, this is the way you define custom buttons and their events:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customButtons={{
                newAppointment: {
                  text: 'Nueva cita',
                  click: () =&amp;gt; {
                    dateClickHandler();
                  },
                },
               ...
              }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;For every click inside the calendar you will set the event this way (where &lt;em&gt;e&lt;/em&gt; contains the date information regarding the clicked date):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dateClick={e =&amp;gt; dateClickHandler(e)}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;You place events into the calendar defining them with this prop:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;events={[
    { title: 'event 1', date: '2019-04-01' },
    { title: 'event 2', date: '2019-04-02' }
  ]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;When you need to know which are the dates the calendar is currently showing define the following prop:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;datesSet={async (dateInfo) =&amp;gt; {
                await getEvents(dateInfo.startStr.split('T')[0], dateInfo.endStr.split('T')[0]);
              }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every time you change the view you can request events from the backend like this. (Don't forget to create your own getEvents definition)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Now that you have events placed, you might need to interact with them for showing or modifying purposes. This props is handy when you need to access the event information:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eventClick={e =&amp;gt; eventsHandler(e)}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here you have it, simple react fullcalendar snippet. There are plenty of options in the docs so you can customize your own calendar. CSS, Events, formats, etc... you will find them here:  &lt;a href="https://fullcalendar.io/docs#toc"&gt;https://fullcalendar.io/docs#toc&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>fullcalendar</category>
    </item>
    <item>
      <title>Laravel Beyond CRUD IMO</title>
      <dc:creator>Hiram</dc:creator>
      <pubDate>Thu, 02 Sep 2021 20:15:47 +0000</pubDate>
      <link>https://forem.com/eichgi/laravel-beyond-crud-imo-5dc3</link>
      <guid>https://forem.com/eichgi/laravel-beyond-crud-imo-5dc3</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post was originally published on &lt;a href="https://blog.eichgi.com/laravel-beyond-crud-imo"&gt;https://blog.eichgi.com/laravel-beyond-crud-imo&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Folks, today's opinion is about the Laravel Beyond CRUD course published by Spatie. As Freek said, if you work with Laravel chances are you are using one of their many libraries for the Laravel ecosystem.&lt;/p&gt;

&lt;p&gt;Link to the course: laravel-beyond-crud.com&lt;/p&gt;

&lt;p&gt;This is a video series &amp;amp; book that provides a mindset when it comes to work with large projects using the Laravel Framework. The entry point is related with Domain Driven Design (DDD) and how could it be applied into the Laravel context. I expected to have some code examples in order to see how the wiring up needs to be modified when connecting the domains modularization and laravel's inner functionality. At the end it's a good overview.&lt;/p&gt;

&lt;p&gt;The next episode shows what a Data Transfer Object (DTO) is and its advantages. Having defined a known data structure for data transfer helps to handle communication; a common case is data storage from the request to the controller. Instead of grabbing data manually, you are able to curate it or transform it before its final usage. And testing might get easier given you always know what to provide and expect.&lt;/p&gt;

&lt;p&gt;In order to not disclose or diving deeper into the course I will stop here. But the following are some points and it's a part of what you can find in this course:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keeping model classes small and clean&lt;/li&gt;
&lt;li&gt;Structuring complex queries&lt;/li&gt;
&lt;li&gt; Passing data around in a structured way using Data Transfer Objects&lt;/li&gt;
&lt;li&gt;How to make code reusable by using actions&lt;/li&gt;
&lt;li&gt;Improving code clarity by using meaningful names&lt;/li&gt;
&lt;li&gt;Keep controllers light by using view models&lt;/li&gt;
&lt;li&gt;Adding behaviour to collections&lt;/li&gt;
&lt;li&gt;Testing actions, DTOs and model-related classes&lt;/li&gt;
&lt;li&gt;Using enhanced test factories to seed data for every scenario&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I believe this is a great content whereas you are becoming proficient with Laravel or if you wishes to know the advanced topics. As mentioned previously I'd like to had more content but the course goal is to show off some techniques, patterns and good practices for your projects. Feel free to share your comments 😎&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>ddd</category>
    </item>
    <item>
      <title>api v2 or something alike</title>
      <dc:creator>Hiram</dc:creator>
      <pubDate>Fri, 02 Jul 2021 17:23:56 +0000</pubDate>
      <link>https://forem.com/eichgi/api-v2-or-something-alike-eg6</link>
      <guid>https://forem.com/eichgi/api-v2-or-something-alike-eg6</guid>
      <description>&lt;p&gt;Hallo Leute! Today's post is about how I migrated from /api/v1/ to platform v2 🤓&lt;/p&gt;

&lt;p&gt;This is the continuation of the &lt;a href="https://dev.to/eichgi/reverse-engineering-a-platform-1ohh"&gt;Reverse engineering a platform&lt;/a&gt; post.&lt;/p&gt;

&lt;p&gt;The next scenario for my brand-new compatible platform was to be in charge of the live app consuming the first backend (v1). Given I already understood how it worked, it was time to update the app in order to consume the new API. But unlike the new apps consuming the v2 platform this one was rooted to the initial database/storage process.&lt;/p&gt;

&lt;p&gt;In order to be able to do a hot swap when updating, I had to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;migrate the database and make sure the new laravel's&lt;/li&gt;
&lt;li&gt;migrations fit as expected&lt;/li&gt;
&lt;li&gt;migrate all the files to the new server (I could patch the links but eventually that server was going to be dropped)&lt;/li&gt;
&lt;li&gt;add business logic for old and new files and data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It sounds easy but I spent some time figuring out several scenarios in order to pick the simplest solution. Once the roadmap was defined it was basically sew n' sing 😗.&lt;/p&gt;

&lt;p&gt;After some polishing it was finally done. I saved some time so the users could make the update, and as usual, you know everything is fine because no one is complaining!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sdZDUoGC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://media1.tenor.com/images/d86a7d4f9caac0b10568b904b5b30bc5/tenor.gif%3Fitemid%3D13361936" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sdZDUoGC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://media1.tenor.com/images/d86a7d4f9caac0b10568b904b5b30bc5/tenor.gif%3Fitemid%3D13361936" alt="It's beautiful"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The cherry of the cake was the unlocking of some features I couldn't have it working in v1. This is how the software works essentially, iterate over and over until you have the desired outcome.&lt;/p&gt;

&lt;p&gt;Stay tune for more 😎&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This post was originally published on &lt;a href="https://blog.eichgi.com/apiv2-or-something-alike"&gt;https://blog.eichgi.com/apiv2-or-something-alike&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>laravel</category>
      <category>android</category>
      <category>apiv2</category>
    </item>
    <item>
      <title>Reverse Engineering a platform</title>
      <dc:creator>Hiram</dc:creator>
      <pubDate>Thu, 24 Jun 2021 14:30:05 +0000</pubDate>
      <link>https://forem.com/eichgi/reverse-engineering-a-platform-1ohh</link>
      <guid>https://forem.com/eichgi/reverse-engineering-a-platform-1ohh</guid>
      <description>&lt;p&gt;Hey folks, today's post is about a reverse engineering work I had to do for a platform and what I found by doing it 💪&lt;/p&gt;

&lt;p&gt;A while ago I acquired a web platform with an android client for a very entertaining project. The backend was written with Symfony 2.X and the client was a native android app written in Java.&lt;/p&gt;

&lt;p&gt;Long short story, the backend was a complete headache. I had to deal with different configurations for the server given it was aimed for PHP 7. A lot of deprecations had happened since then so you can picture it. The first setup was good enough, but I ran out of lucky for the 2nd 🤯, this led me to create my 100% (almost there) compatible backend.&lt;/p&gt;

&lt;p&gt;Version 1&lt;br&gt;
I started by consuming all API endpoints to figure out what responses look like. I blue-printed the web admin views to replicate the same functionality, and after a thorough debugging to the database I was able to create the same relations with some tweaks for a better integration. This work took me like two weeks but I ended up with an easier and well-known backend.&lt;/p&gt;

&lt;p&gt;The android app didn't require too many changes. I basically changed the interfaces, some texts translations and added some validations. Version 1 was a pretty straight forward work.&lt;/p&gt;

&lt;p&gt;Update to version 2:&lt;br&gt;
The mess came with the update, because I changed things and it was necessary to understand what should happen next in order to place it into the app. It costs me two rounds, and a hell of debugging. This is basically how you learn to reverse engineering, things must follow a flow and you need to chunk it into small pieces so they can fit. Discover the rules and validations might be the hardest part, try and failure over and over.&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%2Fmedia1.tenor.com%2Fimages%2Fbf5435861513b64b9ae54eec103a6526%2Ftenor.gif%3Fitemid%3D4614296" 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%2Fmedia1.tenor.com%2Fimages%2Fbf5435861513b64b9ae54eec103a6526%2Ftenor.gif%3Fitemid%3D4614296" alt="Bob's sculpture"&gt;&lt;/a&gt;&lt;br&gt;
Graphic explanation, thanks bob&lt;/p&gt;

&lt;p&gt;Finally I could achieve the expected behavior and things continue working smoothly. It has been a good experience. Now that I have forked the project every new change is easier to me. I could go deeper with advanced android topics as well. This is a practice I completely recommend to everyone in their spare time. Keep coding and keep failing ✌&lt;br&gt;&lt;br&gt;
 &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This post was originally published on &lt;a href="https://eichgi.hashnode.dev/reverse-engineering-a-platform" rel="noopener noreferrer"&gt;https://eichgi.hashnode.dev/reverse-engineering-a-platform&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>reverse</category>
      <category>engineer</category>
    </item>
    <item>
      <title>Do you really know AdMob? 🤑</title>
      <dc:creator>Hiram</dc:creator>
      <pubDate>Fri, 14 May 2021 16:25:59 +0000</pubDate>
      <link>https://forem.com/eichgi/do-you-really-know-admob-oog</link>
      <guid>https://forem.com/eichgi/do-you-really-know-admob-oog</guid>
      <description>&lt;p&gt;For the last 3 years I´ve been learning AdMob by the hard way 😫... The last strike reduced the earnings by 100% because we didn't complied with its policies. I won't share numbers but it really hit us. That was the moment I decided I needed to take the bull by the horns and learn how to avoid these situations again.&lt;/p&gt;

&lt;p&gt;The following list is intended to highlight what you should &amp;amp; shouldn't do with your ads setup and your AdMob account:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monitor any ads frequency change and do it slowly so your users get use to it&lt;/li&gt;
&lt;li&gt;Do not request to your users to consume ads unnecessarily&lt;/li&gt;
&lt;li&gt;Unlike other networks like Unity Ads, AdMob doesn't invalidate user's click which means your score will decrease and potentially lead your account to get suspended.&lt;/li&gt;
&lt;li&gt;Next logic step is to implement a mechanism in which you recognize and prevent intended abuse from your users (firebase events &amp;amp; remote config might help you with this)&lt;/li&gt;
&lt;li&gt;Mediation might help you to get better eCPM by leveraging AdMob to handle different networks&lt;/li&gt;
&lt;li&gt;To have a 2nd network implementation in case you got suspended, the hot-swap functionality might let you lose less money until your account is back in the game.&lt;/li&gt;
&lt;li&gt;Align the Ads categories with your app's content rating; make sure you don't have bets or adult ads if your app's rating is for kids.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another way we increased and improved our ads implementation was working along an apps partner manager from AdMob. These managers will help you land better ad types and formats within your apps. They usually make suggestions about frequency, when, where and how to place better ad units. Also how to avoid common mistakes like accidental clicks and poor visibility. We were reached by one so I am not entirely sure how you can receive help and assistance from them but probably it happens once you reach certain amount of traffic.&lt;/p&gt;

&lt;p&gt;The last suggestion is the most obvious and important, read the guidelines and take an AdMob course so you can have deeper knowledge of the entire ecosystem. In the following link you will find the Google´s official guide for AdMob: &lt;a href="https://skillshop.exceedlms.com/student/path/17105-get-started-with-google-admob"&gt;https://skillshop.exceedlms.com/student/path/17105-get-started-with-google-admob&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Topics you'll find there are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sign and set up of your AdMob account&lt;/li&gt;
&lt;li&gt;How to create Ad units and which are the formats and types available&lt;/li&gt;
&lt;li&gt;Generate reports&lt;/li&gt;
&lt;li&gt;Use mediation for multiple ad networks&lt;/li&gt;
&lt;li&gt;Set up block controls for different audiences or filter undesired advertisers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the moment I am trying to launch in-house campaigns so I can advertise my own app inventory for free. Please feel free to suggest more good practices you know. And if you were wondering, the last implemented strategy allowed us to recover partially while improving our potential to continue growing more over time. Fail once, fail twice, fail better 😉...&lt;/p&gt;

</description>
      <category>admob</category>
      <category>apps</category>
    </item>
    <item>
      <title>Coding Android apps again</title>
      <dc:creator>Hiram</dc:creator>
      <pubDate>Wed, 14 Apr 2021 14:10:51 +0000</pubDate>
      <link>https://forem.com/eichgi/coding-android-apps-again-5cel</link>
      <guid>https://forem.com/eichgi/coding-android-apps-again-5cel</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post was originally published in my blog: &lt;a href="https://blog.eichgi.com/coding-android-apps-again"&gt;https://blog.eichgi.com/coding-android-apps-again&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hi folks, I wanted to share that for the last 2 months I've been coding again android apps! I haven't work with apps since my first year as developer, like 5 years ago.&lt;/p&gt;

&lt;p&gt;I wanted to highlight how much the apps environment has changed as well as my knowledge regarding the Android environment. Last year I was tinkering with the 1st beta of Ionic-vue, leveraging from my js and react-vue skills I was able to create a basic games app which makes me continue doing more apps.&lt;/p&gt;

&lt;p&gt;Then I decided to jump into native code again given I already know Java, and the experience was awesome. I was able to write logic and use the components quite easily, I did remember learning networking with OkHttp and Retrofit which wasn't easy. But now it couldn't be more smoothly given everything I've been learning by exposing me to different technologies in these years.&lt;/p&gt;

&lt;p&gt;Now with Firebase being THE thing for Android everything became easier, natural and fluid. I still prefer to wire up my connections to my backend, but for analytics and maybe auth along other minor services Firebase is the way.&lt;/p&gt;

&lt;p&gt;When JetBrains released the first Kotlin beta I was interested, but in the end I continue as a fullstack. Now it's a thing I want to learn, "fun" for functions? It has to mean something 😁.&lt;/p&gt;

&lt;p&gt;I think that's it for now, I will continue developing more features for the apps we published. I hope we can ramp up into 2M MAU for the next post so I can share more insights about how to deal with updates and changes your community may dislike ✌️&lt;/p&gt;

&lt;p&gt;Stay sharp&lt;/p&gt;

</description>
      <category>android</category>
      <category>apps</category>
    </item>
    <item>
      <title>GOlang discussion 🤔</title>
      <dc:creator>Hiram</dc:creator>
      <pubDate>Wed, 24 Feb 2021 20:56:30 +0000</pubDate>
      <link>https://forem.com/eichgi/golang-discussion-239l</link>
      <guid>https://forem.com/eichgi/golang-discussion-239l</guid>
      <description>&lt;p&gt;Hi folks! &lt;/p&gt;

&lt;p&gt;I am doing a Go bootcamp and I would like to know your thoughts about working with Go compared to other languages like Javascript, PHP or (insert your preferred  language). &lt;/p&gt;

&lt;p&gt;It was easy or hard for you to make the leap to Go? What you think it's great in Go and what stuff do you miss from other languages? What is your recommendation for Go rookies? &lt;/p&gt;

&lt;p&gt;Thanks every1 for your opinions!&lt;/p&gt;

&lt;p&gt;p.d. which is the best guide or course besides officials docs do you recommend? 👌&lt;/p&gt;

</description>
      <category>go</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
