Today I learned


Broadcast Channel

10 oct 2022

While working on the musicroom we’ve had to synchronize every app’s pages on user authentication updates. To do so we’ve been working with the Broadcast Channels. Later I’ve struggled to remember the exact name of the API we’ve been using that’s why I’m writing it down here.

The Broadcast Channel API is a great tool to communicate between several windows/iframes/tabs from the same origin by posting and listening to messages. In this way on the user log in nor log out from a tab, you could reload every app’s pages to avoid unsynced views.

// Listening to broadcast channel messages
broadcastChannel.onmessage = (event) => {
  switch (event.data) {
    case "RELOAD_BROWSER_TABS": {
      // On message reception logic
      break;
    }
    default: {
      console.error("encountered unknown broadcast channel message");
    }
  }
};

// Sending broadcast channel message
broadcastChannel.postMessage("RELOAD_BROWSER_TABS");

// Disconnecting the channel
broadcastChannel.close();

For further information have a look to Broadcast_Channel_API


How to diff two commands output

12 sept 2022

While working on a project, we’ve had to compare two curl requests that both returned a web html page. Instead of saving two tmp files for each curl and then diff the two files, diff allows us to use the following expression:

diff <(command_1) <(command_2)

In this way, to be diffing two curl responses we can use:

diff <(curl url) <(curl url)

Note that curl’s --silent flag can be useful here, see documentation

-s, --silent Silent or quiet mode. Don't show progress meter or error messages. Makes Curl mute. It will still output the data you ask for, potentially even to the terminal/stdout unless you redirect it.

Type checking JavaScript

12 sept 2022

While working on a POC or a small script where we don’t really wanna bring Typescript. We can still type checking a JavaScript file with TypeScript in Visual Studio Code by adding // @ts-check on the top of your file:

// @ts-check

let itsAsEasyAs = "abc";
itsAsEasyAs = 123; // Error: Type '123' is not assignable to type 'string'

Playwright page API testing helper

7 may 2022

While using playwright to end to end testing our application, we had to hit from the test context our API to bypass some verification. We can access from any playwright page an API testing helper. Requests made with this API will use page cookies.

await page.request.get(`${SERVER_ENDPOINT}/test/bypass-email-confirmation`);

In this way inside e2e test we can hit our tests only API routes, using test context authentication.


GitHub action timeout

3 may 2022

I’ve faced an issue where a GitHub action entered an infinite loop. GitHub actions configuration allows you to set a global timeout for a job as follows.

my-job:
    runs-on: ubuntu-latest
    timeout-minutes: 30

Docker compose down volumes

21 apr 2022

Instead of connecting to the container storage to wipe it we can use the --volumes flag.

docker-compose down --volumes

It will remove any container related volumes.


Git checkout less

7 apr 2022

Never too late to discover some basic commands.
Such as cd - will change to previous directory.

git checkout -

Will checkout to the previous git branch.


Redaxios ValidateStatus

20 mar 2022

Axios the great Http client for browser and nodejs also existing as a minified lib called Redaxios allowing you to perform basic RESTful calls. Both defaultly throws an error while encountering an error scoped http status response (default: 200-399 is a success).
While implementing a form validation I needed more granularity to handle errors. In this way axios provides an config option named ValidateStatus

You can either always return true inside validate status manually and handle every http response code

import redaxios from "redaxios";

const rawResponse = await redaxios.get("/route", {
  validateStatus: () => true,
});

console.log(rawResponse.status);
// => can be anything ( 500, 404, 200...)

Or you can provide your own http status range, in the example below redaxios will throw an error on http status code 500

import redaxios from "redaxios";

const rawResponse = await redaxios.get("/route", {
  validateStatus: (status) => status >= 200 && status <= 499,
});

console.log(rawResponse.status);
// => can be anything between 200 and 499

PostgresQL collation implementation depending on OS

10 feb 2022

While creating integration tests on an adonisJS server connected to a Postgres database. I’ve faced an issue where javascript sort by string weight wasn’t matching to Postgres order_by results. It turns out that Postgres uses the collation implementation from the OS he hosted on. source
On a linux OS it’s then not case sensitive at all. Hopefully using docker will easily avoid any further issue in the team you’re working with as everyone will be hosting the Postgres inside identical docker containers.


Remove undefined element from array in typescript

10 feb 2022

I had to use some async method on an array to be generating a new one. As array.filter does not support Promises it leads to be using array.map and Promise.all. However using array.map means that your outputted array will be containing unwanted undefined occurrences, that you could remove using a filter on the end. But this wont be enough for typescript itself, even if indeed it would work on runtime.

const rawResults = await Promise.all(
  array.map(async (_, index) => {
    if (await someAsyncFunction(index)) {
      return index;
    }
  })
);
// const rawResults: (number | undefined)[]

const parsedResults = rawResults.filter((el) => el !== undefined);
// parsedResults still has the same type as rawResults
// const parsedResults: (number | undefined)[]

We need some more TS syntax to make the array type as not containing undefined anymore. Using type predicates we can tell typescript that the filter will return only the expected type

const rawResults = await Promise.all(
  array.map(async (_, index) => {
    if (await someAsyncFunction(index)) {
      return index;
    }
  })
);
// const rawResults: (number | undefined)[]

const parsedResults = rawResults.filter(
  (el: number | undefined): el is number => el !== undefined
);
// const parsedResults: number[]

Array.from second argument

7 feb 2022

Today I learned that Array.from method takes a function as second argument that will be used to fill the created array.

const array = Array.from({ length: 5 }, (_value, index) => index);
// [ 0, 1, 2, 3, 4 ]

Instead of usually

const array = Array.from({ length: 5 }).map((_value, index) => index);
// [ 0, 1, 2, 3, 4 ]

Adonis custom exceptions

1 feb 2022

While implementing some authentication logic on an adonis project. I needed at some point to throw a specific http code status In this case a 403 forbidden.

Adonis thanks to it’s global exceptions handler will manage exceptions for you.

Route.get("dashboard", async ({ auth, response }) => {
  //If someCode comes to throw an error,
  //then adonis global exception listener will handle it
  await auth.Authenfication();

  // business logic
});

//Instead of usually
Route.get("dashboard", async ({ auth, response }) => {
  if (!auth.isLoggedIn) {
    return response.status(401).send("Unauthenticated");
  }

  // business logic
});

Adonis provides an idiomatic way to raise custom exceptions manually.
Using adonis custom exceptions, it’s possible to throw custom exception to the global exception listener at any time.
You can use adonis CLI to init your custom exception as follows

node ace make:exception Forbidden
# CREATE: app/Exceptions/ForbiddenException.ts

Then add static context to the created custom exception if necessary.

//app/Exceptions/ForbiddenException.ts
import { Exception } from "@adonisjs/core/build/standalone";

export default class ForbiddenException extends Exception {
  constructor() {
    const message = "Access forbidden";
    const status = 403;
    const errorCode = "E_FORBIDDEN";

    super(message, status, errorCode);
  }
}

Finally use it inside any route handler

import ForbiddenException from "App/Exceptions/ForbiddenException";

Route.get("dashboard", async ({ auth, response }) => {
  if (someCondition) {
    throw new ForbiddenException();
  }

  // business logic
});