Marzo 11, 2024

2 minutos de lectura

Backend con Typescript y ESBuild

Índice:


Disclaimer: ESBuild por si solo no es suficiente para desarrollo backend. En este artículo se cubre un caso puntual en el que tener todo el código transpilado en un solo archivo no refleja ser un problema.

Caso

Backend para consumir una API externa y poder controlar el consumo.

En este caso la API pública de IMDB:

https://github.com/SpEcHiDe/IMDbOT/wiki

gif-block

Empecemos

Requerimientos

  • node v20
  • pnpm

Configuración inicial

Iniciar proyecto:

mkdir ts-backend
cd ts-backend
pnpm init

Dependencias

Del proyecto:

pnpm add -E fastify

En este caso se usará fastify, aunque podría ser cualquier framework.

De desarrollo:

pnpm add -D -E @types/node esbuild

Configuración package.json

Script para compilar el proyecto:

build.js

import esbuild from "esbuild";

esbuild.build({
  entryPoints: ["src/index.ts"],
  bundle: true,
  platform: "node",
  format: "esm",
  outfile: ".out/index.js",
  external: ["fastify"],
});

Es necesario añadir fastify como externo para que esbuild no lo considere al momento de transpilar.

Ahora modificar el package.json con las siguientes propiedades:

{...
  "type": "module",
  "scripts": {
    "build": "node build.js",
    "start": "node .out/index.js"
  },
...}

Punto de entrada

Crear el script principal para levantar el proyecto:

src/index.ts

import Fastify from "fastify";

const fastify = Fastify();

fastify.get("/", () => {
  return { hi: "you" };
});

fastify.listen({ port: 8080 });

Prueba

Ahora toca levantar el servidor con los siguientes comandos:

pnpm run build
pnpm run start

Respuesta:

// http://localhost:8080/
{
  "hi": "you"
}

API

Bastaría con añadir el controlador para consumir la API:

type Movie = {
  title: string;
  year: string;
  url: string;
};

fastify.get("/search-movie/:query", async (request) => {
  const q = (request.params as { query: string }).query;
  const res = await fetch(`https://search.imdbot.workers.dev/?q=${q}`);
  const data = await res.json();
  const movies: Movie[] = data.description?.map((movie) => ({
    title: movie["#TITLE"],
    year: movie["#YEAR"],
    url: movie["#IMDB_URL"],
  }));
  return movies;
});

Levantamos el servidor nuevamente y la respuesta tendría el siguiente formato:

// http://localhost:8080/search-movie/star-wars

[
  {
    "title": "Star Wars: The Bad Batch",
    "year": 2021,
    "url": "https://imdb.com/title/tt12708542"
  },
  {
    "title": "Star Wars: Episode IV - A New Hope",
    "year": 1977,
    "url": "https://imdb.com/title/tt0076759"
  },
...

Extra

Por ir directo al punto, salteé partes importantes a considerar.

Por seguridad es necesario configurar el CORS, para ello se puede utilizar @fastify/cors.

Para la experiencia de desarrollo quedan muchas cosas en el aire, por ejemplo las siguientes dependencias podrían servir de ayuda:

  • pino-pretty: para imprimir mensajes más legibles.
  • supervisor: para detectar cambios en tiempo real de la carpeta src.
  • concurrently: para ejecutar esbuild en modo watch y supervisor.

Caso real

Utilizo esta forma de hacer backend para consumir la API de Gemini Pro. Verifica la autenticación y cantidad de consultas disponibles por usuario a la API.

No confíen en la respuesta de Gemini, pero es cierto que no es escalable. Para este caso puntual, es más que suficiente.