Volver al blog

Síguenos y suscríbete

Sólo disponible en inglés

Por el momento, esta página solo está disponible en inglés. Lamentamos las molestias. Vuelva a visitar esta página más tarde.

Interfaces HTTP como las de Node.js ahora en Compute

Katsuyuki Omuro

Senior Software Engineer, Developer Relations, Fastly

Nuestra plataforma Compute, programada en JavaScript, proporciona objetos Request y Response. Sin embargo, estos se basan en el estándar de captura de datos Fetch, en lugar de en los objetos req y res que suelen manejar los programas Node.js. Si cuentas con algún programa diseñado para Node.js y tienes previsto migrarlo a Compute, o si quieres utilizar una biblioteca diseñada para Node.js, tenemos lo que necesitas: http-compute-js, nuestra nueva biblioteca de código abierto.

Node.js suele proporcionar los objetos IncomingMessage y ServerReponse, que representan, respectivamente, las peticiones enviadas a un servidor web y las respuestas provenientes de este. Sin embargo, no son equiparables a los objetos Request y Response que vienen definidos en el estándar moderno fetch y que JavaScript implementa en Compute. Aunque algunas versiones recientes de Node.js son compatibles con fetch de forma nativa, la mayoría del código Node.js no lo es.

Lo que buscamos con http-compute-js es proporcionar a los desarrolladores objetos que tengan una interfaz compatible con Node.js que les resulte familiar. Así, tus programas o cualquier biblioteca que quieras utilizar podrán interactuar con esos objetos.

Ver para creer: objetos HTTP Node.js en Compute

Echa un vistazo a estas líneas de código:

import http from '@fastly/http-compute-js';

const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify({
    data: 'Hello World!'
  }));
});

server.listen();

Si no fuera por la instrucción import del inicio, se podría decir que es un programa Node.js al uso. La función createServer acepta un controlador de eventos al que se transmite una devolución de llamadas cuyos argumentos se pueden utilizar del mismo modo que en un programa Node.js.

El objeto req es de tipo IncomingMessage y su interfaz de flujos, que es legible, está vinculada al flujo del cuerpo de la petición de Compute. Este objeto se puede leer utilizando el mecanismo habitual de flujos, como on('data') y on('end'); canalizándolo a otro flujo, o utilizando bibliotecas como parse-body. Los encabezados y demás información también se pueden leer como en Node.js.

El objeto res es de tipo ServerResponse y su interfaz de flujos, que es editable, está vinculada a un búfer en memoria. Este objeto se puede editar mediante res.write() o res.end(), o se le pueden canalizar datos mediante res.pipe(). Los encabezados y el código de estado también se pueden configurar como en Node.js.

Todas estas funcionalidades se ven mejor en este otro ejemplo, que es un poco más específico:

import http from '@fastly/http-compute-js';

const server = http.createServer(async (req, res) => {
  // Get URL, method, headers, and body from req
  const url = req.url;
  const method = req.method;
  const headers = {};
  for (let [key, value] of Object.entries(req.headers)) {
    if(!Array.isArray(value)) {
      value = [String(value)];
    }
    headers[key] = value.join(', ');
  }
  let body = null;
  if (method !== 'GET' && method !== 'HEAD') {
    // Reading data out of a stream.Readable 
    body = await new Promise(resolve => {
      const data = [];
      req.on('data', (chunk) => {
        data.push(chunk);
      });
      req.on('end', () => {
        resolve(data.join(''));
      });
    });
  }

  // Write output to res
  res.setHeader('Content-Type', 'application/json');
  res.statusCode = 200;
  res.write(JSON.stringify({
    url,
    method,
    headers,
    body,
  }));
  res.end();
});

server.listen();

Observa que la devolución de llamadas transmitida a createServer es asíncrona. Aunque la respuesta se crea tras la espera, el controlador es capaz de esperar al método res.end() para crear un objeto Response y enviarlo de vuelta a través del controlador de eventos fetch subyacente de Compute.

Los polyfills ayudan a tenerlo todo en orden

Con la idea de conseguir que http-compute-js se comporte de la manera prevista, decidimos utilizar polyfills de eficacia probada con algunas de las partes subyacentes; por ejemplo, para enviar flujos y para almacenar en búfer. Los programas de Compute escritos con JavaScript utilizan webpack para empaquetarse en web workers (trabajos web), lo que nos permite incorporar los polyfills. Así que, para poder utilizar http-compute-js, debes realizar algunos cambios en el archivo webpack.config.js que trae cualquier proyecto JavaScript de Compute. 

Añade webpack.ProvidePlugin() a la matriz «plugins». Además, añade los elementos siguientes a las secciones «alias» y «fallback», de modo que se creen las propiedades «resolve», «alias» y «fallback» según sea necesario. 

module.exports = {
  /* ...other config... */
  plugins: [
    new webpack.ProvidePlugin({
      Buffer: [ 'buffer', 'Buffer' ],
      process: 'process',
      setTimeout: [ 'timeout-polyfill', 'setTimeout' ],
      clearTimeout: [ 'timeout-polyfill', 'clearTimeout' ],
    }),
  ],
  resolve: {
    alias: {
      'timeout-polyfill': require.resolve('@fastly/http-compute-js/dist/polyfill'),
    },
    fallback: {
      'buffer': require.resolve('buffer/'),
      'process': require.resolve('process/browser'),
      'stream': require.resolve('stream-browserify'),
    }
  },
};

Una vez definido lo anterior, los ejemplos que te hemos mostrado en este artículo funcionarán del mismo modo que lo harían en Node.js. Esperamos tener lista pronto la compatibilidad con setTimeout en Compute, ya que nos permitiría reducir el número de polyfills.

Instalación manual de req y res

Es posible que en alguna ocasión tengas que utilizar objetos Request y Response como los de Node.js con solo algunas partes de tu programa. O que quieras realizar una llamada excepcional a una función exclusiva de Node.js. No te preocupes, está todo pensado: proporcionamos utilidades que te permiten combinar objetos Request y Response de Compute con los objetos equivalentes de Node.js.

/// <reference types='@fastly/js-compute' />
import { toReqRes, toComputeResponse } from '@fastly/http-compute-js';

addEventListener('fetch', (event) => event.respondWith(handleRequest(event)));
async function handleRequest(event) {
  // Create Node.js-compatible req and res from event.request
  const { req, res } = toReqRes(event.request);

  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify({
    data: 'Hello World!',
    url: req.url,
  }));

  // Create a Compute@Edge Response object based on res, and return it
  const response = await toComputeResponse(res);
  return response;
}

El ejemplo anterior demuestra lo fácil que es crear objetos req y res compatibles con Node.js como parte de un controlador de capturas (fetch). También ilustra cómo convertirlos a objetos Response de Compute y cómo devolverlos.

Queremos que ejecutes más código en el edge

La evolución de internet como plataforma programable no cesa, y nosotros estamos orgullosos de liderar ese proceso. Por eso, queremos que Compute siga siendo un espacio atractivo para los desarrolladores.

Sabemos que esa incesante evolución significa que todo está en cambio constante. En Fastly, nos centramos en diseñar herramientas que te permitan ejecutar más código todavía en el edge y adaptarte con más rapidez a este nuevo escenario. Y, al mismo tiempo, que te brinden una amplia gama de utilidades. Tenemos muchas ganas de saber qué creas con esta herramienta, cómo la utilizas y si empleas otras de nuestro catálogo. Si te apetece, ¡cuéntanoslo en Twitter!