<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>Francisco Sousa&apos;s blog</title><description>My thoughts on making computers do stuff.</description><link>https://jfranciscosousa.com/</link><item><title>Leveling up your web apps with Zod and ChatGPT</title><link>https://jfranciscosousa.com/blog/leveling-up-your-web-apps-with-zod-and-chatgpt/</link><guid isPermaLink="true">https://jfranciscosousa.com/blog/leveling-up-your-web-apps-with-zod-and-chatgpt/</guid><description>&lt;p&gt;It&apos;s been pretty much a year since ChatGPT became available to the general public. Time goes pretty fast. Today, OpenAI&apos;s products are better and faster than ever, and the competition is pretty healthy with alternatives like Anthropic and Bard.&lt;/p&gt;
&lt;p&gt;A lot of tech companies across the industry are either pivoting to AI or extending their products with AI features. This leaves us, the engineers of said companies, an entirely new way of thinking about the products we build. Let&apos;s see what we, JavaScript folks, can do to integrate these powerful tools with our existing web apps.&lt;/p&gt;
&lt;h2&gt;Pick an LLM provider&lt;/h2&gt;
&lt;p&gt;The first step is to pick an LLM provider. Most tech companies already have some sort of deal with providers like OpenAI. If your company isn&apos;t in this situation, you can suggest one. I&apos;m biased towards OpenAI for their enterprise agreement and data protection policies.&lt;/p&gt;
&lt;p&gt;Keep costs in mind. If you&apos;re with a big company with a substantial AI budget, go ahead. Otherwise, plan your budget carefully. LLM providers are expensive and offer no free tiers.&lt;/p&gt;
&lt;p&gt;Companies often provide AI features only in paid plans or with strict daily/weekly/monthly limits, like Notion, to manage costs.&lt;/p&gt;
&lt;p&gt;For this blog post, we&apos;ll proceed with the OpenAI API.&lt;/p&gt;
&lt;h2&gt;How to get JSON out of LLMs&lt;/h2&gt;
&lt;p&gt;LLMs take human language and out comes... &quot;more language.&quot; That&apos;s the basic &quot;black box&quot; behavior we can assume from LLMs. This is not ideal if you are planning to integrate your existing APIs and systems with it, right? How do we even go about parsing whatever comes out of the LLM?&lt;/p&gt;
&lt;p&gt;Easy, we tell the LLM to output in JSON. Yes, that&apos;s a thing. You can literally ask ChatGPT to respond in JSON. Even better, you can tell ChatGPT (and most LLMs) to answer you back according to a specific JSON schema specification that you yourself provide.&lt;/p&gt;
&lt;p&gt;We can go up another level and build a JSON schema, asking the LLM to follow said schema. However, writing JSON schemas is not very pleasant, so we use something a little better, &lt;code&gt;zod&lt;/code&gt;, a runtime validation library for JavaScript.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;zod&lt;/code&gt;, you can define schemas and validate your data against them. You can also infer &lt;code&gt;Typescript&lt;/code&gt; types from &lt;code&gt;zod&lt;/code&gt; schemas. This ensures type safety as well as runtime safety. You can then convert the &lt;code&gt;zod&lt;/code&gt; schema to a JSON schema and ask the LLM to respond following the specified schema.&lt;/p&gt;
&lt;p&gt;Luckily, there&apos;s already a great library that does this, &lt;a href=&quot;https://github.com/dzhng/zod-gpt&quot;&gt;zod-gpt&lt;/a&gt;. Here&apos;s how it works.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import z from &quot;zod&quot;;
import { OpenAIChatApi } from &quot;llm-api&quot;;
import { completion } from &quot;zod-gpt&quot;;

const openai = new OpenAIChatApi(
  { apiKey: &quot;YOUR-OPENAI-KEY&quot; },
  { model: &quot;gpt-3.5-turbo-16k&quot; }
);

const schema = z.object({
    name: z.string().describe(&quot;The name of the person&quot;),
    age: z.number().describe(&quot;The person&apos;s age&quot;)
});

const response = await completion(openai, &quot;Imagine a random person&quot;, { schema });

// data will be typed as { name: string; age: number }
console.log(response.data);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Internally, &lt;code&gt;zod-gpt&lt;/code&gt; will make sure the output from the LLM matches your provided zod schema, and it will even retry in case it fails. It&apos;s also aware of rate limits and will wait accordingly!&lt;/p&gt;
&lt;h2&gt;Some practical examples&lt;/h2&gt;
&lt;p&gt;Generating random people is not a very good use case, but one of the best applications for these LLMs is taking natural text and converting it into a structure.&lt;/p&gt;
&lt;p&gt;For example, when I worked in healthcare tech, a popular challenge was taking doctor&apos;s notes and creating structured data out of them. Here&apos;s how you could do it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import z from &quot;zod&quot;;
import { OpenAIChatApi } from &quot;llm-api&quot;;
import { completion } from &quot;zod-gpt&quot;;

const openai = new OpenAIChatApi(
  { apiKey: &quot;YOUR-OPENAI-KEY&quot; },
  { model: &quot;gpt-3.5-turbo-16k&quot; }
);

const schema is z.object({
  name: z.string().describe(&quot;The patient&apos;s name&quot;),
  age: z.number().describe(&quot;The patient&apos;s age&quot;),
  weight: z.number().describe(&quot;The patient&apos;s weight in kilograms&quot;),
  height: z.number().describe(&quot;The patient&apos;s height in centimeters&quot;),
  bmi: z.number().describe(&quot;The patient&apos;s BMI&quot;),
  bmiEnum: z
    .enum([&quot;underweight&quot;, &quot;normal&quot;, &quot;overweight&quot;, &quot;obese&quot;, &quot;very_obese&quot;])
    .describe(&quot;The patient&apos;s BMI in text format&quot;),
  existingConditions: z
    .array(z.string())
    .describe(&quot;The patient&apos;s existing conditions&quot;),
});

function parseDoctorsNotes(notes: string) {
  const prompt = `
    Please take these notes from a doctor
    and try to structure the data following the provided
    JSON schema info. Notes: ${notes}
  `;

  return completion(openai, prompt, { schema });
}

const response = await parseDoctorsNotes(
  &quot;John Doe, 46 years. Had a cardiac arrest 2 years ago. 91kg, 184cm&quot;
);

console.log(response.data);

/**
  Example output:

  {
    name: &quot;John Doe&quot;,
    age: 46,
    weight: 91,
    height: 184,
    bmi: 26.86,
    bmiEnum: &quot;overweight&quot;,
    existingConditions: [&quot;cardiac arrest&quot;]
  }
 */

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This pattern is extremely useful for any boring, repetitive pattern your applications might have. Form filling is one of them and they can often be replaced with natural language parsing through LLMs.&lt;/p&gt;
&lt;h2&gt;Warning: This code is meant for servers!&lt;/h2&gt;
&lt;p&gt;The code above uses an API key from OpenAI. You should never ever store these keys on your client-side applications. Always run them on a server environment.&lt;/p&gt;
&lt;p&gt;The example here would work for &lt;code&gt;node&lt;/code&gt;, &lt;code&gt;Deno&lt;/code&gt;, or &lt;code&gt;bun&lt;/code&gt;. The only thing that would change is probably how you import the dependencies and how you access your environment variable.&lt;/p&gt;
&lt;p&gt;Most popular frontend frameworks, like &lt;code&gt;Next.js&lt;/code&gt;, &lt;code&gt;Remix&lt;/code&gt;, or &lt;code&gt;Svelte-Kit&lt;/code&gt;, have ways to run server code either through server-side rendering or API routes. Use those!&lt;/p&gt;
&lt;p&gt;By running this on the server you can safely handle your API keys, and you can even implement rate limiting mechanisms so that your clients don&apos;t hammer your LLM APIs.&lt;/p&gt;
&lt;h2&gt;UX considerations&lt;/h2&gt;
&lt;p&gt;However, all of this technology needs to be used with a different UI/UX perspective in mind. AI is &lt;strong&gt;not 100% reliable&lt;/strong&gt;. When you talk with any chat assistant they make sure to tell you that results might not be 100% correct and to take them with a grain of salt. But these usages of LLMs often don&apos;t pass this info to our users, so we have to do it ourselves.&lt;/p&gt;
&lt;p&gt;In the real world, this doctor example should pre-fill an existing form component instead of just submitting this data somewhere. This way, doctors could double-check the info generated by the LLM before submitting.&lt;/p&gt;
&lt;p&gt;And please make sure you are transparent with users about the use of AI and always make them opt-in or easy to bypass with a manual alternative!&lt;/p&gt;
&lt;h2&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;I hope this discovery of zod-gpt unlocks some ideas for you! It might be an obvious solution for some, but for me and others, thinking about LLMs this way was truly mind-blowing.&lt;/p&gt;
&lt;p&gt;This wouldn&apos;t be possible without the work of &lt;a href=&quot;https://github.com/dzhng&quot;&gt;David Zhang&lt;/a&gt;, who created both the &lt;code&gt;llm-api&lt;/code&gt; and &lt;code&gt;zod-gpt&lt;/code&gt; packages! Thank you.&lt;/p&gt;
</description><pubDate>Wed, 20 Dec 2023 00:00:00 GMT</pubDate><author>Francisco Sousa</author></item><item><title>Validating environment variables with zod</title><link>https://jfranciscosousa.com/blog/validating-environment-variables-with-zod/</link><guid isPermaLink="true">https://jfranciscosousa.com/blog/validating-environment-variables-with-zod/</guid><description>&lt;p&gt;This is a quick blog post to showcase something I&apos;ve seen in some frontend starter repositories, namely &lt;code&gt;create-t3-app&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Environment variables and &lt;code&gt;.env&lt;/code&gt; files are the de-facto way of configuring JavaScript web applications. Most meta-frameworks, such as Next.js, Svelte Kit, and others, support them out of the box, allowing you to have one or multiple &lt;code&gt;.env&lt;/code&gt; files for your projects.&lt;/p&gt;
&lt;p&gt;Some frameworks even add TypeScript support, generating types based on the contents of your &lt;code&gt;.env&lt;/code&gt; files, as &lt;code&gt;svelte-kit&lt;/code&gt; does.&lt;/p&gt;
&lt;h2&gt;Taking it a step further&lt;/h2&gt;
&lt;p&gt;Even with TypeScript support, there is one thing that I think is essential for most web applications: runtime validation of used environment variables.&lt;/p&gt;
&lt;p&gt;This is extremely useful when other developers check out your projects. It can be difficult to figure out exactly what environment variables are needed and what values they should have, unless you keep a detailed documentation of every environment variable (which you should do, even if you use the information in this blog post).&lt;/p&gt;
&lt;p&gt;And as it’s usual for runtime validations, we can use &lt;code&gt;zod&lt;/code&gt; to perform this task:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import z from &quot;zod&quot;;

const envSchema = z.object({
  DATABASE_URL: z.string(),
  NODE_ENV: z.enum([&quot;development&quot;, &quot;test&quot;, &quot;production&quot;]).default(&quot;development&quot;),
});

export const ENV = envSchema.parse(process.env);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above code, we define a &lt;code&gt;zod&lt;/code&gt; schema that requires the presence of a &lt;code&gt;DATABASE_URL&lt;/code&gt; and &lt;code&gt;NODE_ENV&lt;/code&gt;. The value of &lt;code&gt;NODE_ENV&lt;/code&gt; defaults to &lt;code&gt;development&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, we export an &lt;code&gt;ENV&lt;/code&gt; variable with the result of the &lt;code&gt;parse&lt;/code&gt; function from our &lt;code&gt;zod&lt;/code&gt; schema. The &lt;code&gt;parse&lt;/code&gt; function validates the contents of &lt;code&gt;process.env&lt;/code&gt; and returns an object that matches the schema. This means we filter out any unnecessary information from &lt;code&gt;process.env&lt;/code&gt;. Keep this in mind.&lt;/p&gt;
&lt;p&gt;We can use it as such:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { ENV } from &quot;./env.ts&quot;;

// This will use be fully typed if using Typescript! Will also work for Javascript.
console.log(ENV.NODE_ENV);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When using TypeScript, all uses of &lt;code&gt;ENV&lt;/code&gt; will be strongly typed. Additionally, if the schema has validation errors, the application itself will crash if &lt;code&gt;DATABASE_URL&lt;/code&gt; is missing, for example.&lt;/p&gt;
&lt;h2&gt;Handling non-string values&lt;/h2&gt;
&lt;p&gt;If you need to read non-string values such as numbers or booleans from your environment, you can rely on the &lt;code&gt;transform&lt;/code&gt; function.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import z from &quot;zod&quot;;

const envSchema = z.object({
    DATABASE_URL: z.string(),
    NODE_ENV: z.enum([&quot;development&quot;, &quot;test&quot;, &quot;production&quot;]).default(&quot;development&quot;),
    SOME_NUMBER: z.string().regex(/^\\d+$/).transform(Number),
    SOME_BOOLEAN: z.enum([&quot;true&quot;, &quot;false&quot;]).transform((v) =&amp;gt; v === &quot;true&quot;),
});

export const ENV = envSchema.parse(process.env);

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The strategy here is to first validate the string value, and then use &lt;code&gt;transform&lt;/code&gt; to convert it to the final value. For numbers, a simple regex can be used, and for booleans, an enum can be used. When using &lt;code&gt;ENV&lt;/code&gt;, &lt;code&gt;SOME_NUMBER&lt;/code&gt; and &lt;code&gt;SOME_BOOLEAN&lt;/code&gt; will have a type of &lt;code&gt;number&lt;/code&gt; and &lt;code&gt;boolean&lt;/code&gt;, respectively.&lt;/p&gt;
&lt;h2&gt;Handling a bad schema&lt;/h2&gt;
&lt;p&gt;If our environment doesn&apos;t match our defined schema, our application could crash during runtime. Some frameworks and libraries might actually crash when running the app server itself, which is good, but others might crash during runtime. To prevent this, it is a good practice to validate the schema before running our application.&lt;/p&gt;
&lt;p&gt;First, we need to find a way to extract issues from our schema, as it will be useful when running the validation script.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import z from &quot;zod&quot;;

const envSchema = z.object({
  DATABASE_URL: z.string(),
  NODE_ENV: z.enum([&quot;development&quot;, &quot;test&quot;, &quot;production&quot;]).default(&quot;development&quot;),
});

export const ENV = envSchema.parse(process.env);

export const getEnvIssues = (): z.ZodIssue[] | void =&amp;gt; {
  const result = serverEnvSchema.safeParse(process.env);
  if (!result.success) return result.error.issues;
};

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we write a validation script that we can run before launching our web app. You can even incorporate this step into your CI pipelines, to detect environment errors before actually deploying your application. I personally add this script to my &lt;code&gt;build&lt;/code&gt; step on Vercel or Netlify (or wherever I&apos;m deploying).&lt;/p&gt;
&lt;p&gt;Make sure to install &lt;code&gt;zod-error&lt;/code&gt; as well. It helps to prettify &lt;code&gt;zod&lt;/code&gt; errors for you.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { generateErrorMessage } from &quot;zod-error&quot;;
import { getEnvIssues } from &quot;./env.ts&quot;;

const issues = getEnvIssues();

if (issues) {
  console.error(&quot;Invalid environment variables, check the errors below!&quot;);
  console.error(
    generateErrorMessage(issues, {
      delimiter: { error: &quot;\\n&quot; },
    })
  );
  process.exit(-1);
}

console.log(&quot;The environment variables are valid!&quot;);

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are working in a TypeScript environment, you can run the script above using &lt;code&gt;ts-node&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;I can’t use &lt;code&gt;process.env&lt;/code&gt;!&lt;/h2&gt;
&lt;p&gt;Users of tools like &lt;code&gt;deno&lt;/code&gt;, &lt;code&gt;Cloudflare Workers&lt;/code&gt;, &lt;code&gt;vite&lt;/code&gt;, or &lt;code&gt;bun&lt;/code&gt; cannot use &lt;code&gt;process.env&lt;/code&gt;, which is specific to Node.js (although it also works on Webpack!).&lt;/p&gt;
&lt;p&gt;Instead, here are some alternatives:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;deno&lt;/code&gt; - use &lt;code&gt;Deno.env.toObject()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cloudflare&lt;/code&gt; - while not tested, you can try to access environment variables as global variables via &lt;code&gt;globalThis&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vite&lt;/code&gt; - use &lt;code&gt;import.meta.env&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bun&lt;/code&gt; - use &lt;code&gt;Bun.env&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In summary, consult the documentation for your runtime or meta-framework to learn how to access the objects containing all environment variables.&lt;/p&gt;
&lt;p&gt;Hope this information is helpful! See you soon.&lt;/p&gt;
</description><pubDate>Sat, 18 Mar 2023 00:00:00 GMT</pubDate><author>Francisco Sousa</author></item><item><title>Using zustand with Svelte</title><link>https://jfranciscosousa.com/blog/using-zustand-with-svelte/</link><guid isPermaLink="true">https://jfranciscosousa.com/blog/using-zustand-with-svelte/</guid><description>&lt;p&gt;&lt;a href=&quot;https://github.com/pmndrs/zustand&quot;&gt;zustand&lt;/a&gt; is an excellent library primarily used in the React community. It&apos;s my go-to choice for any shared state management in React due to its ease of use, and for being a solution to most shared state issues like &lt;a href=&quot;https://react-redux.js.org/api/hooks#stale-props-and-zombie-children&quot;&gt;zombie childs&lt;/a&gt;, &lt;a href=&quot;https://github.com/bvaughn/rfcs/blob/useMutableSource/text/0000-use-mutable-source.md&quot;&gt;react concurrency&lt;/a&gt;, and &lt;a href=&quot;https://github.com/facebook/react/issues/13332&quot;&gt;context loss&lt;/a&gt; between mixed renderers.&lt;/p&gt;
&lt;p&gt;For these same reasons, I also started using it in some Svelte projects. I like how short and simple most &lt;code&gt;zustand&lt;/code&gt; stores look. Even though Svelte built-in store mechanisms are excellent, we can go up a level by combining the vanilla solution and &lt;code&gt;zustand&lt;/code&gt;. We can have the best of both worlds.&lt;/p&gt;
&lt;h2&gt;What&apos;s a Svelte store?&lt;/h2&gt;
&lt;p&gt;According to &lt;a href=&quot;https://svelte.dev/tutorial/writable-stores&quot;&gt;Svelte&apos;s docs&lt;/a&gt;, which I recommend reading, a Svelte store can be any object with a subscribe method. However, it&apos;s recommended that you use Svelte&apos;s tools to create &lt;code&gt;writable&lt;/code&gt;, &lt;code&gt;readable&lt;/code&gt;, &lt;code&gt;derived&lt;/code&gt;, or custom stores. Personally, I most often default to custom stores.&lt;/p&gt;
&lt;p&gt;An example of one such store:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { writable } from &apos;svelte/store&apos;;

function createCount() {
  const { subscribe, set, update } = writable&amp;lt;number&amp;gt;(0);

  return {
    subscribe,
    increment: () =&amp;gt; update((n) =&amp;gt; n + 1),
    reset: () =&amp;gt; set(0)
  };
}

const countStore = createCount();

export default countStore;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To use the store in Svelte, you can do this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  import counterStore from &apos;../lib/counter.store&apos;;
&amp;lt;/script&amp;gt;

&amp;lt;h1&amp;gt;Welcome!&amp;lt;/h1&amp;gt;
&amp;lt;p&amp;gt;{$counterStore}&amp;lt;/p&amp;gt;
&amp;lt;button on:click={() =&amp;gt; counterStore.increment()}&amp;gt;Click&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The good thing about native Svelte stores is the &lt;code&gt;auto-subscription&lt;/code&gt; feature. Within a Svelte component, you can automatically use the dollar sign to subscribe to a Svelte store. In the markup we use &lt;code&gt;$counterStore&lt;/code&gt;. This allows us to read the store value immediately and react to its updates. That markup will always contain the latest &lt;code&gt;$counterStore&lt;/code&gt; value.&lt;/p&gt;
&lt;p&gt;In this example, our value is just a number, but the store&apos;s value can be anything, an object even!&lt;/p&gt;
&lt;h2&gt;Zustand&lt;/h2&gt;
&lt;p&gt;These Svelte stores are more than good enough! I&apos;ve shipped production apps that used this to great effect. However, &lt;code&gt;zustand&lt;/code&gt; has some advantages that can be very useful! You can connect &lt;code&gt;zustand&lt;/code&gt; stores to Redux devtools using their &lt;code&gt;devtools&lt;/code&gt; middleware. There is also a &lt;a href=&quot;https://docs.pmnd.rs/zustand/recipes/recipes#persist-middleware&quot;&gt;middleware&lt;/a&gt; that you can use to persist any store with any storage provider. You can also easily craft your middleware, for example, to &lt;a href=&quot;https://docs.pmnd.rs/zustand/guides/connect-to-state-with-url-hash&quot;&gt;connect your store to the URL hash&lt;/a&gt; to save filters on a complex table component.&lt;/p&gt;
&lt;p&gt;Let&apos;s turn our previous &lt;code&gt;counter&lt;/code&gt; store into a &lt;code&gt;zustand&lt;/code&gt; store:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import create from &apos;zustand/vanilla&apos;;
import zustandToSvelte from &apos;./zustandToSvelte&apos;;

export interface CounterState {
  value: number;
  increment: () =&amp;gt; void;
}

const counterStore = create&amp;lt;CounterState&amp;gt;((set) =&amp;gt; ({
  value: 0,
  increment: () =&amp;gt; set((state) =&amp;gt; ({ value: state.value + 1 }))
}));

export default counterStore;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;zustand&lt;/code&gt; can infer types from your store (if you use their &lt;code&gt;combine&lt;/code&gt; utility), but it&apos;s recommended to write them yourself. You must also type the actions themselves, as values and actions are all part of the store.&lt;/p&gt;
&lt;p&gt;Still, this is not ready to be used by Svelte. We need to turn this into a native store first! &lt;code&gt;zustand&lt;/code&gt; stores have a &lt;code&gt;subscribe&lt;/code&gt; method, but it&apos;s not natively compatible with Svelte. Let&apos;s create a wrapper that turns a &lt;code&gt;zustand&lt;/code&gt; store into a compatible Svelte store.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { readable } from &apos;svelte/store&apos;;
import type { StoreApi } from &apos;zustand&apos;;

export default function zustandToSvelte&amp;lt;StateType&amp;gt;(zustandStore: StoreApi&amp;lt;StateType&amp;gt;) {
  const svelteStore = readable(zustandStore.getState(), (set) =&amp;gt; {
    zustandStore.subscribe((value) =&amp;gt; set(value));
  });

  return {
    ...zustandStore,
    subscribe: svelteStore.subscribe
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we create a readable Svelte store from our zustand store. &lt;code&gt;readable&lt;/code&gt; takes two arguments: the initial state and a function that can &lt;code&gt;set&lt;/code&gt; the state of the readable. In this function, we can &lt;code&gt;subscribe&lt;/code&gt; to our store and &lt;code&gt;set&lt;/code&gt; the state of the native Svelte readable store.&lt;/p&gt;
&lt;p&gt;Then, we return the original &lt;code&gt;zustandStore&lt;/code&gt; with the overridden &lt;code&gt;subscribe&lt;/code&gt; method from our readable Svelte store. &lt;code&gt;zustand&lt;/code&gt; subscribe doesn&apos;t work by default on Svelte components. That&apos;s why we need to go through all of these steps.&lt;/p&gt;
&lt;p&gt;All it&apos;s left, is to go back to our &lt;code&gt;zustand&lt;/code&gt; store example and use this utility:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import create from &apos;zustand/vanilla&apos;;
import zustandToSvelte from &apos;./zustandToSvelte&apos;;

export interface CounterState {
  value: number;
  increment: () =&amp;gt; void;
}

// Wrap this with `zustandToSvelte`
const counterStore = zustandToSvelte(create&amp;lt;CounterState&amp;gt;((set) =&amp;gt; ({
  value: 0,
  increment: () =&amp;gt; set((state) =&amp;gt; ({ value: state.value + 1 }))
})));

export default counterStore;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can then use our new store like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  import counterStore from &apos;../lib/counter.store&apos;;
&amp;lt;/script&amp;gt;

&amp;lt;h1&amp;gt;Welcome!&amp;lt;/h1&amp;gt;
&amp;lt;p&amp;gt;{$counterStore.value}&amp;lt;/p&amp;gt;
&amp;lt;button on:click={() =&amp;gt; $counterStore.increment()}&amp;gt;Click&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unlike a native Svelte store, the whole store object must be subscribed to. In our native example, you can call &lt;code&gt;counterStore.increment()&lt;/code&gt; without the dollar sign. With these new zustand stores, you&apos;ll always have to use the dollar sign: &lt;code&gt;$counterStore.increment()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Outside of Svelte components, you can use its methods as usual:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import counterStore from &apos;../lib/counter.store&apos;;

// You can peek into the state or even call some actions
counterStore.getState().increment()

console.log(counterStore.getState().value)

// You can set the state
counterStore.setState({ value: 0 })

// You can also subscribe to it!
counterStore.subscribe((state) =&amp;gt; console.log(state));
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;To be honest, &lt;code&gt;Svelte&lt;/code&gt; custom stores are almost as powerful as &lt;code&gt;zustand&lt;/code&gt;. I&apos;m just more familiar with the latter, and their middleware tools are also a great nice to have. However, this pattern can be applied to many state management technologies. Whether you have some &lt;code&gt;redux&lt;/code&gt; store or &lt;code&gt;xstate&lt;/code&gt; machine that you want to use within Svelte components, you can reach for &lt;code&gt;svelte/readable&lt;/code&gt; to try and make it work!&lt;/p&gt;
&lt;p&gt;Stay safe!&lt;/p&gt;
</description><pubDate>Sun, 09 Oct 2022 00:00:00 GMT</pubDate><author>Francisco Sousa</author></item><item><title>Typing remix loaders with confidence</title><link>https://jfranciscosousa.com/blog/typing-remix-loaders-with-confidence/</link><guid isPermaLink="true">https://jfranciscosousa.com/blog/typing-remix-loaders-with-confidence/</guid><description>&lt;p&gt;Well, hello there, it&apos;s time for a lightning-quick Remix tip. Let&apos;s see how we can genuinely write typesafe Remix routes by sharing types between loaders and components for full-stack typing!&lt;/p&gt;
&lt;h2&gt;Remix, what?&lt;/h2&gt;
&lt;p&gt;For the readers unfamiliar with Remix, it&apos;s a React framework created by the &lt;code&gt;react-router&lt;/code&gt; team. It uses &lt;code&gt;react-router&lt;/code&gt; to make a server-rendering full-stack framework with React support. It&apos;s the other kind of &lt;code&gt;Next.js&lt;/code&gt; (sorry).&lt;/p&gt;
&lt;h2&gt;Loaders, what are they?&lt;/h2&gt;
&lt;p&gt;Remix is a server-side rendering framework, and as such, you can load data directly to your components while they are being rendered on the server.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function loader() {
  return &quot;Hello world!&quot;;
}

export default function SomeRemixPage() {
  const data = useLoaderData();
  return &amp;lt;p&amp;gt;{ data }&amp;lt;/p&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can only define the &lt;code&gt;loader&lt;/code&gt; function on the Remix route file, but you can then call the &lt;code&gt;useLoaderData&lt;/code&gt; hook on every component used inside that route. This is very useful for better SEO and spares you from adding loading states to your app, as the page comes pre-rendered from the server.&lt;/p&gt;
&lt;h2&gt;Let&apos;s add types the regular way&lt;/h2&gt;
&lt;p&gt;You can quickly type &lt;code&gt;useLoaderData&lt;/code&gt; using type variables. Its type signature is &lt;code&gt;useLoaderData&amp;lt;T&amp;gt;: T&lt;/code&gt;, so if you do &lt;code&gt;useLoaderData&amp;lt;string&amp;gt;&lt;/code&gt;, you just typed your loader!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function loader(): string {
  return &quot;Hello world!&quot;;
}

export default function SomeRemixPage() {
  const data = useLoaderData&amp;lt;string&amp;gt;();
  return &amp;lt;p&amp;gt;{ data }&amp;lt;/p&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, this has a couple of issues. Typing the generic &lt;code&gt;T&lt;/code&gt; on &lt;code&gt;useLoaderData&lt;/code&gt; is the same thing as doing this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const data = useLoaderData&amp;lt;string&amp;gt;();
const data = useLoaderData() as string;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you do not type &lt;code&gt;useLoaderData&lt;/code&gt;, its default type is &lt;code&gt;any&lt;/code&gt;, so you can just cast that to whatever you want. This means that the following scenario won&apos;t report type errors and would just crash during runtime.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function loader(): string {
  return &quot;Hello world!&quot;;
}

export default function SomeRemixPage() {
  const { data } = useLoaderData&amp;lt;{data: string}&amp;gt;();
  return &amp;lt;p&amp;gt;{ data }&amp;lt;/p&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above scenario, this will crash, even though the types are all valid. We want the loader data to be of type &lt;code&gt;{ data: string }&lt;/code&gt;, but as there is no direct-type connection between the loader and the hook, some bugs might leak into runtime if you type all of your &lt;code&gt;loader&lt;/code&gt; and &lt;code&gt;useLoaderData&lt;/code&gt; like this.&lt;/p&gt;
&lt;h2&gt;Inferring the types from the &lt;code&gt;loader&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The solution is to infer the types from the &lt;code&gt;loader&lt;/code&gt; automatically. The first step is to never use the &lt;code&gt;LoaderFunction&lt;/code&gt; type.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { json } from &apos;@remix-run/node&apos;; // or &quot;@remix-run/cloudflare&quot;
import type { LoaderFunction } from &apos;@remix-run/node&apos;; // or &quot;@remix-run/cloudflare&quot;

export const loader: LoaderFunction = async () =&amp;gt; {
	return json({ ok: true });
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As of Remix version &lt;code&gt;1.5.1&lt;/code&gt; the &lt;code&gt;LoaderFunction&lt;/code&gt; return type is &lt;code&gt;Promise&amp;lt;Response&amp;gt; | Response | Promise&amp;lt;AppData&amp;gt; | AppData&lt;/code&gt; which means we cannot reliably use the solution I will propose. &lt;code&gt;AppData&lt;/code&gt; is an internal Remix type that is the same as &lt;code&gt;any&lt;/code&gt;, which doesn&apos;t do much for type safety.&lt;/p&gt;
&lt;p&gt;The second step is to &lt;strong&gt;never&lt;/strong&gt; type the return value of the &lt;code&gt;loader&lt;/code&gt; function. We are going to do that automatically from now on. So if you have any &lt;code&gt;export function loader(): SomeType&lt;/code&gt;, make sure you remove the &lt;code&gt;SomeType&lt;/code&gt; from there.&lt;/p&gt;
&lt;p&gt;Then we can start inferring the type of our &lt;code&gt;loader&lt;/code&gt; automatically!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type LoaderType = Awaited&amp;lt;ReturnType&amp;lt;typeof loader&amp;gt;&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This essentially infers the type of the &lt;code&gt;loader&lt;/code&gt; function.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Awaited&lt;/code&gt; extracts the type of a promise because &lt;code&gt;loader&lt;/code&gt; can be async&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ReturnType&lt;/code&gt; is pretty straightforward and returns the type returned by &lt;code&gt;typeof loader&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Revisiting our previous example, it would become this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function loader(): string {
  return &quot;Hello world!&quot;;
}

type LoaderType = Awaited&amp;lt;ReturnType&amp;lt;typeof loader&amp;gt;&amp;gt;;

export default function SomeRemixPage() {
  const { data } = useLoaderData&amp;lt;LoaderType&amp;gt;();
  return &amp;lt;p&amp;gt;{ data }&amp;lt;/p&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Typescript would then complain that there is no property &lt;code&gt;data&lt;/code&gt; on type &lt;code&gt;string&lt;/code&gt;! We can fix that by correctly refactoring our &lt;code&gt;loader&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function loader() {
  return { data: &quot;Hello world!&quot; };
}

type LoaderType = Awaited&amp;lt;ReturnType&amp;lt;typeof loader&amp;gt;&amp;gt;;

export default function SomeRemixPage() {
  const { data } = useLoaderData&amp;lt;LoaderType&amp;gt;();
  return &amp;lt;p&amp;gt;{ data }&amp;lt;/p&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want to type the arguments of &lt;code&gt;loader&lt;/code&gt; you can import the following from Remix internals:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import type { DataFunctionArgs } from &quot;@remix-run/server-runtime&quot;;

export function loader(({ request }: DataFunctionArgs)) {
  // do stuff
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will keep the return type untouched so you can automatically infer it.&lt;/p&gt;
&lt;p&gt;This solution is a great help because it also takes care of conditionals! Imagine that this page is only for authenticated users:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function loader({ request }: DataFunctionArgs) {
  if (!extractUserFromRequest(request)) return new Response(null, { status: 401 });
  return { data: &quot;Hello world!&quot; };
}

type LoaderType = Awaited&amp;lt;ReturnType&amp;lt;typeof loader&amp;gt;&amp;gt;;

export default function SomeRemixPage() {
  const { data } = useLoaderData&amp;lt;LoaderType&amp;gt;();
  return &amp;lt;p&amp;gt;{ data }&amp;lt;/p&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, Typescript would complain that there is no &lt;code&gt;data&lt;/code&gt; on &lt;code&gt;Response&lt;/code&gt; when using the &lt;code&gt;useLoaderData&lt;/code&gt; hook. This would avoid a regression here. You could quickly fix this by using &lt;code&gt;throw&lt;/code&gt; when checking for the user session instead of &lt;code&gt;return&lt;/code&gt;. Remember that you can &lt;code&gt;throw&lt;/code&gt; inside a &lt;code&gt;loader&lt;/code&gt; function to immediately stop rendering! It would also keep Typescript silent because the function only returns &lt;code&gt;{ data: string }&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Final notes&lt;/h2&gt;
&lt;p&gt;You can also export the types from the inferred &lt;code&gt;loader&lt;/code&gt; functions to use wherever you want. This lets us ensure everything is nice and tidy and keep runtime errors to a minimum.&lt;/p&gt;
&lt;p&gt;Hope this helped, let me know if you have any questions! If you also have a better solution than this one, please let me know!&lt;/p&gt;
&lt;p&gt;Stay safe out there!&lt;/p&gt;
</description><pubDate>Sun, 19 Jun 2022 00:00:00 GMT</pubDate><author>Francisco Sousa</author></item><item><title>Locale detection with Remix</title><link>https://jfranciscosousa.com/blog/locale-detection-with-remix-run/</link><guid isPermaLink="true">https://jfranciscosousa.com/blog/locale-detection-with-remix-run/</guid><description>&lt;p&gt;Lately, I&apos;ve been fooling around with &lt;a href=&quot;https://remix.run&quot;&gt;remix.run&lt;/a&gt; and server rendering apps instead of client-side only stuff. However, whenever I use things like &lt;a href=&quot;https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString&quot;&gt;toLocaleString&lt;/a&gt; from the &lt;code&gt;Date&lt;/code&gt; API, we get shifting date formats on the frontend, and &lt;code&gt;Text content did not match&lt;/code&gt; errors from React. This warning often happens in either Remix or Next.js, or any SSR (server-side rendering) React-based solution. Let&apos;s see why it happens and how we can fix it.&lt;/p&gt;
&lt;h2&gt;Why does this happen when using SSR?&lt;/h2&gt;
&lt;p&gt;This warning is common for devs getting started with SSR and using Javascript APIs that access the locale, like &lt;code&gt;toLocaleString&lt;/code&gt; on the &lt;code&gt;Date&lt;/code&gt; API. Often, the locale of the Node.js runtime on the server will not match your browser&apos;s locale, so when your app server renders an HTML page, it will use the default locale of that particular server, usually &lt;code&gt;en-us&lt;/code&gt;. When your browser re-hydrates the page, it will use your default locale. If it doesn&apos;t match the &lt;code&gt;en-us&lt;/code&gt; locale, your &lt;code&gt;toLocaleString&lt;/code&gt; call will most likely be different from the server, and you will get that warning, and you may sometimes even notice the date flashing back from one format to another.&lt;/p&gt;
&lt;h2&gt;How can we fix it then?&lt;/h2&gt;
&lt;p&gt;Well, the quick approach is to hardcode &lt;code&gt;en-us&lt;/code&gt; everywhere. Most locale-based APIs accept a locale or an array of locales as an argument. If your website or app only is available in one language, it might even be a good idea just to show it with a single locale to avoid UI confusion. &lt;code&gt;(...).toLocaleString(&quot;en-us&quot;)&lt;/code&gt; would fix it by using that locale always on the server and the client.&lt;/p&gt;
&lt;p&gt;However, most SSR frameworks can access the &lt;code&gt;headers&lt;/code&gt; of an HTTP request, which means you can access the &lt;code&gt;Accept-Language&lt;/code&gt; HTTP header! You can check out &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language&quot;&gt;in the MDN docs&lt;/a&gt; what the header is and its format. Essentially it tells servers the list of languages the user accepts.&lt;/p&gt;
&lt;p&gt;Why a list, though? Because most browsers allow you to set a list of preferred languages ordered by your preference. Take a look at mine (you can access the page via &lt;a&gt;chrome://settings/languages&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;My solution is simple. We get the header value from the request, then pass it into a &lt;code&gt;Provider&lt;/code&gt;. Then we will create a hook to get the &lt;code&gt;locale&lt;/code&gt; value whenever we need it. We will only extract the user&apos;s first language (the favorite one).&lt;/p&gt;
&lt;p&gt;We will use the &lt;a href=&quot;https://www.npmjs.com/package/accept-language-parser&quot;&gt;accept-language-parser&lt;/a&gt; package to parse the header value. Let&apos;s install it&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# or npm if you are into that
yarn add accept-language-parser

# if you are using typescript, this might be useful
yarn add --dev @types/accept-language-parser
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let&apos;s create our provider/context/hook combo. I&apos;ll put it under &lt;code&gt;app/hooks/useLocale.tsx&lt;/code&gt;. Our default export is the hook, but we also export the provider to use it on the &lt;code&gt;root&lt;/code&gt; file of &lt;code&gt;remix&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { createContext, ReactNode, useContext } from &quot;react&quot;;

const LocaleContext = createContext&amp;lt;string&amp;gt;(&quot;&quot;);

export function LocaleProvider({
  children,
  locale,
}: {
  children: ReactNode;
  locale: string;
}) {
  return (
    &amp;lt;LocaleContext.Provider value={locale}&amp;gt;{children}&amp;lt;/LocaleContext.Provider&amp;gt;
  );
}

export default function useLocale() {
  return useContext(LocaleContext);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then create a new loader in your &lt;code&gt;root&lt;/code&gt; file. Mine is in the &lt;code&gt;app/root.tsx&lt;/code&gt; file. If you already have a loader, augment it with this one.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import type { LoaderFunction } from &quot;remix&quot;;
import acceptLanguage from &quot;accept-language-parser&quot;;

export const loader: LoaderFunction = async ({ request }) =&amp;gt; {
  const languages = acceptLanguage.parse(
    request.headers.get(&quot;Accept-Language&quot;) as string
  );

  // If somehow the header is empty, return a default locale
  if (languages?.length &amp;lt; 1) return &quot;en-us&quot;;

  // If there is no region for this locale, just return the code
  if (!languages[0].region) return languages[0].code;

  return `${languages[0].code}-${languages[0].region.toLowerCase()}`;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, wrap it with our provider in your default export of the &lt;code&gt;root&lt;/code&gt; file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { useLoaderData } from &quot;remix&quot;;
import { LocaleProvider } from &quot;~/hooks/useLocale&quot;;

export default function App() {
  const locale = useLoaderData();

  return (
    &amp;lt;LocaleProvider locale={locale}&amp;gt;
      {/* Whatever your already had here */}
    &amp;lt;/LocaleProvider&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&apos;s it. Any component on your &lt;code&gt;remix&lt;/code&gt; routes can now use &lt;code&gt;useLocale&lt;/code&gt; to get the current locale! An example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import useLocale from &quot;~/hooks/useLocale&quot;;

export default function HomePage() {
  const locale = useLocale();

  return (
    &amp;lt;div&amp;gt;
      {1_000_000.toLocaleString(locale)}
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For &lt;code&gt;Next.js&lt;/code&gt; the solution is very similar! Just play around with this concept using &lt;a href=&quot;https://nextjs.org/docs/api-reference/data-fetching/get-initial-props&quot;&gt;getInitialProps&lt;/a&gt; on the &lt;a href=&quot;https://nextjs.org/docs/advanced-features/custom-app&quot;&gt;_app.js&lt;/a&gt; component.&lt;/p&gt;
&lt;p&gt;I hope this solution is of use to you! You can see it in action &lt;a href=&quot;https://github.com/jfranciscosousa/remix-prisma-starter&quot;&gt;on my remix starter repo&lt;/a&gt; with &lt;code&gt;prisma&lt;/code&gt; support and auth via cookies.&lt;/p&gt;
&lt;p&gt;Stay safe!&lt;/p&gt;
</description><pubDate>Tue, 18 Jan 2022 00:00:00 GMT</pubDate><author>Francisco Sousa</author></item><item><title>Authenticating things with cookies on Next.js</title><link>https://jfranciscosousa.com/blog/authenticating-things-with-cookies-on-next-js/</link><guid isPermaLink="true">https://jfranciscosousa.com/blog/authenticating-things-with-cookies-on-next-js/</guid><description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href=&quot;https://blog.finiam.com/blog/authenticating-things-with-cookies-on-next-js/&quot;&gt;Finiam&apos;s website&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Please click here to skip all of the backstory behind this blog post and &lt;a href=&quot;https://jfranciscosousa.com/#main-content&quot;&gt;jump straight into the code&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;A while back &lt;a href=&quot;https://blog.finiam.com/blog/predictable-react-authentication-with-the-context-api&quot;&gt;I made a blog post&lt;/a&gt; on how to manage user authentication on React apps. It&apos;s a task I&apos;ve made time and time again and decided to share this with you folks. That blog post is aimed at single-page apps obviously, but nowadays we are seeing more and more (again) server-rendered apps, especially with Next.js.&lt;/p&gt;
&lt;p&gt;When server-rendering pages, authentication works a little differently. While you might use the context API or any other kind of mechanism to authenticate users on your app on the client-side, we should do that on the server so that users receive their content in the context of their authentication status. Let&apos;s see how.&lt;/p&gt;
&lt;h2&gt;Next.js&lt;/h2&gt;
&lt;p&gt;Next.js in short is a React app framework that server renders components. If you use React you probably heard about Next.js. If you didn&apos;t, please take some time to read about it on their &lt;a href=&quot;https://nextjs.org/&quot;&gt;website&lt;/a&gt;, it&apos;s great and it&apos;s pretty much the only way we write React apps at Finiam.&lt;/p&gt;
&lt;p&gt;Next.js allows us to server render components and play with the usual &lt;code&gt;req&lt;/code&gt; and &lt;code&gt;res&lt;/code&gt; objects on an HTTP request lifecycle, something that you can&apos;t really do with a traditional React app SPA. This means we can interact with the cookies involved in our app.&lt;/p&gt;
&lt;p&gt;However, in Next.js there is no need to handle that on the client side. You can directly authenticate users in &lt;code&gt;getServerSideProps&lt;/code&gt; and immediately render the page based on the authentication status, without having to deal with that on the frontend.&lt;/p&gt;
&lt;p&gt;It also means that you get access to a &lt;code&gt;node.js&lt;/code&gt; environment, meaning that you can even use a database directly instead of having to develop a remote API yourself. It&apos;s full-stack React development!&lt;/p&gt;
&lt;p&gt;A quick taste of how that can look like.&lt;/p&gt;
&lt;p&gt;By the way, we pretty much just write Typescript nowadays. You should &lt;strong&gt;really&lt;/strong&gt; be writing Typescript.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &apos;react&apos;;
import { GetServerSidePropsContext } from &apos;next&apos;;
import Head from &apos;next/head&apos;;
import Login from &apos;root/components/Login&apos;;
import { userFromRequest } from &apos;root/web/tokens&apos;;

interface User {
	email: string;
	name: string;
}

interface Props {
	user?: User;
}

export default function Home({ user }: Props) {
	if (!user) return &amp;lt;Login /&amp;gt;;

	return &amp;lt;div&amp;gt;Hello {user.name}&amp;lt;/div&amp;gt;;
}

export async function getServerSideProps(context: GetServerSidePropsContext) {
	const user = await userFromRequest(context.req);

	if (!user) return { props: {} };

	return {
		props: { user }
	};
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Awesome right? In this example, the magic is on the &lt;code&gt;userFromRequest&lt;/code&gt; method, which extracts the user by reading the cookie that was previously set during login.&lt;/p&gt;
&lt;p&gt;Let&apos;s dive into the technicalities.&lt;/p&gt;
&lt;h2&gt;Architecture&lt;/h2&gt;
&lt;p&gt;The small project we are going to make consists of a very simple but relatively secure sign-up and login flow, and a user-owned resource, in our case, just some notes that users can write for themselves. Users can authenticate themselves and write notes that can only be accessed by them.&lt;/p&gt;
&lt;p&gt;We are going to store users and their hashed passwords on our database. When users sign up or login successfully we place a JWT token that contains the email of said user, and we sign this JWT with a secret key (that should be random and crypto secure). This token is then stored on an &lt;code&gt;HttpOnly&lt;/code&gt; and &lt;code&gt;Secure&lt;/code&gt; cookie, so that it&apos;s only readable on the server-side and no client-side Javascript can access it. The cookie will be valid for 1 day, but this can be configured as you prefer.&lt;/p&gt;
&lt;p&gt;Whenever we want to identify the user behind a network request, we check the cookies of said request, take our JWT token, and then check if it was signed with our secret key, so bad actors can&apos;t forge JWT tokens. If it was signed with our key we can assume that it&apos;s a valid token. Then we fetch the user info from the database and we can use it freely to build pages on the server-side.&lt;/p&gt;
&lt;p&gt;The users&apos; passwords are all hashed using &lt;code&gt;argon2&lt;/code&gt;, the strongest and safest hashing algorithm out there, that won the &lt;a href=&quot;https://www.password-hashing.net/&quot;&gt;PHC (Password Hashing Competition)&lt;/a&gt; very recently.&lt;/p&gt;
&lt;h3&gt;Disclaimer&lt;/h3&gt;
&lt;p&gt;The code you are about to see, especially the backend part of it, lacks a lot of the required functionality to make a truly robust and secure authentication flow. It doesn&apos;t handle brute force attacks, doesn&apos;t rate-limit sign-ups, has no geolocation protection, has no session management, meaning that we can&apos;t log out users remotely or anything like that, and has no CSRF protection. We work in the finance world where most of these things are &lt;strong&gt;mandatory&lt;/strong&gt;. We recommend using &lt;a href=&quot;https://auth0.com/&quot;&gt;auth0&lt;/a&gt; which has most of these features or at least setting up &lt;a href=&quot;https://cloudflare.com&quot;&gt;Cloudflare&lt;/a&gt; to rate the limit on your endpoints.&lt;/p&gt;
&lt;p&gt;Our password hashing algorithm is safe, the strongest in the industry. The JWT tokens are being generated correctly, but it&apos;s recommended to rotate the signing key daily or weekly. It&apos;s enough to secure most web apps, but if you are handling sensitive data, you should take more steps to secure it.&lt;/p&gt;
&lt;p&gt;We usually write most of our complex backend apps with Phoenix or Rails. In these frameworks, you have much more complete libraries that handle most of the steps we are going to make by hand!&lt;/p&gt;
&lt;p&gt;Still, you also have &lt;a href=&quot;https://next-auth.js.org/&quot;&gt;NextAuth.js&lt;/a&gt; which is a pretty nice project that handles integration with 3rd parties, implement CSRF and session management. But still lacks brute force and geolocation protection.&lt;/p&gt;
&lt;p&gt;Consider this a learning project, that can go into production, but don&apos;t implement, let&apos;s say, a banking system using it without added protection!&lt;/p&gt;
&lt;p&gt;&amp;lt;span id=&quot;main-content&quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;h2&gt;Let&apos;s get coding&lt;/h2&gt;
&lt;p&gt;To get started just bootstrap a &lt;code&gt;Next.js&lt;/code&gt; example project:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn create next-app --typescript
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All of the code I&apos;m going to show is a simplified version of what&apos;s present on our &lt;a href=&quot;https://github.com/finiam/next-prisma-starter&quot;&gt;next-prisma-starter template&lt;/a&gt; at the time of writing this blog post.&lt;/p&gt;
&lt;h3&gt;User management&lt;/h3&gt;
&lt;p&gt;Let&apos;s create a Prisma schema for our app: &lt;code&gt;prisma/schema.prisma&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;generator client {
  provider = &quot;prisma-client-js&quot;
}

datasource db {
  provider = &quot;postgres&quot;
  url      = env(&quot;DATABASE_URL&quot;)
}

model User {
  id         String        @id @default(uuid())
  email      String        @unique
  name       String
  password   String
  createdAt  DateTime      @default(now())
  updatedAt  DateTime      @updatedAt
  Note       Note[]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This schema will create a database where we can store users and each user has many notes.&lt;/p&gt;
&lt;p&gt;You can then use the Prisma migration tool to apply these changes to your database.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;prisma migrate dev --name init
prisma migrate deploy --preview-feature &amp;amp;&amp;amp; yarn db:generate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let&apos;s create our &lt;code&gt;lib&lt;/code&gt; layer. On full-stack Next.js projects, I like to put all of the web-related code on &lt;code&gt;src&lt;/code&gt; and then all of the backend business logic on a &lt;code&gt;lib&lt;/code&gt; directory. Both, at the root of the project.&lt;/p&gt;
&lt;p&gt;First, the users module.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { User } from &apos;@prisma/client&apos;;
import prisma from &apos;lib/prisma&apos;;
import { encryptPassword } from &apos;lib/auth/passwordUtils&apos;;
import pick from &apos;lodash/pick&apos;;

export interface UserParams {
	email: string;
	name: string;
	password: string;
}

// Given some params, create a user on the database,
// storing the encrypted password.
export async function createUser(params: UserParams): Promise&amp;lt;User&amp;gt; {
	const filteredParams = pick(params, [&apos;email&apos;, &apos;name&apos;, &apos;password&apos;]);
	const password = await encryptPassword(filteredParams.password);
	const user = await prisma.user.create({
		data: { ...filteredParams, password }
	});

	// Make sure all our lib methods obfuscate the password
	user.password = &apos;&apos;;

	return user;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, the auth module.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { User } from &apos;@prisma/client&apos;;
import prisma from &apos;lib/prisma&apos;;
import { verifyPassword } from &apos;./passwordUtils&apos;;

export interface LoginParams {
	email: string;
	password: string;
}

// Given some login params (email and password)
// return the user if the password is valid
// or null if it&apos;s not.
export async function login(params: LoginParams): Promise&amp;lt;User&amp;gt; {
	const user = await prisma.user.findUnique({ where: { email: params.email } });

	if (!user) return null;

	if (await verifyPassword(user.password, params.password)) {
		// Make sure all our lib methods obfuscate the password
		user.password = &apos;&apos;;

		return user;
	}

	return null;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the final missing piece, the hashing algorithm.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import argon2 from &apos;argon2&apos;;

export async function encryptPassword(password: string): Promise&amp;lt;string&amp;gt; {
	return argon2.hash(password);
}

export async function verifyPassword(hash: string, password: string): Promise&amp;lt;boolean&amp;gt; {
	return argon2.verify(hash, password);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can create and login users easily.&lt;/p&gt;
&lt;h3&gt;Cookies and JWT&lt;/h3&gt;
&lt;p&gt;You probably heard about &lt;a href=&quot;https://jwt.io/&quot;&gt;JWT&lt;/a&gt; also known as JSON web tokens. They are used by most web services for authentication, using JWT to encode some of the user info and use it as a token that identifies the user. You should &lt;strong&gt;never&lt;/strong&gt; store secret info on JWTs as they are meant to be readable by anyone. It&apos;s the signing mechanism of a JWT however that guarantees the identity and validity of a given token.&lt;/p&gt;
&lt;p&gt;When generating a JWT we sign it with a &lt;strong&gt;very secret key&lt;/strong&gt;, and then we can verify that any subsequent JWT passed to us was correctly generated with said &lt;strong&gt;very secret key&lt;/strong&gt;. If any attacker forges a JWT, that won&apos;t work as they are not generated with said key.&lt;/p&gt;
&lt;p&gt;Like most web services, we generate a JWT with the user email (or any other unique identifier of the user) in it on a successful login or sign-up and then use server-side secure cookies to store it. This way, it&apos;s impossible for malicious Javascript to get the cookie. It&apos;s also pretty easy to directly, on the server-side, render the user&apos;s authenticated state.&lt;/p&gt;
&lt;p&gt;The code now. We are going to save this file under &lt;code&gt;src/web/tokens.ts&lt;/code&gt;. This is related to web logic and not exactly business-side logic. Our module exports 3 functions: &lt;code&gt;authenticateUser&lt;/code&gt;, &lt;code&gt;clearUser&lt;/code&gt; and &lt;code&gt;userFromRequest&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { User } from &quot;@prisma/client&quot;;
import jwt from &quot;jsonwebtoken&quot;;
import { serialize } from &quot;cookie&quot;;
import { NextApiResponse } from &quot;next&quot;;
import prisma from &quot;lib/prisma&quot;;
import { NextApiRequestCookies } from &quot;next/dist/next-server/server/api-utils&quot;;
import { IncomingMessage } from &quot;http&quot;;

// You should really not use the fallback and perhaps
// throw an error if this value is not set!
const JWT_TOKEN_KEY = process.env.JWT_TOKEN_KEY || &quot;super duper secret key&quot;;
const cookieOptions = {
  httpOnly: true,
  maxAge: 2592000,
  path: &quot;/&quot;,
  sameSite: &quot;Strict&quot;,
  secure: process.env.NODE_ENV === &quot;production&quot;,
};

function setCookie(
  res: any,
  name: string,
  value: string,
  options: Record&amp;lt;string, unknown&amp;gt; = {}
): void {
  const stringValue =
    typeof value === &quot;object&quot; ? `j:${JSON.stringify(value)}` : String(value);

  res.setHeader(&quot;Set-Cookie&quot;, serialize(name, String(stringValue, options));
}

// This sets the cookie on a NextApiResponse so we can authenticate
// users on API routes.
export function authenticateUser(res: NextApiResponse, user: User): void {
  if (!user) return;

  const token = jwt.sign({ email: user.email }, JWT_TOKEN_KEY, {
    expiresIn: &quot;1d&quot;,
  });

  setCookie(res, &quot;auth&quot;, token, cookieOptions);
}

// This removes the auth cookie, effectively logging out
// the user.
export function clearUser(res: NextApiResponse): void {
  setCookie(res, &quot;auth&quot;, &quot;0&quot;, {
    ...cookieOptions,
    path: &quot;/&quot;,
    maxAge: 1,
  });
}

// This gives back the user behind a given request
// either on API routes or getServerSideProps
export async function userFromRequest(
  req: IncomingMessage &amp;amp; { cookies: NextApiRequestCookies }
): Promise&amp;lt;User | undefined&amp;gt; {
  const { auth: token } = req.cookies;

  if (!token) return undefined;

  try {
    const data = jwt.verify(token, JWT_TOKEN_KEY);

    if (!data) return undefined;

    const user = await prisma.user.findUnique({
      where: { email: (data as any).email },
    });

    if (user) user.password = &quot;&quot;;

    return user;
  } catch (error) {
    return undefined;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we are able to create our API routes&lt;/p&gt;
&lt;h3&gt;API routes&lt;/h3&gt;
&lt;p&gt;We are using &lt;a href=&quot;https://www.npmjs.com/package/next-connect&quot;&gt;next-connect&lt;/a&gt; to implement our API routes. They have a cleaner API and allow us to easily use middlewares and the like.&lt;/p&gt;
&lt;p&gt;Let&apos;s quickly create a default &lt;code&gt;next-connect&lt;/code&gt; handler first. You can add default middlewares to this handler so we can re-use those on all of our API routes. In this, we can even define custom error handling behavior.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { NextApiResponse } from &apos;next&apos;;
import nextConnect from &apos;next-connect&apos;;

export default function defaultHandler&amp;lt;ReqType, ResType&amp;gt;() {
	return nextConnect&amp;lt;ReqType, ResType&amp;gt;({
		attachParams: true,
		onError: (err, req, res) =&amp;gt; {
			console.error(err);

			(res as unknown as NextApiResponse).status(500).json({ error: &apos;Internal Server Error&apos; });
		}
	});
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, for our API we are going to need two modules and a total of three endpoints.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/sessions&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;POST&lt;/code&gt; - logs in a user with a email/password combo&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DELETE&lt;/code&gt; - logs out the user&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/users&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;POST&lt;/code&gt; - creates users&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import { NextApiRequest, NextApiResponse } from &apos;next&apos;;
import { login } from &apos;lib/auth&apos;;
import { authenticateUser, clearUser } from &apos;root/web/tokens&apos;;
import defaultHandler from &apos;./_defaultHandler&apos;;

const handler = defaultHandler&amp;lt;NextApiRequest, NextApiResponse&amp;gt;()
	.post(async (req, res) =&amp;gt; {
		const user = await login(req.body);

		if (user) {
			authenticateUser(res, user);
			res.json(user);
		} else {
			res.status(404).send(&apos;&apos;);
		}
	})
	.delete((_req, res) =&amp;gt; {
		clearUser(res);

		res.send(&apos;&apos;);
	});

export default handler;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import { NextApiRequest, NextApiResponse } from &apos;next&apos;;
import { createUser } from &apos;lib/users&apos;;
import { authenticateUser } from &apos;src/web/tokens&apos;;
import defaultHandler from &apos;./_defaultHandler&apos;;

const handler = defaultHandler&amp;lt;NextApiRequest, NextApiResponse&amp;gt;().post(async (req, res) =&amp;gt; {
	const user = await createUser(req.body);

	authenticateUser(res, user);
	res.json(user);
});

export default handler;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now to go full circle, the frontend.&lt;/p&gt;
&lt;h3&gt;Frontend&lt;/h3&gt;
&lt;p&gt;On the frontend we need 4 dependencies, &lt;code&gt;redaxios&lt;/code&gt;, &lt;code&gt;react-hook-form &lt;/code&gt;, &lt;code&gt;react-query&lt;/code&gt; and &lt;code&gt;superjson&lt;/code&gt;. Feel free to go 100% vanilla on this, but we often end up installing this when projects become more complicated anyway.&lt;/p&gt;
&lt;p&gt;Let&apos;s just make 2 pages, a sign-up page, and a homepage. The home page either shows the current user email or the login form.&lt;/p&gt;
&lt;p&gt;Setup &lt;code&gt;react-query&lt;/code&gt; first.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &apos;react&apos;;
import { QueryClient, QueryClientProvider } from &apos;react-query&apos;;

const queryClient = new QueryClient();

export default function App({ Component, pageProps }) {
	return (
		&amp;lt;QueryClientProvider client={queryClient}&amp;gt;
			&amp;lt;Component {...pageProps} /&amp;gt;
		&amp;lt;/QueryClientProvider&amp;gt;
	);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, let&apos;s just implement a very nice utility that allows us to refresh server-side data from Next.js without a full page reload.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { useRouter } from &apos;next/router&apos;;

export default function useServerRefresher(): () =&amp;gt; void {
	const router = useRouter();

	return () =&amp;gt; router.replace(router.asPath);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can use this hook to refresh the data from &lt;code&gt;getServerSideProps&lt;/code&gt; without a full page reload! This is very handy to update stale data.&lt;/p&gt;
&lt;p&gt;Then, our sign-up page. We use this hook, after the successful &lt;code&gt;createUser&lt;/code&gt; mutation, the server refresher gets called and then we re-run the code on &lt;code&gt;getServerSideProps&lt;/code&gt; again, which redirects us to the homepage.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &apos;react&apos;;
import Head from &apos;next/head&apos;;
import { useForm } from &apos;react-hook-form&apos;;
import Link from &apos;next/link&apos;;
import { GetServerSidePropsContext } from &apos;next&apos;;
import { useMutation } from &apos;react-query&apos;;
import useServerRefresher from &apos;src/hooks/useServerRefresher&apos;;
import { userFromRequest } from &apos;src/web/tokens&apos;;

export default function SignUp() {
	const {
		handleSubmit,
		register,
		formState: { errors }
	} = useForm();
	const {
		isLoading,
		isError,
		mutate: createUserMutation
	} = useMutation((params) =&amp;gt; redaxios.post(&apos;/users&apos;, params), {
		onSuccess: useServerRefresher()
	});

	const handleCreateUser = (params) =&amp;gt; createUserMutation(params);

	return (
		&amp;lt;main&amp;gt;
			&amp;lt;form
				className=&quot;h-screen center flex flex-col items-center justify-center&quot;
				onSubmit={handleSubmit(handleCreateUser)}
			&amp;gt;
				&amp;lt;Head&amp;gt;
					&amp;lt;title&amp;gt;Sign Up&amp;lt;/title&amp;gt;
				&amp;lt;/Head&amp;gt;

				&amp;lt;div className=&quot;space-y-8&quot;&amp;gt;
					&amp;lt;h1 className=&quot;self-start text-xl&quot;&amp;gt;Sign up&amp;lt;/h1&amp;gt;

					&amp;lt;label className=&quot;flex flex-col&quot; htmlFor=&quot;email&quot;&amp;gt;
						Email
						&amp;lt;input id=&quot;email&quot; type=&quot;email&quot; {...register(&apos;email&apos;, { required: true })} /&amp;gt;
					&amp;lt;/label&amp;gt;

					&amp;lt;label className=&quot;flex flex-col&quot; htmlFor=&quot;name&quot;&amp;gt;
						Name
						&amp;lt;input id=&quot;name&quot; type=&quot;text&quot; {...register(&apos;name&apos;, { required: true })} /&amp;gt;
					&amp;lt;/label&amp;gt;

					&amp;lt;label className=&quot;flex flex-col&quot; htmlFor=&quot;password&quot;&amp;gt;
						Password
						&amp;lt;input id=&quot;password&quot; type=&quot;password&quot; {...register(&apos;password&apos;, { required: true })} /&amp;gt;
					&amp;lt;/label&amp;gt;

					&amp;lt;button
						className=&quot;u-button&quot;
						type=&quot;submit&quot;
						disabled={Object.keys(errors).length &amp;gt; 0 || isLoading}
					&amp;gt;
						Sign Up
					&amp;lt;/button&amp;gt;

					{isError &amp;amp;&amp;amp; &amp;lt;p&amp;gt;User exists&amp;lt;/p&amp;gt;}

					&amp;lt;Link href=&quot;/&quot;&amp;gt;
						&amp;lt;a className=&quot;block underline&quot; href=&quot;/&quot;&amp;gt;
							Login
						&amp;lt;/a&amp;gt;
					&amp;lt;/Link&amp;gt;
				&amp;lt;/div&amp;gt;
			&amp;lt;/form&amp;gt;
		&amp;lt;/main&amp;gt;
	);
}

export async function getServerSideProps(context: GetServerSidePropsContext) {
	const user = await userFromRequest(context.req);

	if (user) {
		return {
			redirect: {
				destination: &apos;/&apos;,
				permanent: false
			}
		};
	}

	return {
		props: {}
	};
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then, our homepage. In this case, we are not doing redirects. When having protected pages it&apos;s good to have the login logic on the component itself so users are still on the correct URL after logging in.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &apos;react&apos;;
import { User } from &apos;@prisma/client&apos;;
import { GetServerSidePropsContext } from &apos;next&apos;;
import Login from &apos;src/components/Login&apos;;
import { userFromRequest } from &apos;src/web/tokens&apos;;

interface Props {
	user?: User;
}

export default function Home({ user }: Props) {
	if (!user) return &amp;lt;Login /&amp;gt;;

	const handleLogout = () =&amp;gt; redaxios.delete(&apos;/sessions&apos;);

	return (
		&amp;lt;main className=&quot;max-w-4xl mx-auto py-20 space-y-8&quot;&amp;gt;
			Hello {user.name}!
			&amp;lt;button type=&quot;button&quot; onClick={handleLogout}&amp;gt;
				Logout
			&amp;lt;/button&amp;gt;
		&amp;lt;/main&amp;gt;
	);
}

export async function getServerSideProps(context: GetServerSidePropsContext) {
	const user = await userFromRequest(context.req);

	if (!user) return { props: {} };

	// Always use superjson as Next.js
	// can&apos;t serialize prisma objects by default
	return {
		props: superjson.serialize({
			user
		}).json
	};
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Don&apos;t forget the login component.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &apos;react&apos;;
import Head from &apos;next/head&apos;;
import { useForm } from &apos;react-hook-form&apos;;
import { useMutation } from &apos;react-query&apos;;
import Link from &apos;next/link&apos;;
import useServerRefresher from &apos;src/hooks/useServerRefresher&apos;;

export default function Login() {
	const {
		handleSubmit,
		register,
		formState: { errors }
	} = useForm();
	const {
		isLoading,
		isError,
		mutate: loginMutation
	} = useMutation((params) =&amp;gt; redaxios.post(&apos;/sessions&apos;, params), {
		onSuccess: useServerRefresher()
	});

	const onSubmit = async (params) =&amp;gt; loginMutation(params);

	return (
		&amp;lt;form
			className=&quot;h-screen u-center flex flex-col items-center space-y-8&quot;
			onSubmit={handleSubmit(onSubmit)}
		&amp;gt;
			&amp;lt;Head&amp;gt;
				&amp;lt;title&amp;gt;Login&amp;lt;/title&amp;gt;
			&amp;lt;/Head&amp;gt;

			&amp;lt;div className=&quot;space-y-8&quot;&amp;gt;
				&amp;lt;h1 className=&quot;self-start text-xl&quot;&amp;gt;Login&amp;lt;/h1&amp;gt;

				&amp;lt;label className=&quot;flex flex-col&quot; htmlFor=&quot;email&quot;&amp;gt;
					Email
					&amp;lt;input type=&quot;text&quot; {...register(&apos;email&apos;, { required: true })} /&amp;gt;
				&amp;lt;/label&amp;gt;

				&amp;lt;label className=&quot;flex flex-col&quot; htmlFor=&quot;password&quot;&amp;gt;
					Password
					&amp;lt;input type=&quot;password&quot; {...register(&apos;password&apos;, { required: true })} /&amp;gt;
				&amp;lt;/label&amp;gt;

				&amp;lt;button
					className=&quot;u-button&quot;
					type=&quot;submit&quot;
					disabled={Object.keys(errors).length &amp;gt; 0 || isLoading}
				&amp;gt;
					Login
				&amp;lt;/button&amp;gt;

				{isError &amp;amp;&amp;amp; &amp;lt;p&amp;gt;User password combination not found&amp;lt;/p&amp;gt;}

				&amp;lt;Link href=&quot;/signup&quot;&amp;gt;
					&amp;lt;a className=&quot;block underline&quot; href=&quot;/signup&quot;&amp;gt;
						Sign up
					&amp;lt;/a&amp;gt;
				&amp;lt;/Link&amp;gt;
			&amp;lt;/div&amp;gt;
		&amp;lt;/form&amp;gt;
	);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Closing notes&lt;/h2&gt;
&lt;p&gt;This is a great way to get started on full-stack projects on Next.js, very useful for small to medium-sized projects. You get full SSR so users never get to see any loading spinner while getting authentication details, as you get on most SPAs and you get to iterate on features easily without having to maintain separate projects and technologies for backend and frontend.&lt;/p&gt;
&lt;p&gt;You don&apos;t even need to use Prisma or do it all full-stack. You can have all of your business logic on a remote API and just use some Next.js API routes to manage authentication cookies and then fetch all your data on &lt;code&gt;getServerSideProps&lt;/code&gt;. Really, the sky is the limit here.&lt;/p&gt;
&lt;p&gt;If you really enjoyed the full-stack part of this, I recommend you looking into either &lt;a href=&quot;https://blitzjs.com/&quot;&gt;Blitz.js&lt;/a&gt; or &lt;a href=&quot;https://redwoodjs.com/&quot;&gt;Redwood.js&lt;/a&gt;, they do most of the heavy lifting for you and have built-in features that are essential for app development. We only use our &lt;a href=&quot;https://github.com/finiam/next-prisma-starter&quot;&gt;starter template&lt;/a&gt; for simpler stuff so we can enjoy the greater flexibility.&lt;/p&gt;
</description><pubDate>Sat, 31 Jul 2021 00:00:00 GMT</pubDate><author>Francisco Sousa</author></item><item><title>Predictable React authentication with the Context API</title><link>https://jfranciscosousa.com/blog/predictable-react-authentication/</link><guid isPermaLink="true">https://jfranciscosousa.com/blog/predictable-react-authentication/</guid><description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href=&quot;https://blog.finiam.com/blog/predictable-react-authentication-with-the-context-api/&quot;&gt;Finiam&apos;s website&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Despite there being many React and authentication tutorials out there, I feel like I showcased this to too many people I mentored over the past few months, so, this is a good time to share it with a greater audience. Authentication is an app-wide concern, and therefore a global state issue. For many, keeping global state in React is a tricky task, however, React has had a nice way of handling &quot;sort of&quot; global state, with the context API and hooks. Let&apos;s see how.&lt;/p&gt;
&lt;h2&gt;Praise be the Context&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;useContext&lt;/code&gt; is our best bet today. I use it a ton for both complex, app-wide state management, or even on smaller multi-component APIs, like making a re-usable dropdown component (it&apos;s how the &lt;a href=&quot;https://reach.tech/menu-button/&quot;&gt;reach-ui&lt;/a&gt; folks do it). &lt;a href=&quot;https://kentcdodds.com/blog/application-state-management-with-react&quot;&gt;Kent’s blog post&lt;/a&gt; is a great way to learn a bit more about the context API and how to use it effectively if you have never heard of it before.&lt;/p&gt;
&lt;p&gt;So, in order to manage authentication, we will use React’s context API to make it available for every component on the app, so you can easily implement classic login/logout/sign-up logic on your projects.&lt;/p&gt;
&lt;h2&gt;Some disclaimers&lt;/h2&gt;
&lt;p&gt;I am going to assume that you have some sort of backend already set up. The examples I am going to show you are present on our &lt;a href=&quot;https://github.com/finiam/phoenix_starter&quot;&gt;phoenix starter template&lt;/a&gt;. You can replace the API calls here with anything you have available. All of the code on this tutorial is there.&lt;/p&gt;
&lt;p&gt;Also, this is probably not ideal for 3rd party OAuth providers. To integrate with providers like Auth0, Google, Facebook, and others, you should use their own SDKs instead of using the patterns I am going to show you. It’s just easier and their tools usually handle all of this.&lt;/p&gt;
&lt;p&gt;On our work, at Finiam, we usually roll out our own authentication APIs together with the backend or we use whatever the client is using, which rarely includes OAuth providers.&lt;/p&gt;
&lt;h2&gt;Time to code&lt;/h2&gt;
&lt;p&gt;So, for our authentication management component, we have some basic requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Allow login info to be submitted&lt;/li&gt;
&lt;li&gt;Allow sign-up info to be submitted&lt;/li&gt;
&lt;li&gt;Allow a user to logout&lt;/li&gt;
&lt;li&gt;Check if a current user is logged in upon loading the app.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The plan is to provide these operations for the entire app using React’s context API and make them available with a simple &lt;code&gt;useAuth&lt;/code&gt; hook, that allows us to read and manipulate the authentication.&lt;/p&gt;
&lt;p&gt;Now the first step is to communicate with your authentication backend. We are going to make simple HTTP calls with &lt;a href=&quot;https://github.com/developit/redaxios&quot;&gt;redaxios&lt;/a&gt;. We just communicate with a few endpoints that manipulate server-side cookies to manage auth. There is no need to send authorization headers or manage tokens because all of the authentication is handled on the server-side and the browser just picks it up. We just make the HTTP calls and the server handles everything!&lt;/p&gt;
&lt;p&gt;If your backend handles with something like JWT bearer tokens, you can use &lt;code&gt;localStorage&lt;/code&gt; for that. You just need to modify your HTTP client to use the returned token on all of the following requests. You can also store it on local storage so users should not login every time. Be advised, that for web applications, server-side cookie authentication still offers the best security! Check &lt;a href=&quot;https://www.rdegges.com/2018/please-stop-using-local-storage/&quot;&gt;this blog post&lt;/a&gt; for an accurate explanation about that. &lt;strong&gt;Avoid using localStorage&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The code to interact with the sessions API, which handles login and logout.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import redaxios from &apos;redaxios&apos;;

export async function login(params: { email: string; password: string }): Promise&amp;lt;User&amp;gt; {
	const response = await redaxios.post(&apos;/api/sessions&apos;, { session: params });

	return response.data.data;
}

export async function logout() {
	const response = await redaxios.delete(&apos;/api/sessions&apos;);

	return response.data.data;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the code to interact with the users API, that signs up users or fetches the currently authenticated user in the session.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import redaxios from &apos;redaxios&apos;;

export async function getCurrentUser(): Promise&amp;lt;User&amp;gt; {
	const response = await redaxios.get(&apos;/api/user&apos;);

	return response.data.data;
}

export async function signUp(params: {
	email: string;
	name: string;
	password: string;
}): Promise&amp;lt;User&amp;gt; {
	const response = await redaxios.post(&apos;/api/user&apos;, { user: params });

	return response.data.data;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All the methods above throw an error if something happens. Validation errors, wrong passwords, users not logged in, and other things like network errors and such.&lt;/p&gt;
&lt;p&gt;Now, on to the context API stuff.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React, { createContext, ReactNode, useContext, useEffect, useMemo, useState } from &apos;react&apos;;
import { useHistory, useLocation } from &apos;react-router-dom&apos;;
import * as sessionsApi from &apos;./api/sessions&apos;;
import * as usersApi from &apos;./api/users&apos;;

interface AuthContextType {
	// We defined the user type in `index.d.ts`, but it&apos;s
	// a simple object with email, name and password.
	user?: User;
	loading: boolean;
	error?: any;
	login: (email: string, password: string) =&amp;gt; void;
	signUp: (email: string, name: string, password: string) =&amp;gt; void;
	logout: () =&amp;gt; void;
}

const AuthContext = createContext&amp;lt;AuthContextType&amp;gt;({} as AuthContextType);

// Export the provider as we need to wrap the entire app with it
export function AuthProvider({ children }: { children: ReactNode }): JSX.Element {
	const [user, setUser] = useState&amp;lt;User&amp;gt;();
	const [error, setError] = useState&amp;lt;any&amp;gt;();
	const [loading, setLoading] = useState&amp;lt;boolean&amp;gt;(false);
	const [loadingInitial, setLoadingInitial] = useState&amp;lt;boolean&amp;gt;(true);
	// We are using `react-router` for this example,
	// but feel free to omit this or use the
	// router of your choice.
	const history = useHistory();
	const location = useLocation();

	// Reset the error state if we change page
	useEffect(() =&amp;gt; {
		if (error) setError(undefined);
	}, [location.pathname]);

	// Check if there is a currently active session
	// when the provider is mounted for the first time.
	//
	// If there is an error, it means there is no session.
	//
	// Finally, just signal the component that the initial load
	// is over.
	useEffect(() =&amp;gt; {
		usersApi
			.getCurrentUser()
			.then((user) =&amp;gt; setUser(user))
			.catch((_error) =&amp;gt; {})
			.finally(() =&amp;gt; setLoadingInitial(false));
	}, []);

	// Flags the component loading state and posts the login
	// data to the server.
	//
	// An error means that the email/password combination is
	// not valid.
	//
	// Finally, just signal the component that loading the
	// loading state is over.
	function login(email: string, password: string) {
		setLoading(true);

		sessionsApi
			.login({ email, password })
			.then((user) =&amp;gt; {
				setUser(user);
				history.push(&apos;/&apos;);
			})
			.catch((error) =&amp;gt; setError(error))
			.finally(() =&amp;gt; setLoading(false));
	}

	// Sends sign up details to the server. On success we just apply
	// the created user to the state.
	function signUp(email: string, name: string, password: string) {
		setLoading(true);

		usersApi
			.signUp({ email, name, password })
			.then((user) =&amp;gt; {
				setUser(user);
				history.push(&apos;/&apos;);
			})
			.catch((error) =&amp;gt; setError(error))
			.finally(() =&amp;gt; setLoading(false));
	}

	// Call the logout endpoint and then remove the user
	// from the state.
	function logout() {
		sessionsApi.logout().then(() =&amp;gt; setUser(undefined));
	}

	// Make the provider update only when it should.
	// We only want to force re-renders if the user,
	// loading or error states change.
	//
	// Whenever the `value` passed into a provider changes,
	// the whole tree under the provider re-renders, and
	// that can be very costly! Even in this case, where
	// you only get re-renders when logging in and out
	// we want to keep things very performant.
	const memoedValue = useMemo(
		() =&amp;gt; ({
			user,
			loading,
			error,
			login,
			signUp,
			logout
		}),
		[user, loading, error]
	);

	// We only want to render the underlying app after we
	// assert for the presence of a current user.
	return (
		&amp;lt;AuthContext.Provider value={memoedValue}&amp;gt;{!loadingInitial &amp;amp;&amp;amp; children}&amp;lt;/AuthContext.Provider&amp;gt;
	);
}

// Let&apos;s only export the `useAuth` hook instead of the context.
// We only want to use the hook directly and never the context component.
export default function useAuth() {
	return useContext(AuthContext);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now this &lt;code&gt;useAuth.tsx&lt;/code&gt; file exports both the &lt;code&gt;AuthProvider&lt;/code&gt; and the &lt;code&gt;useAuth&lt;/code&gt;. In order to use the hook, we need to wrap the entire app (or the parts that need authentication), with the provider.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &quot;react&quot;;
import useAuth, { AuthProvider } from &quot;./useAuth&quot;;

function InnerApp() {
  const { user, loading, error, login, signUp, logout } = useAuth();

  // Do whatever you want with these!
}

export default function App() {
  return (
    &amp;lt;AuthProvider&amp;gt;
        &amp;lt;InnerApp /&amp;gt;
    &amp;lt;/AuthRouter&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I cut the &lt;code&gt;InnerApp&lt;/code&gt; content short because I&apos;m going to show you how this would look in a more &quot;production line&quot; environment. We will integrate &lt;code&gt;react-router&lt;/code&gt; with this hook in order to create login and sign-up pages, and also add protected routes.&lt;/p&gt;
&lt;p&gt;First, let&apos;s create two page components, one for signing up users and another for login.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React, { FormEvent } from &apos;react&apos;;
import { Link } from &apos;react-router-dom&apos;;
import useAuth from &apos;../useAuth&apos;;

// Just regular CSS modules, style, however, you desire
import styles from &apos;./index.module.css&apos;;

// This is a uncontrolled form! No need to manage state for each input!
export default function SignUpPage() {
	const { signUp, loading, error } = useAuth();

	async function handleSubmit(event: FormEvent&amp;lt;HTMLFormElement&amp;gt;) {
		event.preventDefault();

		const formData = new FormData(event.currentTarget);

		signUp(
			formData.get(&apos;email&apos;) as string,
			formData.get(&apos;name&apos;) as string,
			formData.get(&apos;password&apos;) as string
		);
	}

	return (
		&amp;lt;form className={styles.root} onSubmit={handleSubmit}&amp;gt;
			&amp;lt;h1&amp;gt;Sign up&amp;lt;/h1&amp;gt;

			{/*
          On a real world scenario, you should investigate
          the error object to see what&apos;s happening
      */}
			{error &amp;amp;&amp;amp; &amp;lt;p className={styles.error}&amp;gt;Sign up error!&amp;lt;/p&amp;gt;}

			&amp;lt;label&amp;gt;
				Name
				&amp;lt;input name=&quot;name&quot; /&amp;gt;
			&amp;lt;/label&amp;gt;

			&amp;lt;label&amp;gt;
				Email
				&amp;lt;input name=&quot;email&quot; type=&quot;email&quot; /&amp;gt;
			&amp;lt;/label&amp;gt;

			&amp;lt;label&amp;gt;
				Password
				&amp;lt;input name=&quot;password&quot; type=&quot;password&quot; /&amp;gt;
			&amp;lt;/label&amp;gt;

			{/*
        While the network request is in progress,
        we disable the button. You can always add
        more stuff, like loading spinners and whatnot.
      */}
			&amp;lt;button disabled={loading}&amp;gt;Submit&amp;lt;/button&amp;gt;

			&amp;lt;Link to=&quot;/login&quot;&amp;gt;Login&amp;lt;/Link&amp;gt;
		&amp;lt;/form&amp;gt;
	);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, the login page.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React, { FormEvent } from &apos;react&apos;;
import { Link } from &apos;react-router-dom&apos;;
import useAuth from &apos;../useAuth&apos;;

import styles from &apos;./index.module.css&apos;;

// Again, uncontrolled forms!
export default function Login() {
	const { login, loading, error } = useAuth();

	function handleSubmit(event: FormEvent&amp;lt;HTMLFormElement&amp;gt;) {
		event.preventDefault();

		const formData = new FormData(event.currentTarget);

		login(formData.get(&apos;email&apos;) as string, formData.get(&apos;password&apos;) as string);
	}

	return (
		&amp;lt;form className={styles.root} onSubmit={handleSubmit}&amp;gt;
			&amp;lt;h1&amp;gt;Login&amp;lt;/h1&amp;gt;

			&amp;lt;label&amp;gt;
				Email
				&amp;lt;input name=&quot;email&quot; /&amp;gt;
			&amp;lt;/label&amp;gt;

			&amp;lt;label&amp;gt;
				Password
				&amp;lt;input name=&quot;password&quot; type=&quot;password&quot; /&amp;gt;
			&amp;lt;/label&amp;gt;

			&amp;lt;button disabled={loading}&amp;gt;Submit&amp;lt;/button&amp;gt;

			{/*
        As I said above, these errors can happen for
        more reasons, like network errors.
        Control these as you desire!
      */}
			{error &amp;amp;&amp;amp; &amp;lt;p className={styles.error}&amp;gt;Bad login/password&amp;lt;/p&amp;gt;}

			&amp;lt;Link to=&quot;/sign_up&quot;&amp;gt;Sign Up&amp;lt;/Link&amp;gt;
		&amp;lt;/form&amp;gt;
	);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, let&apos;s just add a very simple home page so users go somewhere after logging in.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &apos;react&apos;;
import useAuth from &apos;../useAuth&apos;;

import styles from &apos;./index.module.css&apos;;

export default function HomePage() {
	const { user, logout } = useAuth();

	return (
		&amp;lt;div className={styles.root}&amp;gt;
			&amp;lt;p&amp;gt;Hello {user!.email}&amp;lt;/p&amp;gt;

			&amp;lt;button type=&quot;button&quot; onClick={logout}&amp;gt;
				Logout
			&amp;lt;/button&amp;gt;
		&amp;lt;/div&amp;gt;
	);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let&apos;s revisit the root of our app. We are going to use &lt;code&gt;react-router-dom&lt;/code&gt; to build the routes of our app, and we are also going to add a way to protect routes so that only logged-in users are able to access them.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &apos;react&apos;;
import { BrowserRouter, Switch, Route, RouteProps, Redirect } from &apos;react-router-dom&apos;;
import useAuth, { AuthProvider } from &apos;./useAuth&apos;;
import HomePage from &apos;./HomePage&apos;;
import LoginPage from &apos;./LoginPage&apos;;
import SignUpPage from &apos;./SignUpPage&apos;;

// As the router is wrapped with the provider,
// we can use our hook to check for a logged in user.
function AuthenticatedRoute({ roles, ...props }: RouteProps) {
	const { user } = useAuth();

	if (!user) return &amp;lt;Redirect to=&quot;/login&quot; /&amp;gt;;

	return &amp;lt;AsyncRoute {...props} /&amp;gt;;
}

function Router() {
	return (
		&amp;lt;Switch&amp;gt;
			&amp;lt;AuthenticatedRoute exact path=&quot;/&quot; component={HomePage} /&amp;gt;
			&amp;lt;Route exact path=&quot;/login&quot; component={LoginPage} /&amp;gt;
			&amp;lt;Route exact path=&quot;/sign_up&quot; component={SignUpPage} /&amp;gt;
		&amp;lt;/Switch&amp;gt;
	);
}

export default function App() {
	return (
		&amp;lt;BrowserRouter&amp;gt;
			&amp;lt;AuthProvider&amp;gt;
				&amp;lt;Router /&amp;gt;
			&amp;lt;/AuthProvider&amp;gt;
		&amp;lt;/BrowserRouter&amp;gt;
	);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you have protected routes that redirect anonymous users to the login page!&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Hope this is useful for you! This is pretty close to the scenarios we have in production, but the bulk of the logic is all here. Add some robust error handling and you are all set!&lt;/p&gt;
&lt;p&gt;Please check &lt;a href=&quot;https://github.com/finiam/phoenix_starter&quot;&gt;our Phoenix/React starter project&lt;/a&gt; if you want to see this in action. The code is not 100% what you see in this tutorial, and might change as time goes on and our requirements change, but it&apos;s always going to be a great starting point with authentication already handled.&lt;/p&gt;
&lt;p&gt;Stay safe 👋&lt;/p&gt;
</description><pubDate>Mon, 08 Mar 2021 00:00:00 GMT</pubDate><author>Francisco Sousa</author></item><item><title>Next.js and Airtable: a tale of empowering no-code tools</title><link>https://jfranciscosousa.com/blog/nextjs-and-a-tale-of-empowering-no-code-tools/</link><guid isPermaLink="true">https://jfranciscosousa.com/blog/nextjs-and-a-tale-of-empowering-no-code-tools/</guid><description>&lt;p&gt;Originally posted on &lt;a href=&quot;https://blog.finiam.com/blog/next-js-and-airtable-a-tale-of-empowering-no-code-tools/&quot;&gt;Finiam&apos;s blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Back in June, we received a request to help build a website for an academic non-profit organization (our friends at &lt;a href=&quot;https://www.aaum.pt/&quot;&gt;AAUM&lt;/a&gt;), in Braga, Portugal. They needed a website to help spread awareness of the &lt;strong&gt;MinhoCovid19&lt;/strong&gt; movement, a group of people trying to supply organizations in need (nursery homes, hospitals, etc) with protective materials built by volunteers.&lt;/p&gt;
&lt;p&gt;Their operation was relatively simple, in theory at least. They connected donators of either money or materials, to the organizations in need, handling all of the logistics. The volunteers were using &lt;a href=&quot;https://airtable.com/&quot;&gt;Airtable&lt;/a&gt; to manage inventories, deliveries, and also the transparency reports regarding financial movements, which is required to be public by Portuguese laws.&lt;/p&gt;
&lt;h2&gt;Getting technical&lt;/h2&gt;
&lt;p&gt;What they needed was a simple marketing landing page, with some stats that represented the actual progress of the movement. The team at the time was hoping that we could set up a CMS to power the web site&apos;s data and copy, and they would copy over manually some of the data from Airtable to said CMS. Unbeknownst to them, Airtable could solve most of these problems directly, because Airtable itself would generate HTTP APIs for each of the tables they had, and we could directly fetch the data from there.&lt;/p&gt;
&lt;p&gt;So, we decided to build the website with Next.js, as most of the developers that volunteered to help had React experience. Next.js static site generation abilities were also a factor in choosing it, as we didn&apos;t want to manage (and pay for) dedicated hosting. We still plugged in Forestry, a git-based CMS, so the team could edit the website&apos;s copy easily. Then we would just deploy everything from Github to Netlify. As the site is static, costs would be non-existent or pretty low, and we would not be rate-limited by Airtable at all.&lt;/p&gt;
&lt;h2&gt;Show me the code!&lt;/h2&gt;
&lt;p&gt;Disclaimer: not all of the Airtable data could be used on the Website. Currently, the transparency report is being imported from the Airtable, the rest is managed on the CMS side. We are working with the volunteering team to migrate every single data piece to Airtable (except the copy of the website, which will be kept on Forestry).&lt;/p&gt;
&lt;p&gt;With Next.js it&apos;s pretty easy to integrate with Airtable. First, install Airtable:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn add airtable

// or

npm install airtable
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we just need to get an API key on Airtable, you can follow &lt;a href=&quot;https://support.airtable.com/hc/en-us/articles/219046777-How-do-I-get-my-API-key-&quot;&gt;this guide&lt;/a&gt; on how to get there but just look for it on your account settings. I recommend that you create an Airtable &lt;strong&gt;bot&lt;/strong&gt; user, a separate account with access just to the base you need. Each base is basically a project on Airtable so, that way, I avoid using my own API key. If an evil thief gets access to it, all of my bases would get compromised. To even make it more secure, we created a user that only has read-only access to the respective base.&lt;/p&gt;
&lt;p&gt;After getting the API key, just create a &lt;code&gt;.env&lt;/code&gt; file with this content:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AIRTABLE_API_KEY=api_key
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next.js will automatically pick up that value from your &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Now, let&apos;s see the content of our table.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The table labels are in Portuguese, translated to English they are: ID, Purpose, Date, Value. Basically, they represent a list of financial transactions, with their purpose (whether they were donations, material acquisition, etc), their date, and their value.&lt;/p&gt;
&lt;p&gt;Airtable automatically generates an HTTP API for each table. You can play with it on &lt;a href=&quot;https://airtable.com/api&quot;&gt;their API playground&lt;/a&gt; and figure out how to use it. They can even generate Javascript code for listing, retrieval, and create operations. It acts as a good base for what we want to do next.&lt;/p&gt;
&lt;p&gt;Now, we want to get all of these values on our Next.js app. In our case, we slightly changed the generated code to do what we needed. This is how we did it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const Airtable = require(&apos;airtable&apos;);

const base = new Airtable({ apiKey: process.env.AIRTABLE_API_KEY }).base(
	&apos;&amp;lt;HIDDEN BECAUSE SECURITY STUFF&amp;gt;&apos;
);

export default function getTransparencyReport() {
	const totalRecords = [];

	return new Promise((resolve, reject) =&amp;gt; {
		base(&apos;Relatório de Transparência&apos;)
			.select({
				fields: [&apos;Propósito&apos;, &apos;Data&apos;, &apos;Valor&apos;],
				sort: [{ field: &apos;Data&apos;, direction: &apos;desc&apos; }]
			})
			.eachPage(
				function page(records, fetchNextPage) {
					records.forEach((record) =&amp;gt; {
						const id = record.getId();
						const purpose = record.get(&apos;Propósito&apos;);
						const date = record.get(&apos;Data&apos;);
						const value = record.get(&apos;Valor&apos;);

						if (!purpose || !date || !value) return;

						totalRecords.push({
							id,
							purpose,
							date,
							value
						});
					});

					fetchNextPage();
				},
				function done(err) {
					if (err) return reject(err);

					return resolve(totalRecords);
				}
			);
	});
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We extract the fields from the table, sorted by date, and return an array of objects with these keys &lt;code&gt;[date, purpose, value]&lt;/code&gt;. Then, we use Next.js data fetching mechanism &lt;code&gt;getStaticProps&lt;/code&gt; to get this data at &lt;strong&gt;build&lt;/strong&gt; time.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &quot;react&quot;;
import PropTypes from &quot;prop-types&quot;;

import getTransparencyReport from &quot;root/lib/getTransparencyReport&quot;;

// Excluded most of the code for simplicity sake

export default function TransparencyPage({ transparencyReport }) {
  return &amp;lt;&amp;gt;
    {transparencyReport.map(reportLine =&amp;gt; (
        &amp;lt;div key={reportLine.id}&amp;gt;
            &amp;lt;p&amp;gt;{reportLine.date}&amp;lt;/p&amp;gt; &amp;amp;nbsp;
            &amp;lt;p&amp;gt;{reportLine.purpose}&amp;lt;/p&amp;gt; &amp;amp;nbsp;
            &amp;lt;p&amp;gt;{reportLine.value}&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    ))}
  &amp;lt;/&amp;gt;;
}

TransparencyPage.propTypes = {
  transparencyReport: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.string.isRequired,
      date: PropTypes.string.isRequired,
      purpose: PropTypes.string.isRequired,
      value: PropTypes.number.isRequired,
    })
  ).isRequired,
};

export async function getStaticProps() {
  return {
    props: {
      transparencyReport: await getTransparencyReport(),
    },
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&apos;ve omitted most of the code here for easy demonstration purposes. Check the actual real code at our &lt;a href=&quot;https://github.com/cooperativa-tech/minhocovid19&quot;&gt;git repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So, every time we run &lt;code&gt;next build &amp;amp;&amp;amp; next export&lt;/code&gt; we are going to build the entire site and fetch everything from Airtable.&lt;/p&gt;
&lt;h2&gt;Updating Airtable data&lt;/h2&gt;
&lt;p&gt;Now, we are using Next.js as a static site generator. The website data is not live but updates are made daily if not weekly at best. So how do we update the data on Airtable and trigger a site rebuild? If we make changes on the CMS, we trigger &lt;code&gt;git&lt;/code&gt; commits to the repo and Netlify picks that up and rebuilds the site.&lt;/p&gt;
&lt;p&gt;However, Airtable does not have any sort of notification mechanism (like webhooks for example) to trigger Netlify builds (at the time of writing this blog post). The only option that&apos;s left is to schedule periodic builds.&lt;/p&gt;
&lt;p&gt;We decided to settle on 1 periodic build per day, using Github Actions to deploy everything.&lt;/p&gt;
&lt;p&gt;To start building the website on Github Actions, just add the necessary environment variables to your Github project&apos;s &lt;code&gt;secrets&lt;/code&gt; section. Also, set these secrets on your repository settings.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;NETLIFY&lt;em&gt;SITE_ID - Go to _Site settings &amp;gt; General &amp;gt; Site details &amp;gt; Site information&lt;/em&gt;, and copy the value for API ID.&lt;/li&gt;
&lt;li&gt;NETLIFY&lt;em&gt;AUTH_TOKEN - Go to _User settings &amp;gt; Application &amp;gt; New Access Token&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;AIRTABLE_API_KEY - you can use your local AIRTABLE API key&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, we need to define the workflow.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: Daily Netlify Deploy

on:
  schedule:
    - cron: &apos;0 0 * * *&apos;

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v1

      - uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}

      - name: Build website
        run: yarn &amp;amp;&amp;amp; yarn build
        env:
            AIRTABLE_API_KEY: ${{ secrets.AIRTABLE_API_KEY }}

      - name: Upload to netlify
        uses: netlify/actions/cli@master
        with:
            args: deploy --prod
        env:
            NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
            NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are using the &lt;code&gt;schedule&lt;/code&gt; option to trigger this workflow every day at midnight. Then our steps are very simple, we just run our &lt;code&gt;build&lt;/code&gt; script, and use the &lt;code&gt;netlify-cli&lt;/code&gt; action to deploy the website with the &lt;code&gt;prod&lt;/code&gt; flag, which will actually overwrite the existing regular Netlify build with the new one.&lt;/p&gt;
&lt;p&gt;If your project is open-source, Github Actions minutes are completely free. Also, you do not consume Netlify build minutes if you already upload a finished build. I use this pattern on pretty much all of the static websites I build with periodic rebuilds, to keep their data up to date.&lt;/p&gt;
&lt;h2&gt;Final notes&lt;/h2&gt;
&lt;p&gt;This is another example of how no-code tools are not here to put us, developers, out of a job. They have a lot to offer to us via cool integrations like these ones. It also shows that services with good developer docs and APIs, like Airtable, end up being a massive success.&lt;/p&gt;
&lt;p&gt;Feel free to check the &lt;a href=&quot;https://github.com/cooperativa-tech/minhocovid19&quot;&gt;Github repo&lt;/a&gt; for inspiration for your projects.&lt;/p&gt;
&lt;p&gt;Stay safe out there ❤️&lt;/p&gt;
</description><pubDate>Mon, 07 Dec 2020 00:00:00 GMT</pubDate><author>Francisco Sousa</author></item><item><title>Reactive Rails applications with StimulusReflex</title><link>https://jfranciscosousa.com/blog/reactive-rails-applications-with-stimulus-reflex/</link><guid isPermaLink="true">https://jfranciscosousa.com/blog/reactive-rails-applications-with-stimulus-reflex/</guid><description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href=&quot;https://blog.finiam.com/blog/reactive-rails-applications-with-stimulus-reflex/&quot;&gt;Finiam&apos;s website&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A while ago I made a blog post about the modern web with just Rails (and a few other things). Today I&apos;m going to explore another way of doing awesome things with Rails, in the spirit of the modern, reactive, and real-time, web. Let&apos;s explore StimulusReflex, an extension to the amazing library made by Basecamp, to make server-side reactive applications.&lt;/p&gt;
&lt;h2&gt;So what is this Stimulus Reflex thing?&lt;/h2&gt;
&lt;p&gt;StimulusReflex is a Rails library that allows developers to make reactive, real-time apps easily. Much like &lt;a href=&quot;https://github.com/phoenixframework/phoenix_live_view&quot;&gt;Phoenix LiveView&lt;/a&gt; on Elixir land, this library gives us tools to make reactive UIs running mostly on the server-side.&lt;/p&gt;
&lt;p&gt;It uses ActionCable, and their own CableReady extension, to trigger DOM updates from the server-side. It uses WebSockets, so it&apos;s faster than using regular HTTP requests with say, Turbolinks, or just consuming a regular API. Imagine kinda like Turbolinks, but sending HTML via WebSockets, instead of regular requests.&lt;/p&gt;
&lt;p&gt;StimulusReflex adds the ability to write &lt;strong&gt;reflexes&lt;/strong&gt;. Pieces of code that execute asynchronously and trigger a DOM update. Imagine a React component and some callback that triggers a &lt;code&gt;setState&lt;/code&gt; call. These reflexes are fully server-side, so you can read directly from the database and do all kinds of things you would do on a typical Rails controller. You then attach DOM events to these reflexes with &lt;code&gt;data&lt;/code&gt; attributes, kinda like Stimulus. You can also call these reflexes from Stimulus controllers directly.&lt;/p&gt;
&lt;p&gt;Let&apos;s dive right into the action. Imagine a view that renders a group of &lt;code&gt;todo&lt;/code&gt; records. Then we want each todo to have a delete button that removes that todo without refreshing the webpage.&lt;/p&gt;
&lt;p&gt;First, the controller:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class TodosController &amp;lt; ApplicationController
  def index
    @todos = Todo.all
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The respective view:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;% @todos.each do |todo| %&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;p&amp;gt;
      &amp;lt;%= todo.description %&amp;gt;
    &amp;lt;/p&amp;gt;

    &amp;lt;button
      data-reflex=&quot;click-&amp;gt;TodoReflex#delete&quot;
      data-todo_id=&quot;&amp;lt;%= todo.id %&amp;gt;&quot;
    &amp;gt;Delete&amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then our reflex becomes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class TodoReflex &amp;lt; ApplicationReflex
  def delete
    todo = Todo.find(element.dataset[:todo_id])

    todo.destroy
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You use the &lt;code&gt;element&lt;/code&gt; variable, available on all reflexes, to get the DOM element where we define our &lt;code&gt;data-reflex&lt;/code&gt; (our button). Then we get our &lt;code&gt;data-todo_id&lt;/code&gt; from it.&lt;/p&gt;
&lt;p&gt;We then find the todo on the database and delete it. When the method returns, StimulusReflex uses the current controller action to re-render the page with the new info. Then, the new HTML is sent via WebSockets and merged with the previous DOM contents.&lt;/p&gt;
&lt;p&gt;You could replace this functionality with a simple &lt;code&gt;remote: true&lt;/code&gt; Rails form, but as this is submitted with Websockets, the UI update is much faster and gives user&apos;s faster feedback, making the app feel snappier.&lt;/p&gt;
&lt;h2&gt;How the magic happens&lt;/h2&gt;
&lt;p&gt;As I said earlier, StimulusReflex uses CableReady to trigger DOM updates from the server-side. It&apos;s a library developed by the same authors, and you can use it to do some more low-level stuff. It essentially sends DOM instructions through ActionCable, which is a WebSocket library built into Rails.&lt;/p&gt;
&lt;p&gt;The magic happens in those reflex classes. When you trigger a reflex, StimulusReflex essentially rebuilds the current page, calling your controller action again, and it sends that new HTML via the ActionCable socket. Then, the client-side StimulusReflex library morphs the updated DOM with &lt;a href=&quot;https://github.com/patrick-steele-idem/morphdom&quot;&gt;morphdom&lt;/a&gt;. By default, it updates the entire page, but you can reduce the scope of the updates manually to increase performance, check &lt;a href=&quot;https://docs.stimulusreflex.com/morph-modes&quot;&gt;their docs&lt;/a&gt; for more info.&lt;/p&gt;
&lt;p&gt;This is a very quick primer on this amazing library. &lt;a href=&quot;https://docs.stimulusreflex.com/&quot;&gt;Their docs&lt;/a&gt; explain all of this in detail, plus some advanced use cases. It also contains all the instructions to deploy this to production without slowing down the entire application!&lt;/p&gt;
&lt;h2&gt;The classic todo app&lt;/h2&gt;
&lt;p&gt;So let&apos;s revisit that example I showed you earlier. That example called reflexes directly on the markup. It&apos;s a quick way of doing it, but the best way is through a Stimulus controller itself. It gives you more control, allowing you to do cooler things.&lt;/p&gt;
&lt;p&gt;The objective here is to make a simple todo list that allows you to create and delete todos. We are doing this entirely on &lt;code&gt;StimulusRefex&lt;/code&gt; to achieve instant feedback without any full-page refreshes whatsoever. So, the only thing our Rails controller does is rendering the page.&lt;/p&gt;
&lt;p&gt;By the way, I&apos;m assuming you already made the Stimulus and StimulusReflex setup steps, and that you have a model and associated database migration, for a &lt;code&gt;Todo&lt;/code&gt; with a &lt;code&gt;description&lt;/code&gt; text field.&lt;/p&gt;
&lt;p&gt;So, our markup then becomes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;Todos&quot; data-controller=&quot;todo&quot;&amp;gt;
  &amp;lt;% @todos.each do |todo| %&amp;gt;
    &amp;lt;div class=&quot;Todos-row&quot;&amp;gt;
      &amp;lt;p&amp;gt;
        &amp;lt;%= todo.description %&amp;gt;
      &amp;lt;/p&amp;gt;

      &amp;lt;button
        class=&quot;Todos-delete&quot;
        data-action=&quot;click-&amp;gt;todo#delete&quot;
        data-todo_id=&quot;&amp;lt;%= todo.id %&amp;gt;&quot;
      &amp;gt;Delete&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;% end %&amp;gt;

  &amp;lt;%= form_with id: &quot;todo-form&quot;,
        model: @todo,
        class: &quot;Todos-row&quot;,
        data: {
          action: &quot;submit-&amp;gt;todo#create&quot;,
        } do |f|
  %&amp;gt;
    &amp;lt;%= f.text_field :description %&amp;gt;
    &amp;lt;%= f.submit &quot;Add todo&quot; %&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important here is the &lt;code&gt;data&lt;/code&gt; attributes. Firstly, we annotate the top-level &lt;code&gt;div&lt;/code&gt; with &lt;code&gt;data-controller=&quot;todo&quot;&lt;/code&gt; which tells Stimulus that it should attach the &lt;code&gt;todo_controller.js&lt;/code&gt; to this markup. Then we just define two actions. &lt;code&gt;click-&amp;gt;todo#delete&lt;/code&gt; on the button, to delete todos, and &lt;code&gt;submit-&amp;gt;todo#create&lt;/code&gt; to submit the form and create todos. Notice that on the delete button we also set &lt;code&gt;data-todo_id=&quot;&amp;lt;%= todo.id %&amp;gt;&quot;&lt;/code&gt; to so the reflex knows what todo it should delete.&lt;/p&gt;
&lt;p&gt;Now, our Stimulus controller:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import ApplicationController from &apos;./application_controller&apos;;

export default class TodoController extends ApplicationController {
	async delete(event) {
		event.preventDefault();
		event.stopPropagation();

		this.showLoading();
		await this.stimulate(&apos;Todo#delete&apos;, event.currentTarget);
		this.hideLoading();
	}

	async create(event) {
		event.preventDefault();
		event.stopPropagation();

		const form = event.currentTarget;

		this.showLoading();
		await this.stimulate(&apos;Todo#create&apos;, event.currentTarget);
		this.hideLoading();
		form.reset();
	}

	showLoading() {
		document.body.classList.add(&apos;wait&apos;);
	}

	hideLoading() {
		document.body.classList.remove(&apos;wait&apos;);
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we can see the StimulusReflex &lt;code&gt;stimulate&lt;/code&gt; method. It allows you to call reflexes from Stimulus controllers. We pass the &lt;code&gt;event.currentTarget&lt;/code&gt; so our reflexes have access to the DOM element where the event handler was specified (the button and the form).&lt;/p&gt;
&lt;p&gt;We also have two methods to show a little loading spinner! The &lt;code&gt;stimulate&lt;/code&gt; method returns a promise, so you can do things before and after and keep everything in sync. We also &lt;code&gt;reset&lt;/code&gt; the form after submitting it, which doesn&apos;t happen automatically because we are canceling the default events so we don&apos;t submit a real &lt;code&gt;POST&lt;/code&gt; request.&lt;/p&gt;
&lt;p&gt;Finally, our reflex:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class TodoReflex &amp;lt; ApplicationReflex
  def create
    Todo.create(todo_params)
  end

  def delete
    todo = Todo.find(element.dataset[&quot;todo_id&quot;])

    todo.destroy
  end

  private

  def todo_params
    params.require(:todo).permit(:description)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our &lt;code&gt;delete&lt;/code&gt; function remains unchanged, but notice the cool thing about the &lt;code&gt;create&lt;/code&gt;. Because the DOM element that triggered the event is a form, we can use the classic Rails param mechanisms, so the code looks like a classic Rails controller.&lt;/p&gt;
&lt;p&gt;This is everything you need to render a list of todos and also delete and create them as you wish. The styling with CSS is not part of this blog post scope, I&apos;ll leave that up to you.&lt;/p&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;The basic setup of StimulusReflex won&apos;t cut it for most Rails apps. They have an &lt;a href=&quot;https://docs.stimulusreflex.com/authentication&quot;&gt;authentication&lt;/a&gt; and a &lt;a href=&quot;https://docs.stimulusreflex.com/deployment&quot;&gt;deployment&lt;/a&gt; section on their docs that explain the major caveats.&lt;/p&gt;
&lt;p&gt;But, in short, if your app has authentication, you need to identify each ActionCable connection with the currently logged in user, otherwise, the users will see each other&apos;s updates because they will all share the same socket.&lt;/p&gt;
&lt;p&gt;Also, you need to configure your app to use Redis as a cache on a production environment. I recommend doing this in all environments. It&apos;s a good practice for your dev environment to be as similar as possible to your production environment.&lt;/p&gt;
&lt;p&gt;Finally, if you are worried about Rails&apos; ability to scale, just use &lt;a href=&quot;https://anycable.io/&quot;&gt;AnyCable&lt;/a&gt;. It boosts Rails&apos; ActionCable by delegating socket management to a piece of software written in Go which is miles better than Ruby for this workload. Rough benchmarks show that a Rails instance with ActionCable might handle 4000 connections while a single AnyCable node might handle 10000.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;There you go, another cool way of making reactive interfaces without SPAs. It&apos;s a very nice manner of adding bits of reactivity to existing Rails&apos; apps.&lt;/p&gt;
&lt;p&gt;We are using it for internal projects, trying to add some interactivity on some classic Rails apps.&lt;/p&gt;
&lt;p&gt;I hope this has been useful in some way, it&apos;s not a detailed tutorial but more of a showcase of another interesting out-of-the-box technology. If you are interested in this sort of thing (alternatives to SPAs) but are afraid of jumping into Ruby because of performance concerns (an overrated concern), checkout &lt;a href=&quot;https://github.com/phoenixframework/phoenix_live_view&quot;&gt;Phoenix LiveView&lt;/a&gt;. It&apos;s written in Elixir, which runs on the BEAM. Performance will not be a problem whatsoever, and... we might have a blog post coming about that soon ;)&lt;/p&gt;
</description><pubDate>Wed, 30 Sep 2020 00:00:00 GMT</pubDate><author>Francisco Sousa</author></item><item><title>Spicing up your Rails frontend experience</title><link>https://jfranciscosousa.com/blog/spicing-up-your-rails-frontend-experience/</link><guid isPermaLink="true">https://jfranciscosousa.com/blog/spicing-up-your-rails-frontend-experience/</guid><description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href=&quot;https://blog.finiam.com/blog/spicing-up-your-frontend-experience/&quot;&gt;Finiam&apos;s website&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Rails is great. You can still check DHH&apos;s &lt;a href=&quot;https://www.youtube.com/watch?v=Gzj723LkRJY&quot;&gt;15-minute blog demo&lt;/a&gt; and just appreciate the combination of features and ease of use of the framework. Even though there are even easier ways to make a blog nowadays (cough cough Gatsby), Rails is still a rock-solid choice for crafting digital products.&lt;/p&gt;
&lt;h2&gt;Rails and the modern web&lt;/h2&gt;
&lt;p&gt;Despite being a Rails advocate, I mainly used it to build APIs, resorting to React for the frontend stuff. Working with some demanding web designers, I felt that React always had a flexibility that I did not get with HTML and JS. Also, having worked on very large codebases for both full Rails with HTML and Javascript (jQuery at the time) and Rails with a SPA on top, the latter always evolved better in terms of technical debt and time needed to implement features.&lt;/p&gt;
&lt;p&gt;There are pros and cons to each approach and we like to analyze those on a per-project basis. More and more we see great success on teams going with fully server-side approaches, like Basecamp for example. Github is another case of a mostly server-side Rails app that works great (most of the time).&lt;/p&gt;
&lt;p&gt;Even with a fully server-side approach, you can create experiences that for end-users, will be indistinguishable from a fully client-side web app. The instant page transitions, components, and cool APIs to interact with the DOM. Let&apos;s see how.&lt;/p&gt;
&lt;h2&gt;Turbolinks and remote helpers&lt;/h2&gt;
&lt;p&gt;The big deal about SPAs is on its name. &lt;strong&gt;single page applications&lt;/strong&gt; don&apos;t have page transitions, it&apos;s usually done in the client-side and it looks and feels like a native app. An issue with server-side approaches is that every page change (clicking links) triggers a server request and a subsequent full page load. But you can fix that with Turbolinks.&lt;/p&gt;
&lt;p&gt;Perhaps you&apos;ve heard about it, Turbolinks has been on the Rails ecosystem for quite a while. It basically is a piece of Javascript that detects navigation requests, like clicking a link, and then fetches the page and merges the &lt;code&gt;head&lt;/code&gt; and swaps the &lt;code&gt;body&lt;/code&gt;. Thus, changing a page without a full refresh.&lt;/p&gt;
&lt;p&gt;How do you install it? Simple, you don&apos;t, Rails comes with it installed and set up by default! For non-Rails projects it&apos;s pretty simple, just check out &lt;a href=&quot;https://github.com/turbolinks/turbolinks#installation-using-npm&quot;&gt;their documentation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Not only Rails has default Turbolinks support, but it also has a ton of small features to assist in implementing instant user interactions. You can submit forms and trigger controller actions with links and buttons with AJAX and no extra code. &lt;a href=&quot;https://guides.rubyonrails.org/working_with_javascript_in_rails.html#remote-elements&quot;&gt;Their doc page&lt;/a&gt; about working with Javascript has a pretty detailed explanation of these &quot;remote elements&quot;.&lt;/p&gt;
&lt;p&gt;The only thing that is not handled by default in Rails and Turbolinks is when a controller action renders a new template. Turbolinks supports &lt;code&gt;redirect_to&lt;/code&gt; out of the box, but when a template is rendered using &lt;code&gt;render&lt;/code&gt;, it causes a full page refresh. This often happens after you submit a form. Even if you are using the &lt;code&gt;form_with&lt;/code&gt; helper, which triggers an AJAX request instead of the traditional form submission, you will still re-render the page.&lt;/p&gt;
&lt;p&gt;Take for instance, this &lt;code&gt;create&lt;/code&gt; controller action:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def create
  @schedule = Schedule.new(schedule_params)
  @statuses = User.statuses.keys

  if @schedule.valid?
    schedule_status_change(@schedule)

    redirect_to(new_schedule_path, notice: &quot;Scheduled!&quot;)
  else
    flash.now[:alert] = &quot;Invalid!&quot;

    render action: &quot;new&quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default, Turbolinks handles the case where the form is valid because we use &lt;code&gt;redirect_to&lt;/code&gt;. But when the form is invalid, we will have a full page refresh, because we are re-rendering with the &lt;code&gt;render&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;Solution? Just install &lt;a href=&quot;https://github.com/jorgemanrubia/turbolinks_render&quot;&gt;this handy gem&lt;/a&gt; made by Jorge Manrubia. That&apos;s it. Right now, at the time of writing, Turbolinks version 5 doesn&apos;t support this, but the upcoming version 6 will handle this with no extra setup.&lt;/p&gt;
&lt;h2&gt;Components&lt;/h2&gt;
&lt;p&gt;Right now, developers and designers on the web design and implement UI with a component-based approach. It&apos;s easier to organize and re-use code this way. It&apos;s also the way design systems are usually maintained. And now with design systems becoming more and more popular, components even feel like the &lt;strong&gt;way&lt;/strong&gt; to make web applications.&lt;/p&gt;
&lt;p&gt;The traditional SPA assumes this model, usually, and it&apos;s pretty easy to do it. However, on Rails, there is no notion of this, and you are on your own to implement your components. The most popular way is just to use CSS. Naming schemes like SuitCSS and BEM allow you to define reusable CSS classes for your project. But what happens when you also need markup or behavior?&lt;/p&gt;
&lt;p&gt;You can always implement your components using view partials, and your behavior as regular Javascript files, but that isn&apos;t ideal. What if there was a way to encapsulate CSS, JS, and HTML on a nice package, and re-use that throughout your app?&lt;/p&gt;
&lt;p&gt;Introducing &lt;code&gt;view_component&lt;/code&gt;, made by Github. Using this library, you can define all of your components on &lt;code&gt;app/components&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A component is a combination of a Ruby class, an ERB template, a CSS file, and a JS file. You can control server-side behavior on that Ruby class, define your markup on your ERB template and then control the frontend side of things with CSS and JS.&lt;/p&gt;
&lt;p&gt;As an example, I&apos;ll show you the implementation of a flash alert component, that we can use to render Rails flash notices and errors.&lt;/p&gt;
&lt;p&gt;First, we need to define the Ruby class that renders the component. In its basic form, it&apos;s just a regular constructor that you use to set instance variables, kinda like a controller.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;render?&lt;/code&gt; method can be used to determine if we should render the markup of the component. In this case, we don&apos;t render anything at all if we don&apos;t have any flash messages.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class FlashMessageComponent &amp;lt; ViewComponent::Base
  def initialize(flash:)
    @flash = flash
  end

  def render?
    @flash&amp;amp;.keys&amp;amp;.count&amp;amp;.positive?
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we just define markup like we would for a regular Rails view. You can use any template engine you like (&lt;code&gt;slim&lt;/code&gt; for example). I prefer traditional ERB:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div&amp;gt;
	&amp;lt;% @flash.each do |key, value| %&amp;gt;
	&amp;lt;div class=&quot;Flash Flash-&amp;lt;%= key %&amp;gt;&quot;&amp;gt;&amp;lt;%= value %&amp;gt;&amp;lt;/div&amp;gt;
	&amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some basic styling:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.Flash {
	position: fixed;
	top: 12px;
	left: 50%;

	display: inline;
	padding: 0.5rem;

	color: white;

	transform: translateX(-50%);
}

.Flash-notice {
	background-color: cadetblue;
}

.Flash-alert {
	background-color: red;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&apos;s all it takes to define a component. To use it, we just reference it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;%= render(FlashMessageComponent.new(flash: flash)) %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can do this for all sorts of UI elements and it allows you to encapsulate UI logic on these re-usable bits. Now, you probably noticed that we don&apos;t have any JS here. Will touch that in a bit, let&apos;s just talk about Stimulus first.&lt;/p&gt;
&lt;h2&gt;Stimulus&lt;/h2&gt;
&lt;p&gt;Now, our previous example was pretty nice, but we lack JS to make the flash component &lt;em&gt;behave&lt;/em&gt;. When I first started working with Rails (back in 2014) the defacto way was to just use jQuery to make stuff all interactive and nice. And we all know how that ends up with large codebases.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Spaghetti code get it? Yes, it&apos;s a lame joke.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The folks at Basecamp, who pretty much invented Rails anyway, created this simple library (not a framework) to solve the mess of having a bunch of random Javascript with jQuery all over the place. Stimulus uses simple data attributes and small Javascript controllers to add rich interactions to Rails apps (also works without Rails).&lt;/p&gt;
&lt;p&gt;I&apos;m not going super in-depth on Stimulus, their docs are pretty thorough and easy to pick up. Still, let&apos;s see how we can combine &lt;code&gt;Stimulus&lt;/code&gt; with &lt;code&gt;view_component&lt;/code&gt; to make our flash component nicer. Currently, when a flash message is rendered, it stays there forever, so lets us make it go away after some time and also add a button to dismiss it.&lt;/p&gt;
&lt;p&gt;Refer to Stimulus docs to set it up. Then refer to &lt;a href=&quot;https://github.com/github/view_component#stimulus&quot;&gt;this part&lt;/a&gt; of the &lt;code&gt;view_component&lt;/code&gt; docs to integrate both libraries.
By default Stimulus just searches for controllers on the &lt;code&gt;app/javascript/controllers&lt;/code&gt; path. With these integrations, we can also define controllers on the &lt;code&gt;app/components&lt;/code&gt; folder.&lt;/p&gt;
&lt;p&gt;Now, back to our flash component. Let&apos;s change our old markup a little bit. We need to add the button and the respective Stimulus related attributes.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div data-controller=&quot;flash-message&quot;&amp;gt;
  &amp;lt;% @flash.each do |key, value| %&amp;gt;
    &amp;lt;div class=&quot;Flash Flash-&amp;lt;%= key %&amp;gt;&quot;&amp;gt;
      &amp;lt;%= value %&amp;gt;

      &amp;lt;button data-action=&quot;click-&amp;gt;flash-message#close&quot;&amp;gt;Close&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We added the &lt;code&gt;data-controller&lt;/code&gt; attribute, to tie this markup with its respective Stimulus controller. Then we added a button with a &lt;code&gt;data-action&lt;/code&gt; attribute. We specify &lt;code&gt;click-&amp;gt;flash-message#close&lt;/code&gt;, which means we are mapping the &lt;code&gt;click&lt;/code&gt; event with the &lt;code&gt;close&lt;/code&gt; function of our &lt;code&gt;flash-message&lt;/code&gt; Stimulus controller.&lt;/p&gt;
&lt;p&gt;The naming of these files matters a lot, like most Rails stuff. The &lt;code&gt;flash_message_controller&lt;/code&gt; defines the &lt;code&gt;flash_message&lt;/code&gt; controller. If you want to use it on your markup, you must reference it that way, always. And yes you can use it in different places on your HTML. &lt;strong&gt;Warning&lt;/strong&gt;, controllers on &lt;code&gt;app/components&lt;/code&gt; can have name collisions with components on &lt;code&gt;app/javascript/controllers&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, the controller:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { Controller } from &apos;stimulus&apos;;

export default class extends Controller {
	connect = () =&amp;gt; {
		this.timeout = setTimeout(() =&amp;gt; this.close(), 5000);
	};

	disconnect = () =&amp;gt; {
		clearTimeout(this.timeout);
	};

	close = () =&amp;gt; {
		this.element.remove();
	};
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Stimulus controllers have a bunch of lifecycle related functions, you can check those on the docs, but here we are redefining &lt;code&gt;connect&lt;/code&gt; and &lt;code&gt;disconnect&lt;/code&gt;. We use those to set a timeout to close the flash message and also to clear that message if the controller is unloaded. Just a nice touch.&lt;/p&gt;
&lt;p&gt;Then, we get to our &lt;code&gt;close&lt;/code&gt; function that we use on the markup. Every Stimulus controller has access to the respective DOM element where it&apos;s being used. That &lt;code&gt;data-controller&lt;/code&gt; attribute takes care of that. When we use &lt;code&gt;this.element&lt;/code&gt; we are reaching for that DOM piece, then we can just call &lt;code&gt;.remove&lt;/code&gt; to delete that from the DOM, making the flash message go away.&lt;/p&gt;
&lt;h2&gt;A fun alternative&lt;/h2&gt;
&lt;p&gt;Remember, pick technologies that make your team happy and productive! In our case, everyone loves React and everyone loves Rails, but it&apos;s refreshing to just go full Rails once in a while and break out of the norm. If you are interested in solving problems more on the server-side, keep tuned, as we are going to talk about &lt;a href=&quot;https://github.com/hopsoft/stimulus_reflex&quot;&gt;StimulusReflex&lt;/a&gt; for Rails and also &lt;a href=&quot;https://github.com/phoenixframework/phoenix_live_view&quot;&gt;LiveView&lt;/a&gt; for Phoenix.&lt;/p&gt;
&lt;p&gt;The modern web is great because you have a ton of choices on how to do things, let&apos;s cheers on that.&lt;/p&gt;
</description><pubDate>Wed, 19 Aug 2020 00:00:00 GMT</pubDate><author>Francisco Sousa</author></item><item><title>Building and hosting a Discord bot</title><link>https://jfranciscosousa.com/blog/building-and-hosting-discord-bots/</link><guid isPermaLink="true">https://jfranciscosousa.com/blog/building-and-hosting-discord-bots/</guid><description>&lt;p&gt;Discord is one of the premier instant communication platforms on the web right now. Free, easy to use, and just cool, Discord is one of the main hangout apps for gamers, developers, and just overall tech people. One of the coolest things about it is the ability to install bots on your servers that make some cool stuff. Bots that play music, text-based RPGs, dice rollers for RPGs (I made one), meme generators, and many more. In this blog post, I&apos;m going to walk you through on all the steps needed to make your bot and host it for free (kinda free), provided you have some basic &lt;code&gt;ruby&lt;/code&gt; and &lt;code&gt;git&lt;/code&gt; knowledge.&lt;/p&gt;
&lt;h2&gt;Getting started&lt;/h2&gt;
&lt;p&gt;Why Ruby? Well, no reason at all. It&apos;s just my favorite language at the moment of writing. Its also the language I have been using at work for these past few years. And, I find it&apos;s just so simple and easy to understand for people who use it sparingly.&lt;/p&gt;
&lt;p&gt;And why Heroku? It&apos;s a hosting platform that integrates with Github and automatically provides a CI/CD pipeline that continuously deploys your changes. This way you can make changes to your bot, commit that with &lt;code&gt;git&lt;/code&gt; and your bot will just update itself on Heroku. We just need a little hack to make it work there but will get to that.&lt;/p&gt;
&lt;p&gt;So some prerequisites:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Discord account&lt;/li&gt;
&lt;li&gt;Github account&lt;/li&gt;
&lt;li&gt;Heroku account&lt;/li&gt;
&lt;li&gt;Basic Ruby knowledge&lt;/li&gt;
&lt;li&gt;Ruby (latest version) and Git installed and available on your path&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After you create all of these accounts, make sure you create a new Discord app. For that visit &lt;a href=&quot;https://discord.com/developers&quot;&gt;their developer portal&lt;/a&gt;. Click &lt;code&gt;New Application&lt;/code&gt; on the top right corner and follow along.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;After creating it, on the bot&apos;s main page you can take note of the &lt;code&gt;CLIENT ID&lt;/code&gt;, we are going to need it. Then go to the &lt;code&gt;Bot&lt;/code&gt; tab on the side menu and. On that new screen, you can add a bot user to your application. Do it and take note of the &lt;code&gt;TOKEN&lt;/code&gt; value also. All the other options are not needed for now, but you&apos;ll probably explore those later on as you develop your bot.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;The bot&lt;/h2&gt;
&lt;p&gt;Now make sure your directory dedicated to this project and also don&apos;t forget to initialize a git repo, we are going to need it. Now, like most ruby projects, let&apos;s start with a &lt;code&gt;Gemfile&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;source &quot;https://rubygems.org&quot;

ruby &quot;2.7.1&quot;

gem &quot;discordrb&quot;
gem &quot;dotenv&quot;
gem &quot;rake&quot;
gem &quot;zeitwerk&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After creating and saving this file, run &lt;code&gt;bundle install&lt;/code&gt; to install these dependencies.&lt;/p&gt;
&lt;p&gt;Now, what do these gems do? Let&apos;s go through them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;discordrb&lt;/code&gt; - this is a wrapper of the Discord API. We are going to need this to effectively communicate with Discord&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dotenv&lt;/code&gt; - we are going to use this to load the secret keys that we got from Discord&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rake&lt;/code&gt; - the classic ruby task runner. We are going to use this to start the app.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;zeitwerk&lt;/code&gt; - a class loader. We&apos;ll set up this soon.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, let&apos;s write the code that will connect to discord and handle incoming commands. Let&apos;s place it on &lt;code&gt;src/discord_bot.rb&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;module DiscordBot
  throw &quot;Lacking required secrets!&quot; unless ENV[&quot;TOKEN&quot;] &amp;amp;&amp;amp; ENV[&quot;CLIENT_ID&quot;]

  @bot = Discordrb::Bot.new(
    token: ENV[&quot;TOKEN&quot;],
    client_id: ENV[&quot;CLIENT_ID&quot;],
  )

  puts &quot;This bot&apos;s invite URL is #{@bot.invite_url}&quot;
  puts &quot;Click on it to invite it to your server&quot;

  @bot.message(with_text: &quot;!ping&quot;) do |event|
    event.respond &quot;pong&quot;
  end

  def self.run
    @bot.run
  end

  def self.invite_url
    @bot.invite_url
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We use the &lt;code&gt;discordrb&lt;/code&gt; library to create a bot instance using the values from our shell environment.&lt;/p&gt;
&lt;p&gt;Then, we define a handler for a bot command. The handler will intercept every message that matches &lt;code&gt;!ping&lt;/code&gt; and respond to it with a &lt;code&gt;pong&lt;/code&gt; message.&lt;/p&gt;
&lt;p&gt;Then we just declare methods to run the bot and get the invite URL. The bot &lt;code&gt;run&lt;/code&gt; method connects to Discord via a persistent websocket connection. That&apos;s how our bot reads all of the incoming messages from the servers it&apos;s installed in.&lt;/p&gt;
&lt;p&gt;To correctly set the environment, create a file called &lt;code&gt;.env&lt;/code&gt; and add the values you got on the Discord developer portal. The &lt;code&gt;dotenv&lt;/code&gt; gem will take care of loading all of those values into the environment, just when we run our application. On Heroku we will define these variables on their dashboard. But for now, the &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CLIENT_ID=the client id you got on discord dev
TOKEN=the client secret you got on discord dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;DO NOT FORGET TO ADD THIS FILE TO THE GITIGNORE OF YOUR PROJECT&lt;/strong&gt; otherwise, you are leaking secret information to the public.&lt;/p&gt;
&lt;p&gt;Now to wrap everything up, let&apos;s write our &lt;code&gt;Rakefile&lt;/code&gt; so that we can run our discord bot.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ENV[&quot;RUBY_ENV&quot;] ||= &quot;development&quot;
require &quot;bundler/setup&quot;
require &quot;dotenv/load&quot; if ENV[&quot;RUBY_ENV&quot;] != &quot;production&quot;

Bundler.require(:default, ENV[&quot;RUBY_ENV&quot;])
loader = Zeitwerk::Loader.for_gem
loader.push_dir(File.dirname(__FILE__) + &quot;/src&quot;)
loader.setup

task :bot do
  DiscordBot.run
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We wrap the entire file in a conditional that checks if we are running the file with ruby and not requiring it from another ruby file. This is usually done on ruby scripts that are meant to be executed from the command line.&lt;/p&gt;
&lt;p&gt;Then we require the &lt;code&gt;bundler/setup&lt;/code&gt;, so we can automatically require all the gems into the project. We then require &lt;code&gt;dotenv/load&lt;/code&gt; only if we are outside of a production environment. During production, we don&apos;t want to load environment variables from the &lt;code&gt;.env&lt;/code&gt; file, we just want to load them from the Heroku configuration.&lt;/p&gt;
&lt;p&gt;We also set up &lt;code&gt;zeitwerk&lt;/code&gt;, which will automatically require our own files. In this case, we have a &lt;code&gt;src/discord_bot.rb&lt;/code&gt; file which defines a &lt;code&gt;DiscordBot&lt;/code&gt; module. Check &lt;a href=&quot;https://github.com/fxn/zeitwerk#file-structure&quot;&gt;this link&lt;/a&gt; for more info on Zeitwerk file structure. We push the &lt;code&gt;src&lt;/code&gt; dir as a root dir for our code and then we run setup to load it.&lt;/p&gt;
&lt;p&gt;Then we define the task to run the bot. Then, to start it, just run &lt;code&gt;bundle exec rake bot&lt;/code&gt;. Now use the invite URL that shows up in the console and try it on your server! The bot should appear on the online user list and it should respond to a &lt;code&gt;!ping&lt;/code&gt; message with &lt;code&gt;pong&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;How to put this online&lt;/h2&gt;
&lt;p&gt;Now the bot works great and it&apos;s even ready for deployment. You host this on any server that you might have handy. Just send this code over and run &lt;code&gt;rake bot&lt;/code&gt;. But, most of us don&apos;t have handy servers there. This is where Heroku comes in.&lt;/p&gt;
&lt;p&gt;Heroku is a hosting platform for web apps. &lt;em&gt;Web apps&lt;/em&gt; is an important term. Meaning that Heroku expects that your app is an HTTP server of some sort, that binds to a port and responds to incoming request. If you deploy our code to Heroku, it will work for a couple of minutes before shutting down. This is because Heroku shuts down any app that does not bind to any port! So we just need to add a webserver to it.&lt;/p&gt;
&lt;h3&gt;Sinatra&lt;/h3&gt;
&lt;p&gt;We are going to spin up a very simple Sinatra webserver that will have a simple HTML page that will contain the invite URL for our bot. It&apos;s pretty useful stuff. You just share the Heroku app URL with your friends and they&apos;ll be able to invite the bot to their servers.&lt;/p&gt;
&lt;p&gt;Let&apos;s go back to our Gemfile. Just the add &lt;code&gt;gem &quot;sinatra&quot;&lt;/code&gt; to it and then run &lt;code&gt;bundle install&lt;/code&gt; again.&lt;/p&gt;
&lt;p&gt;Now let&apos;s set up our webserver. We need to create a folder at &lt;code&gt;src/webserver&lt;/code&gt; and then one more subfolder at &lt;code&gt;src/webserver/views&lt;/code&gt;. The &lt;code&gt;views&lt;/code&gt; folder will hold all of our HTML templates. In this case, just one.&lt;/p&gt;
&lt;p&gt;Then we create our HTML template at &lt;code&gt;src/webserver/views/index.erb&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;utf-8&quot; /&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&amp;gt;
    &amp;lt;title&amp;gt;The greatest bot&amp;lt;/title&amp;gt;
    &amp;lt;style&amp;gt;
      body {
        margin: 40px auto;
        max-width: 650px;
        line-height: 1.6;
        font-size: 18px;
        color: #444;
        padding: 0 10px;
      }
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Greatest Bot&amp;lt;/h1&amp;gt;

    &amp;lt;p&amp;gt;
      I am the greatest bot. Add me to your server with my glorious
      &amp;lt;a href=&amp;lt;%= @url %&amp;gt;&amp;gt;invite url&amp;lt;/a&amp;gt;.
    &amp;lt;/p&amp;gt;

    &amp;lt;p&amp;gt;
      This is a good place to post instructions on how
      to use your bot by the way!
    &amp;lt;/p&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then create a &lt;code&gt;src/webserver.rb&lt;/code&gt; file and a &lt;code&gt;src/webserver&lt;/code&gt; folder. On our &lt;code&gt;src/webserver.rb&lt;/code&gt; we are going to define the homepage route of our web app.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Webserver &amp;lt; Sinatra::Base
  set :views, File.dirname(__FILE__) + &quot;/webserver/views&quot;

  get &quot;/&quot; do
    @url = DiscordBot.invite_url

    erb :index
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we receive a request on the &lt;code&gt;/&lt;/code&gt; route, we set the &lt;code&gt;@url&lt;/code&gt; with the bot&apos;s invite URL. Then we render the &lt;code&gt;index.erb&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, we just need to add another task to our &lt;code&gt;Rakefile&lt;/code&gt; file to boot up the webserver and the bot at the same time. Add this to the &lt;code&gt;Rakefile&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;task :web do
  Rack::Server.start(
    Port: ENV[&quot;PORT&quot;] || 4567,
    app: Webserver.new,
  )
end

task :all do
  Process.fork { Rake::Task[&quot;bot&quot;].invoke }
  Process.fork { Rake::Task[&quot;web&quot;].invoke }
  Process.waitall
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The web task boots up a webserver that listens for requests at the port defined on our environment or 4657. By default, Heroku adds a &lt;code&gt;PORT&lt;/code&gt; variable that holds the port we need to bind to. On the other hand, the &lt;code&gt;all&lt;/code&gt; task spawns two processes that will run at the same time. One for the webserver and other for the Discord bot itself. This will allow us to run the entire thing for free, more details on that later.&lt;/p&gt;
&lt;p&gt;During development, you can always just work on the bot or the webserver, or both at the same time.&lt;/p&gt;
&lt;p&gt;The final step we need is to create the &lt;code&gt;Procfile&lt;/code&gt;. On this file, we specify what Heroku will run. In this case, we want Heroku to run everything in one dyno, the &lt;code&gt;web&lt;/code&gt; dyno. Heroku can run multiple &lt;code&gt;dynos&lt;/code&gt; of different kinds. We could even have separate dynos, one for the bot and other for the webserver, but to fit on the free tier, we can only use one without running into limits.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;web: rake all
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, to run the bot and the server at the same time do &lt;code&gt;rake all&lt;/code&gt;. This will start both scripts and it will display its outputs on different colors. Very useful for debugging and seeing what&apos;s happening. Oh, and you can visit your shiny website at &lt;a href=&quot;http://localhost:5000&quot;&gt;http://localhost:5000&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Remember, that with this setup you can always run just the bot or just the webserver locally. &lt;code&gt;rake bot&lt;/code&gt; or &lt;code&gt;rake web&lt;/code&gt; will do the trick. Just in case you don&apos;t want to work on the entire thing.&lt;/p&gt;
&lt;h3&gt;Heroku&lt;/h3&gt;
&lt;p&gt;Now, to deploy all of this to Heroku make sure you have all of this code on a Github repo. Then create a new app on Heroku and use the &lt;code&gt;Connect with Github&lt;/code&gt; function. Then just follow to connect with your Github repo. Finally, click the &lt;code&gt;Deploy branch&lt;/code&gt; button to deploy your code. You can optionally enable automatic deploys so every time you commit to master, you trigger a new build.&lt;/p&gt;
&lt;p&gt;Your app will probably not work right away. You need to set the correct environment variables for it to work. Go to the &lt;code&gt;Settings&lt;/code&gt; tab on Heroku. Then on the &lt;code&gt;Config Vars&lt;/code&gt; zone click reveal and then introduce the following variables:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;RUBY_ENV=production
CLIENT_ID=your client id from discord dev portal
TOKEN=your secret token id from discord dev portal
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can use the same values you have on your &lt;code&gt;.env&lt;/code&gt; file for the discord secrets. But note that is a best practice to have different apps and different values for the development and production build. If your bot is used by many people, it&apos;s probably a bad idea to use the same bot app during development, because it&apos;s already running an instance on Heroku and it might introduce downtime for its users. But all it takes is just creating another app as we did earlier, and use those new credentials here on Heroku.&lt;/p&gt;
&lt;p&gt;After setting the environment variables, Heroku should automatically re-release the app. Click the &lt;code&gt;Open App&lt;/code&gt; in the top-right area. It should take you to the website with the invite URL. Use it for your server, then test the bot as you did during development. By the way, the server on your own machine, in case you are using the same credentials for both production (Heroku) and development, just to make sure there are no interferences.&lt;/p&gt;
&lt;h3&gt;Disclaimer&lt;/h3&gt;
&lt;p&gt;Heroku offers a &lt;em&gt;hobby&lt;/em&gt; tier that allows you to host an app for free, provided it runs only a few hours per day. Heroku automatically kills your app after it idles for a while. That&apos;s based on web requests, so even if people are using the bot on Discord, that doesn&apos;t count as traffic for Heroku. Our bot uses websockets to send and receive messages from Discord, so that doesn&apos;t count for Heroku. More info on &lt;a href=&quot;https://link&quot;&gt;their docs page&lt;/a&gt; about the free tier.&lt;/p&gt;
&lt;p&gt;Though, if you add a credit card to your account to validate it, you get enough hours to run a single dyno the entire month 24/7. By fitting all of our tasks into the &lt;code&gt;rake all&lt;/code&gt; task, we can just consume hours in a single dyno. Not ideal, but it works for our use case.&lt;/p&gt;
&lt;h2&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;Now the bot is up running, but it doesn&apos;t do much. The hardest part of getting it up and running on the outside world is done, but now it&apos;s up to you to fill the rest in.&lt;/p&gt;
&lt;p&gt;You should take a look at &lt;code&gt;discordrb&lt;/code&gt; &lt;a href=&quot;https://github.com/discordrb/discordrb/wiki&quot;&gt;wiki page&lt;/a&gt; and also their &lt;a href=&quot;https://www.rubydoc.info/github/meew0/discordrb/Discordrb&quot;&gt;API documentation&lt;/a&gt; for more advanced stuff. Their API is plenty easy to work with.&lt;/p&gt;
&lt;p&gt;You may also see a &quot;real&quot; bot in action using this template and these libraries. I made a bot a while back to roll dice. I use it just for fun and some online Dungeons &amp;amp; Dragons sessions. You can check the code &lt;a href=&quot;https://github.com/jfranciscosousa/dicer&quot;&gt;here on my Github repo&lt;/a&gt; and you can also invite it to your server by visiting &lt;a href=&quot;https://thedicer.herokuapp.com&quot;&gt;it&apos;s home page&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;A final note&lt;/h2&gt;
&lt;p&gt;This will work for hobby projects of sorts, but any bot with a significant amount of servers will perhaps need a little more horsepower to run, and the hobby tier from Heroku won&apos;t cut it. Also, running two processes on one dyno is not the best way to go about a performant. But hey! We usually say that &quot;premature optimization is the root of all evil&quot;, so don&apos;t sweat it. When things get slow, just split off the webserver and the bot in different dynos and boost them (that won&apos;t be free anymore though).&lt;/p&gt;
&lt;p&gt;I packaged the teachings of this blog post into a starter repo that you can use instead of following these steps. I&apos;ll keep that repo updated but there might be the chance that the code is slightly different. People might find better ways to do things and I&apos;ll happily accept that! Anyway, the repo&apos;s README will get you sorted. Check it at &lt;a href=&quot;https://github.com/jfranciscosousa/discordrb-template&quot;&gt;my Github repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Have fun!&lt;/p&gt;
</description><pubDate>Thu, 30 Jul 2020 00:00:00 GMT</pubDate><author>Francisco Sousa</author></item><item><title>Let’s slim down on Javascript</title><link>https://jfranciscosousa.com/blog/lets-slim-down-on-javascript/</link><guid isPermaLink="true">https://jfranciscosousa.com/blog/lets-slim-down-on-javascript/</guid><description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href=&quot;https://blog.finiam.com/blog/lets-slim-down-on-javascript/&quot;&gt;Finiam&apos;s website&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The ancient narrative of &quot;Javascript bloat&quot; is ever-present in the tech world. Web developers love Javascript, backend developers hate it and end-users usually don&apos;t give a damn about it as long as websites open fast, work as they should, and do not spy on them. Still, web pages have way too much Javascript, especially SPAs, that could be avoided with just a few tips. Let&apos;s see how.&lt;/p&gt;
&lt;h2&gt;Why care?&lt;/h2&gt;
&lt;p&gt;Nowadays Internet connections are pretty fast (usually) so why worry if your app has 1 megabyte of JS? Most native mobile apps go into the hundreds of megabytes!&lt;/p&gt;
&lt;p&gt;So, when dealing with Javascript, you are dealing with a scripting language (it’s in the name you see), so the code needs to be read and interpreted by something. In this case, it’s your own browser. Then that code gets loaded into memory and your browser starts doing stuff with it. The problem here is that it takes time and processing power. So if you have a simple news website, and everyone needs to download 4 megabytes of scripts and trackers to be able to see a news article, you probably just lost a reader. Those 4 megabytes of Javascript would take an immense amount of time to be downloaded, read and parsed, especially on mobile devices. Despite the awesomeness of our internet connections, most people access it via WiFi or cellular networks (3G, 4G, etc). These are unreliable and slow even in areas with good coverage. Also, note that the average phone is not that fast.&lt;/p&gt;
&lt;p&gt;The more Javascript you use, the more it takes to fetch a page from the server, the more it takes to render the content, and in the end, the more it takes the user to view the webpage. Even with top-class server tech and CDNs.&lt;/p&gt;
&lt;p&gt;A good starting point is measuring your website performance with Google Lighthouse (the Audits tab on Chrome’s Web Tools). Metrics like Largest Contentful Paint and Time to Interactive are good ways to see if Javascript is slowing down your website loading. &lt;a href=&quot;https://www.webpagetest.org/&quot;&gt;WebPageTest&lt;/a&gt; is also a great tool that allows you to test your website with different devices, like slow smartphones on a limited connection, so you can better test real-world situations.&lt;/p&gt;
&lt;p&gt;Given that, let&apos;s see how we can improve our website&apos;s performance metrics, with a few tips.&lt;/p&gt;
&lt;h2&gt;Ditch SPAs&lt;/h2&gt;
&lt;p&gt;Ok, so this one is a bit extreme and probably only useful before you actually start working on your project. The fastest Javascript is no-Javascript at all. No code is the best code or so they say. By server-rendering your apps you can just ditch client-side Javascript entirely. Using something like Rails, Phoenix, Laravel or any full-stack web framework will allow you to implement web apps entirely rendered on the backend.&lt;/p&gt;
&lt;p&gt;I know, shocking. We made apps like this for ages and it kinda worked out. Check out Basecamp, they&apos;re doing pretty well. Having worked with multiple SPA libraries and server-rendered full-stack frameworks, I can attest that most times, we duplicate a bunch of concepts on the client-side that could just exist as a full-stack unit: routing, data validations, app state, API calls (when full-stack, these just don&apos;t exist).&lt;/p&gt;
&lt;p&gt;Working in a digital agency I often saw SPAs applied to very simple projects, where a classic Rails app would excel. Mea culpa, I’ve done that myself. A client-side approach is great when you want to have separate teams for backend and frontend. Having dedicated teams for both will help teams organize themselves. After all, all these SPA frameworks and libraries were made by very large organizations with very large codebases.&lt;/p&gt;
&lt;p&gt;But even for smaller projects, SPAs excel on highly reactive applications, and any application that is trying to emulate a native experience, through animations and navigation controls, for example, will also benefit from these modern frontend technologies.&lt;/p&gt;
&lt;p&gt;But still, a very good stack for CRUD like apps can be made with Rails and a tiny bit of JS, with Stimulus and Turbolinks. You get the awesome developer experience of Rails, you avoid the dread of full-page refreshes with Turbolinks, then Stimulus helps you write Javascript in a sensible way. No network request handling, no API writing, no manual auth token handling. Just raw productivity.&lt;/p&gt;
&lt;p&gt;And if you are looking for reactivity, check &lt;a href=&quot;https://github.com/hopsoft/stimulus_reflex&quot;&gt;StimulusReflex&lt;/a&gt; (for Rails) and &lt;a href=&quot;https://github.com/phoenixframework/phoenix_live_view&quot;&gt;LiveView&lt;/a&gt; (for Phoenix). Reactivity on the server-side.&lt;/p&gt;
&lt;p&gt;Finally, take a look at &lt;a href=&quot;https://basecamp.com&quot;&gt;Basecamp&lt;/a&gt;, it only uses Rails and a very lightweight Javascript library called Stimulus and good old Turbolinks. For both Mobile and Desktop.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;A final friendly reminder on this matter. Pick something that strikes a good balance between maintainability and your team&apos;s happiness. Don&apos;t switch over to a specific technology or way of doing things just because a random dude on the internet says so! Explore and see what works for you.&lt;/p&gt;
&lt;h2&gt;Use native libraries&lt;/h2&gt;
&lt;p&gt;Now back to Javascript. Often developers reach out for a popular dependency before trying and using the native tools the browser platform offers. It&apos;s ok, for most of the Internet history the default libraries on the browser were pretty unstable or just didn&apos;t work across multiple browser versions. For example, libraries to make network requests are imported into almost every project when the browser already has a powerful tool for that: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API&quot;&gt;fetch&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const response = await fetch(&apos;/api/validator&apos;, {
	method: &apos;POST&apos;,
	headers: {
		&apos;Content-Type&apos;: &apos;application/json&apos;,
		Accept: &apos;application/json&apos;
	},
	body: JSON.stringify(formState)
});

const json = await response.json();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Right now you probably are using something like &lt;code&gt;axios&lt;/code&gt; or &lt;code&gt;superagent&lt;/code&gt; on your web app, both sitting at 4.4kb and 6.4kb total size respectively, at the time of writing this blog post. I resort to &lt;code&gt;axios&lt;/code&gt; all the time due to old habits, but I&apos;ve been replacing it with &lt;code&gt;fetch&lt;/code&gt; and life has been great.&lt;/p&gt;
&lt;p&gt;Most problems pointed to &lt;code&gt;fetch&lt;/code&gt; are its lack of defaults and weird error handling (they only throw errors on network failure and not on bad responses), but that can be fixed with a simple custom wrapper. Check out this awesome &lt;a href=&quot;https://kentcdodds.com/blog/replace-axios-with-a-simple-custom-fetch-wrapper&quot;&gt;blog post&lt;/a&gt; from Kent C. Dodds where he builds a custom wrapper to remove all problems regarding the lack of sane defaults.&lt;/p&gt;
&lt;p&gt;If you want to keep around the axios API, you can always use &lt;a href=&quot;https://github.com/developit/redaxios&quot;&gt;redaxios&lt;/a&gt;. An axios compatible API that uses &lt;code&gt;fetch&lt;/code&gt; under the hood and it&apos;s just 800 bytes!&lt;/p&gt;
&lt;p&gt;And if you need to support older browsers, use &lt;a href=&quot;https://github.com/developit/unfetch&quot;&gt;unfetch&lt;/a&gt; as a polyfill.&lt;/p&gt;
&lt;p&gt;Try and use the browser&apos;s native functionality before going after solutions on npm, you will be surprised. Browsers can do awesome stuff nowadays and almost every single functionality can be polyfilled back into older browsers.&lt;/p&gt;
&lt;h2&gt;Be careful with 3rd party dependencies&lt;/h2&gt;
&lt;p&gt;Even though the browser is a great platform, it&apos;s pretty rare to complete a project without ever using a 3rd party dependency. Even if you maximize the natural power of the browser, there are essential libraries that you will probably need. One thing that happens though, is that people often search for a given library and don&apos;t really think about the consequences. Any library you use will increase the total size of your web app. We should be mindful of that.&lt;/p&gt;
&lt;p&gt;How? Use a tool like &lt;a href=&quot;https://bundlephobia.com/&quot;&gt;Bundlephobia&lt;/a&gt; to inspect the size of a package before using it, and to check if it is tree-shakeable. And what is a tree-shakeable package you might ask? It basically means that most Javascript bundler like Rollup, Webpack, and others will remove the code you do not use from said package.&lt;/p&gt;
&lt;p&gt;For example, if you import &lt;code&gt;lodash&lt;/code&gt; the entire package will end up on your final bundle. However, you can use the alternative &lt;code&gt;lodash-es&lt;/code&gt;, which does the same thing and is tree-shakeable, and you only use the functions you import. As long as you do this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { uniqueId } from &apos;lodash-es&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remember, try to find the right balance between &quot;reinventing the wheel&quot; or adding another dependency. And when you are looking for libraries to solve your problems, pick one that is small and tree-shakeable.&lt;/p&gt;
&lt;p&gt;You can also resort to code-splitting and load polyfills conditionally. I&apos;ll show you how in a bit.&lt;/p&gt;
&lt;h2&gt;Code Splitting&lt;/h2&gt;
&lt;p&gt;If you are using a Javascript bundler, chances are you have the ability to perform code-splitting. It basically consists of splitting your overall Javascript codebase into different modules. It is usually used to not load your entire application at once. If you have a large web app, it&apos;s usually a good idea to perform code-splitting, so your users don&apos;t need to download every single piece of Javascript in your app.&lt;/p&gt;
&lt;p&gt;For example, if you have a React app with &lt;code&gt;react-router&lt;/code&gt; you can perform route-based code splitting. Each different page on your app will have its own module plus a common bundle, which will contain the Javascript code that is common to all different modules. This will reduce the initial load size for each part of the web app but at the cost of having to make a network request every time the route changes.&lt;/p&gt;
&lt;p&gt;I am not going in-depth on implementation details, but you can check the &lt;a href=&quot;https://reactrouter.com/web/guides/code-splitting&quot;&gt;&lt;code&gt;react-router&lt;/code&gt; docs&lt;/a&gt; on the best way of doing this. The important thing to note is that we should only load code that the user needs or will almost surely need in the future.&lt;/p&gt;
&lt;p&gt;Popular frameworks on top of their SPA libraries like Next.js (React), Nuxt (Vue.js), and Sapper (Svelte) do this out of the box via code splitting based on-page components. This is a cool way of going about this since you do need to manually implement this yourself.&lt;/p&gt;
&lt;p&gt;You can even use this strategy to conditionally load dependencies. In the next example, we are importing some polyfills only if the browser does not support the given functionality natively.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (typeof IntersectionObserver === &apos;undefined&apos;) {
	await import(&apos;intersection-observer&apos;);
}

if (typeof Map === &apos;undefined&apos;) {
	await import(&apos;core-js/es6/map&apos;);
}

if (typeof Set === &apos;undefined&apos;) {
	await import(&apos;core-js/es6/set&apos;);
}

if (typeof window.requestAnimationFrame === &apos;undefined&apos;) {
	await import(&apos;raf/polyfill&apos;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apply this to anything you need. You can use this to load different bundles for mobile or desktop. For different user roles, for example, regular users won&apos;t probably need to have the admin dashboard code loaded into their browsers.&lt;/p&gt;
&lt;h2&gt;Don&apos;t support older browsers&lt;/h2&gt;
&lt;p&gt;Dramatic statement. Nowadays you are probably using &lt;code&gt;babel&lt;/code&gt; to transpile your JS code for it to be compatible with older browsers. So every single new feature of the language is then ported back to be supported. If you have IE (Internet Explorer) as a target, then &lt;code&gt;babel&lt;/code&gt; will convert every arrow function into a regular function. Transpiled code is longer, heavier, and probably not as optimized as the code you have actually written.&lt;/p&gt;
&lt;p&gt;How to solve this? Ditch older browsers. I mean, this might seem ridiculous and counter-intuitive at first but older browsers, mostly IE obviously, are insecure, slower, and just plain worse than the alternative. If a computer runs IE it probably can run either Chrome or Firefox. There are a few cases where this is not possible. Some institutions and companies just don&apos;t allow people to update or install applications on their computers, so they are stuck with Windows XP and IE.&lt;/p&gt;
&lt;p&gt;If you cut down the number of polyfills you need, and the transformations your code needs to run on the browser, you can save some serious space. You can also create different Javascript bundles, one for modern browsers and one for older ones. You can check if the user runs IE and ship off the polyfilled bundles to them, but for that, you would need a server that is parsing the user agent of the HTTP requests. If you are making a &lt;a href=&quot;https://auroradigital.co/blog/articles/a-primer-on-the-jamstack&quot;&gt;JAMstack&lt;/a&gt; app you probably can&apos;t parse the user agent string efficiently, and everyone will get the same bundle anyway.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://polyfill.io&quot;&gt;polyfill.io&lt;/a&gt; is a possibility, it loads polyfills conditionally, based on your browser’s version.&lt;/p&gt;
&lt;p&gt;Remember, you can always review your web app user base and check the percentage of users with older browsers. You can track your user&apos;s browser versions respectfully by using a privacy-focused tracker (a bit paradoxical) like &lt;a href=&quot;https://www.goatcounter.com/&quot;&gt;Goatcounter&lt;/a&gt;. They just collect very basic information that cannot uniquely identify users, respecting their privacy. You will probably notice that you don&apos;t have IE users at all (this is the case for the products I&apos;ve been working on at least).&lt;/p&gt;
&lt;p&gt;For the global market share, IE has 3%, but it&apos;s a good idea to scan the market and see if it makes sense to be IE friendly. Imagine that your app is a specific tool for Linux people. They won&apos;t be using IE at all.&lt;/p&gt;
&lt;p&gt;It&apos;s a matter of user research like all great products should have. An app for the enterprise financial market would probably need IE. Lot&apos;s of people on that field are stuck on Windows XP due to organizational restrictions. For a rad startup idea? Probably no IE users will pop up.&lt;/p&gt;
&lt;p&gt;At the very least, make sure your landing page works on IE, then just tell people to upgrade :)&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;The rationale is simple. Be mindful of the cost of Javascript (probably the &lt;a href=&quot;https://v8.dev/blog/cost-of-javascript-2019&quot;&gt;greatest blog post&lt;/a&gt; on this matter), and use simple solutions to deal with it. Don’t forget that premature optimization is the root of all evil, but a couple of tweaks early on in a project’s lifetime can make a world of difference.&lt;/p&gt;
&lt;p&gt;Also, make sure you test your web experiences on slower devices. I cannot say this enough. Not everyone has a Galaxy S20 or the latest shiny iPhone in their pockets.&lt;/p&gt;
&lt;p&gt;It all boils down to the user experience. Make accessible, performant web apps that do just what they are supposed to do, well.&lt;/p&gt;
</description><pubDate>Thu, 23 Jul 2020 00:00:00 GMT</pubDate><author>Francisco Sousa</author></item><item><title>Dealing with webfont loading</title><link>https://jfranciscosousa.com/blog/loading-webfonts/</link><guid isPermaLink="true">https://jfranciscosousa.com/blog/loading-webfonts/</guid><description>&lt;p&gt;The time has come to implement a beautiful website design. It has a great Webfont. So you go to Google Fonts or Adobe&apos;s Typekit or you just buy one and self host it. But once you start writing code and previewing its results, you notice one &lt;em&gt;little&lt;/em&gt; pickle: the whole website shifts, flashes, and janks all over the place when refreshed or opened from a cold cache. It ain&apos;t pretty. Let&apos;s see how we can attenuate those effects.&lt;/p&gt;
&lt;h2&gt;FOIT &amp;amp; FOUT&lt;/h2&gt;
&lt;p&gt;FOIT and FOUT are acronyms likely to pop up when you search for the issues above. If you haven&apos;t heard of them before, this is what they mean:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;FOIT - flash of invisible text. When the website has no fonts rendered until the web font loads.&lt;/li&gt;
&lt;li&gt;FOUT - flash of unstyled text. When the whole typography flashes from an unstyled state to your web font style.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Examples of FOIT (left) and FOUT (right).&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;These issues are manifestations of a root problem. What the hell does your browser do while your web font loads?&lt;/p&gt;
&lt;p&gt;Let&apos;s remember what &lt;code&gt;font-family&lt;/code&gt; does:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;html {
	font-family: &apos;MyRadWebfont&apos;, Arial, sans-serif;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You are telling the browser to use &lt;code&gt;MyRadWebFont&lt;/code&gt; first, then &lt;code&gt;Arial&lt;/code&gt; and, if none of these are available, any &lt;code&gt;sans-serif&lt;/code&gt; font the user&apos;s device might have. &lt;code&gt;Arial&lt;/code&gt; is a safe bet because it&apos;s a font that&apos;s included in every major browser.&lt;/p&gt;
&lt;p&gt;While the web font is loading, you either get a FOIT or FOUT. It usually depends on the user&apos;s browser. A couple of years ago, most browsers applied the FOIT approach, detecting if the website is using a web font and then waiting for it until it becomes available and then swapping the invisible text with the font. Then, some browsers (mainly Firefox) started defaulting to the FOUT approach, to make sure users could see some text while fonts loaded.&lt;/p&gt;
&lt;p&gt;At the time, if you wanted control over this behavior, you had to do it yourself with custom Javascript. Nowadays, there&apos;s an easy way to do it.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;font-display&lt;/code&gt; to the rescue&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;font-display&lt;/code&gt; is a somewhat recent CSS property that can be used to control the way web fonts are loaded and how their fallback is handled. It has awesome &lt;a href=&quot;https://caniuse.com/#search=font-display&quot;&gt;browser support&lt;/a&gt;. However, it doesn&apos;t work on IE. What a surprise.&lt;/p&gt;
&lt;p&gt;The property takes on these values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;block&lt;/strong&gt; - renders nothing at first, then shows the web font. If the web font loading timeouts, it shows the fallback font. An expected FOIT;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;swap&lt;/strong&gt; - renders the fallback, then swaps when the web font is loaded. An expected FOUT;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fallback&lt;/strong&gt; - same as swap, but if the web font takes too long to load, it doesn&apos;t swap and it keeps the fallback. FOUT if it doesn&apos;t timeout;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;optional&lt;/strong&gt; - same as fallback, but the browser &lt;em&gt;decides&lt;/em&gt; if the ideal conditions to download web fonts are present. On a slow internet connection, for example, the browser won&apos;t download it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is the gist of it. You can check out Monica&apos;s in-depth explanation, demo, and talk &lt;a href=&quot;https://font-display.glitch.me/&quot;&gt;on this website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Still, the same FOIT and FOUT happen, but now at least it&apos;s predictable. And you ensure all users will get the same behavior (as long as their browsers support the &lt;code&gt;font-display&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;If you don&apos;t use &lt;strong&gt;Google Fonts&lt;/strong&gt;, you might be unable to specify a &lt;code&gt;font-display&lt;/code&gt; property. Most font providers give you a CSS file with &lt;code&gt;@font-face&lt;/code&gt; declarations that you cannot modify, meaning you cannot add the &lt;code&gt;font-display&lt;/code&gt; property to it. This is an issue with Typekit (which I use at work all the time), where you cannot apply the &lt;code&gt;font-display&lt;/code&gt; property. If you self host your fonts though, all is fine.&lt;/p&gt;
&lt;p&gt;Then the only solution that&apos;s left is actually handling this stuff with Javascript. Controversial, but I&apos;m gonna try and spin up a solution with a progressive enhancement mindset. Works great if you have JS, still works as expected without JS.&lt;/p&gt;
&lt;h2&gt;Smoothing out the FOIT&lt;/h2&gt;
&lt;p&gt;I&apos;ve had this idea where we knowingly apply a FOIT, by hiding the entire website until the font is available, and then fading in the entire thing. The regular FOIT behavior is a bit ugly, because you still have parts of the website rendered and then the whole thing janks and becomes visible. With this, we nicely fade in the entire website and avoid layout shifts.&lt;/p&gt;
&lt;p&gt;We can use the &lt;code&gt;opacity&lt;/code&gt; property for that and then we&apos;ll apply a simple &lt;code&gt;transition&lt;/code&gt;. All with CSS. Then we somehow apply these styles with JS after the font is loaded.&lt;/p&gt;
&lt;p&gt;So, I&apos;ll exemplify with what is powering my own website. The source code is available on &lt;a href=&quot;https://github.com/jfranciscosousa/jfranciscosousa.com&quot;&gt;my Github repo&lt;/a&gt;, which is an Eleventy + Webpack project.&lt;/p&gt;
&lt;p&gt;First, we need to set up &lt;a href=&quot;https://github.com/bramstein/fontfaceobserver&quot;&gt;fontfaceobserver&lt;/a&gt;, either with your favorite JS package manager or by adding it via &lt;a href=&quot;https://cdnjs.com/libraries/fontfaceobserver&quot;&gt;CDN&lt;/a&gt;. Works on IE with a &lt;code&gt;Promise&lt;/code&gt; polyfill. It&apos;s a small dependency, just 1.3kb gzipped.&lt;/p&gt;
&lt;p&gt;The code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// if you can&apos;t require packages, load it via a CDN
const FontFaceObserver = require(&apos;fontfaceobserver&apos;);

const htmlElement = document.documentElement;

htmlElement.classList.remove(&apos;no-js&apos;);
htmlElement.classList.add(&apos;has-js&apos;);

function applyHasFont() {
	htmlElement.classList.remove(&apos;no-font&apos;);
	htmlElement.classList.add(&apos;has-font&apos;);
}

new FontFaceObserver(&apos;Muli&apos;, 2000)
	.load()
	.then(() =&amp;gt; {
		applyHasFont();
	})
	.catch(() =&amp;gt; applyHasFont());
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this, we replace a &lt;code&gt;no-js&lt;/code&gt; class with a &lt;code&gt;has-js&lt;/code&gt; class when the script is loaded. Then, we replace a &lt;code&gt;no-font&lt;/code&gt; with a &lt;code&gt;has-font&lt;/code&gt; class when we load our font, &lt;code&gt;Muli&lt;/code&gt;. We also apply the &lt;code&gt;has-font&lt;/code&gt; class after a 2-second timeout, just in case the user&apos;s internet is really slow and can&apos;t download the font in an acceptable time. The second argument of the &lt;code&gt;FontFaceObserver&lt;/code&gt; constructor is the desired timeout in milliseconds. After that time elapses, the &lt;code&gt;load&lt;/code&gt; function errors.&lt;/p&gt;
&lt;p&gt;Then, on the CSS side of things:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@import url(&apos;https://fonts.googleapis.com/css2?family=Muli:wght@200..900&amp;amp;display=swap&apos;);

html {
	font-family: &apos;Muli&apos;, Arial, sans-serif;
}

.has-js.no-font body {
	opacity: 0;
}

.has-js.has-font body {
	opacity: 1;

	transition: opacity 0.5s;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Also don&apos;t forget to add the default classes to the HTML document:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;html class=&quot;no-js no-font&quot;&amp;gt;
	...
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that we only apply the styles &lt;strong&gt;if&lt;/strong&gt; we have JS. This way, people that (for some reason) have their Javascript disabled, still can see the website. We are passing &lt;code&gt;display=swap&lt;/code&gt; to the Google Fonts API, so that on the lack of Javascript, this will fall back to the &lt;code&gt;swap&lt;/code&gt; behavior.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;I hope this post has been helpful. Personally, I avoid reinventing the wheel with JS for features that can be handled with just CSS, but in this case, a nice compromise can be reached. It respects all users in terms of connection speed and Javascript capabilities and supports different browsers.&lt;/p&gt;
</description><pubDate>Thu, 25 Jun 2020 00:00:00 GMT</pubDate><author>Francisco Sousa</author></item><item><title>A primer on the JAMstack</title><link>https://jfranciscosousa.com/blog/a-primer-on-the-jamstack/</link><guid isPermaLink="true">https://jfranciscosousa.com/blog/a-primer-on-the-jamstack/</guid><description>&lt;p&gt;&lt;em&gt;Originally posted &lt;a href=&quot;https://auroradigital.co/blog/articles/a-primer-on-the-jamstack&quot;&gt;here&lt;/a&gt; on my previous, but very nice, company &lt;a href=&quot;https://auroradigital.co/&quot;&gt;Aurora Digital&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Our own &lt;a href=&quot;https://auroradigital.co/&quot;&gt;website&lt;/a&gt; is a product of the JAMstack. Perhaps you have heard of it if you are a web developer, and if you are not, then this will probably remind you of that jam that your aunt gave you and you never even tasted it and how it&apos;s probably full of mold right now. Nevertheless, the JAMstack is not another boring tech-specific stack. It&apos;s a change of paradigm. A shift in how we develop web apps and websites.&lt;/p&gt;
&lt;h2&gt;The usual web&lt;/h2&gt;
&lt;p&gt;Websites can be developed in all sorts of ways, but most are made by stitching together some HTML, JS, and CSS (the usual web stuff for those of you without a technical background). You drop a bunch of these files on a web server, and now everything is available on the web. Most times you need some way of representing dynamic content, a web page where the structure is the same but only the content changes. A website for a news agency perhaps will need to have a web page for each news article, something like:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;www.awewsomenews.com/news/old-man-wins-the-lottery&lt;/p&gt;
&lt;p&gt;(that&apos;s not a real URL by the way)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Usually, web servers have custom code to handle this, either written by a developer or enabled by some CMS, like Wordpress for example. When a user visits that URL, the webserver code takes the &lt;strong&gt;old-man-wins-the-lottery&lt;/strong&gt; part of the URL, uses that to fetch the specific news article record, which is usually stored on a database, and builds an HTML page with that.&lt;/p&gt;
&lt;p&gt;There, I just told you how most websites work.&lt;/p&gt;
&lt;p&gt;This is perfectly fine, however, there are some underlying issues here. Using traditional web servers to serve your websites is usually a bad idea, at least if you do it yourself. The major issue is performance. If you are serving hundreds of thousands of web pages per second, one web server won&apos;t be enough.&lt;/p&gt;
&lt;h2&gt;The new way&lt;/h2&gt;
&lt;p&gt;So what is the JAMstack? JAM stands for &lt;strong&gt;Javascript, APIs, and Markup.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;But wait? That&apos;s basically what everyone nowadays uses. Yes, but... everything is served via static HTML files. So it becomes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Javascript&lt;/strong&gt; - to make our websites feel and look dynamic&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;APIs&lt;/strong&gt; - our server-side processes and databases live here. They can be self-made or 3rd party&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Markup&lt;/strong&gt; - the structure of our content, our assets and our dependencies, all pre-built and pre-rendered, deployed on a CDN&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Rather than targeting specific frameworks, databases and operative systems, like most stack terminologies, the JAMstack ties itself to the core foundations of modern web applications, not tying developers to specific technologies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The JAMstack is a different philosophy.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Instead of building your pages on a webserver on every request, you would have most of your website pages pre-built and deployed on a CDN. With this, you remove any overhead of maintaining and scaling web servers and or any kind of advanced infrastructure. Instead, you just have static files on a CDN, with super low response times, near-limitless scalability and geographical distribution, so everyone around the world would have a smooth experience while accessing your content.&lt;/p&gt;
&lt;p&gt;Behind this core concept, you find the tool that is often associated with the JAMstack, the static site generator. Super in vogue, with most being optimized for instant page load and cutting edge optimizations, static site generators take your code and data sources and they turn it into static files, ready to be deployed to any CDN of your liking.&lt;/p&gt;
&lt;p&gt;Most static site generators support all kinds of data sources. Your data may be extracted from any existing service or API that you may have. You can also use CMSs to layout your content, and you can even use... more static files! You can have your data laid out as static markdown files (or any type of file really) and import that on your static site generator.&lt;/p&gt;
&lt;p&gt;Then you bundle it all in on a CI/CD pipeline, the only infrastructure you need to manage, that is responsible for rebuilding your website every time you update your code or your data and sending it into a CDN. That&apos;s why it&apos;s essential to keep track of your code on hosted git services, like Github, as they seamlessly integrate with most CI/CD pipelines to trigger builds. If you are using a CMS, you should also integrate it into this pipeline, so content changes also trigger a new build. Check out the &lt;a href=&quot;https://medium.com/subvisual/its-not-continuous-delivery-yet-27a9e838f5df&quot;&gt;blogpost&lt;/a&gt; about CI/CD from our friends at &lt;a href=&quot;https://subvisual.com&quot;&gt;Subvisual&lt;/a&gt; if you need more info about the process!&lt;/p&gt;
&lt;p&gt;If you feel intimidated by developing your own CI/CD pipeline you can use managed services like &lt;a href=&quot;https://netlify.com&quot;&gt;Netlify&lt;/a&gt; or &lt;a href=&quot;https://zeit.co/&quot;&gt;Now&lt;/a&gt;. These platforms integrate directly with hosted git services like Github or Gitlab and they build and deploy your website into a CDN, making the entire process very easy. They also can manage domains, SSL certificates, reducing the entry barrier to production to a few clicks on their dashboards. They offer very generous free plans for developers and hobby projects also, making them a great tool to try out the JAMstack.&lt;/p&gt;
&lt;h2&gt;Static is not boring&lt;/h2&gt;
&lt;p&gt;You still can develop highly interactive web applications by resorting more to APIs instead of generated static HTML. If you are not using third-party services, perhaps you still need to maintain some sort of web server and infrastructure to maintain your APIs, but still, the decoupling that you achieve now will allow you to be more productive and you still serve your frontend from a highly capable CDN, which will boost your performance all the same.&lt;/p&gt;
&lt;p&gt;But even then, you can write your server-side code without servers, by resorting to &lt;strong&gt;serverless&lt;/strong&gt; platforms, allowing you to deploy server-side code, without requiring any infrastructure whatsoever. Adding to their extensive feature set, Netlify and Now also support deploying serverless functions in Javascript and more languages.&lt;/p&gt;
&lt;p&gt;And with modern browsers having a ton of features that were mostly unthinkable in the past, we can push more and more work to the client-side. &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition&quot;&gt;Speech recognition&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API&quot;&gt;3D graphics&lt;/a&gt;,&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps&quot;&gt; PWAs&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/WebAssembly&quot;&gt;Web Assembly&lt;/a&gt; and many more tools are available on most browsers. Things that were possible only in native environments are now a reality on the web.&lt;/p&gt;
&lt;h2&gt;Our stack&lt;/h2&gt;
&lt;p&gt;To build our &lt;a href=&quot;https://auroradigital.co/&quot;&gt;website&lt;/a&gt; we use a static site generator to build the entire website, and our blog posts are managed by a headless CMS. You can check out the source code in our &lt;a href=&quot;https://github.com/aurora-digital/auroradigital.co&quot;&gt;Github repo&lt;/a&gt;. We use Gatsby as a static site generator and Forestry as our CMS. We then build everything using Netlify. With that, we have a wonderful development experience, that allows us to develop features and also fix bugs very quickly.&lt;/p&gt;
&lt;p&gt;With near limitless scalability, we don&apos;t have to worry about scaling the website, it&apos;s just static files on a CDN. Super secure, our third parties all use Github&apos;s OAuth and are strictly audited. And finally, the developer experience is just amazing. All it takes is three steps to being able to contribute to our website: clone the git repository, install dependencies, run the bundled development server.&lt;/p&gt;
&lt;p&gt;The JAMstack is here to stay, and we are putting it into use whenever we can, to build faster, more accessible and more secure websites. In the end, all that matters is our user&apos;s experience.&lt;/p&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;p&gt;You can check these resources and tools to find out more about JAMstack! Check out their blogs for tutorials, guides, and more advanced use cases.&lt;/p&gt;
&lt;h3&gt;Static Site Generators&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gatsbyjs.org/&quot;&gt;Gatsby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/&quot;&gt;Next&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gohugo.io/&quot;&gt;Hugo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;CMSs&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://forestry.io&quot;&gt;Forestry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.contentful.com/&quot;&gt;Contentful&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.sanity.io/&quot;&gt;Sanity&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Platforms&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.netlify.com/&quot;&gt;Netlify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zeit.co/&quot;&gt;Now by Zeit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description><pubDate>Fri, 10 Jan 2020 00:00:00 GMT</pubDate><author>Francisco Sousa</author></item></channel></rss>