Cap'n Web, un nuevo sistema RPC para navegadores y servidores web
(blog.cloudflare.com)- Cap'n Web es un nuevo protocolo RPC implementado en TypeScript, optimizado para entornos web y capaz de funcionar en varios runtimes de JavaScript
- Ofrece serialización basada en JSON y un formato de datos legible para humanos, sin esquemas ni boilerplate engorroso
- Mediante un modelo basado en capacidades de objetos, permite llamadas bidireccionales, paso de referencias a funciones y objetos, pipeline de promesas e implementación de patrones de seguridad
- Soporta distintos entornos de red como WebSocket, HTTP y postMessage, y es un proyecto open source liviano de menos de 10kB
- Además de resolver el problema de waterfall similar a GraphQL, permite modelar RPC de forma natural, como una API normal de JavaScript
Qué es Cap'n Web
- Cap'n Web es un sistema open source de RPC (protocolo) basado en TypeScript desarrollado por Cloudflare
- Está inspirado en Cap'n Proto, pero funciona sin definir esquemas por separado y adopta una forma de serialización amigable para humanos usando JSON
- Se integra con TypeScript para mejorar la experiencia de desarrollo con autocompletado, verificación de tipos, etc.; la validación de tipos en runtime puede manejarse por separado (por ejemplo, con type guards)
- Soporta protocolos de red como HTTP, WebSocket y postMessage, y funciona en navegadores principales, Cloudflare Workers, Node.js y más
- Tiene una estructura liviana sin dependencias y se distribuye con menos de 10kB tras minify + gzip
El modelo basado en capacidades de objetos (OCap) de Cap'n Web
- Adopta un modelo basado en capacidades de objetos (object-capability) que permite expresar más casos que los sistemas RPC tradicionales
- Llamadas bidireccionales: el cliente y el servidor pueden invocar funciones entre sí
- Paso de referencias a funciones y objetos: si una función u objeto se envía por RPC, la contraparte recibe un stub y, al invocarlo, se ejecuta en su ubicación original
- Promise Pipelining: al encadenar varios RPC, puede resolverse con un solo viaje de ida y vuelta por la red
- Patrones de seguridad: permite implementar de forma natural controles de seguridad como autorización y manejo de sesiones
Uso básico
-
Ejemplo de cliente
import { newWebSocketRpcSession } from "capnweb" let api = newWebSocketRpcSession("wss://example.com/api") let result = await api.hello("World") console.log(result) -
Ejemplo de servidor (basado en Cloudflare Worker)
import { RpcTarget, newWorkersRpcResponse } from "capnweb" class MyApiServer extends RpcTarget { hello(name) { return `Hello, ${name}!` } } export default { fetch(request, env, ctx) { let url = new URL(request.url) if (url.pathname === "/api") { return newWorkersRpcResponse(request, new MyApiServer()) } return new Response("Not found", {status: 404}) } } -
Es fácil agregar métodos a la API, pasar funciones callback del cliente y definir/aplicar interfaces de TypeScript
Qué es RPC y qué distingue a Cap'n Web
- RPC (Remote Procedure Call) es un concepto que permite que dos programas en red se comuniquen como si estuvieran haciendo llamadas a funciones
- A diferencia de los protocolos HTTP/REST tradicionales, RPC permite escribir código con una abstracción de llamada a función que coincide con la forma de pensar del desarrollador
- Cap'n Web encaja bien con el flujo del JavaScript moderno, con soporte para async/await, Promise y Exception
- A diferencia de las controversias históricas sobre RPC (llamadas sincrónicas, errores de red), en el entorno moderno de JS puede usarse de forma más segura y eficiente
Escenarios de uso de Cap'n Web
- Es útil en cualquier entorno donde se necesite comunicación de red entre dos aplicaciones JavaScript
- Cliente-servidor, llamadas entre microservicios, etc.
- Es especialmente adecuado para web apps de colaboración en tiempo real e interacciones que cruzan límites de seguridad complejos
- Está en etapa experimental, por lo que resulta aún más útil para desarrolladores abiertos a adoptar tecnología reciente
Funciones diversas
Modo batch sobre HTTP
-
Cuando no se necesita una conexión persistente, se pueden agrupar varias llamadas RPC de una sola vez con el modo batch de HTTP
import { newHttpBatchRpcSession } from "capnweb" let batch = newHttpBatchRpcSession("https://example.com/api") let result = await batch.hello("World") console.log(result) -
Dentro de un mismo batch, varias llamadas pueden ejecutarse al mismo tiempo y recibir sus resultados en paralelo
let promise1 = batch.hello("Alice") let promise2 = batch.hello("Bob") let [result1, result2] = await Promise.all([promise1, promise2])
Promise Pipelining (llamadas encadenadas)
-
Soporta usar el resultado directamente como argumento de la siguiente llamada sin esperar a que termine la llamada anterior
-
Ejemplo: pasar directamente la Promise del resultado de
getMyName()ahello()para resolverlo en un solo viaje de redlet namePromise = batch.getMyName() let result = await batch.hello(namePromise) -
Las Promise de Cap'n Web funcionan como objetos proxy, de modo que al invocar métodos adicionales pueden encadenarse sin demoras
let sessionPromise = batch.authenticate(apiKey) let name = await sessionPromise.whoami()
Seguridad: autenticación y capacidades de objetos
- Mediante el método
authenticate, al tener éxito se asigna un objeto de permisos (sesión) y luego es posible invocar funciones sin pasos extra de autenticación - A diferencia de otros RPC, el objeto de sesión no puede falsificarse, y no se puede acceder a métodos que requieren permisos sin autenticación
- Supera de forma natural las limitaciones estructurales de WebSocket y garantiza consistencia en la lógica de autenticación
- Al declarar interfaces de API en TypeScript, pueden aplicarse automáticamente entre cliente y servidor, obteniendo autocompletado y seguridad de tipos
Comparación con GraphQL y diferencias de Cap'n Web
-
GraphQL alivia el problema de waterfall de REST, pero requiere introducir un nuevo lenguaje, esquemas y toolchain
-
Cap'n Web resuelve el problema de waterfall usando solo código JavaScript y,
- con soporte para pipeline de promesas y referencias a objetos, permite modelar de forma natural llamadas anidadas o lógica de transacciones compuestas
let user = api.createUser({ name: "Alice" }) let friendRequest = await user.sendFriendRequest("Bob") -
Puede usarse de manera similar a una API de JavaScript, sin la complejidad ni el costo de aprendizaje y administración de GraphQL
Operaciones sobre arreglos (array.map, etc.) y optimización
-
En Cap'n Web es posible hacer operaciones map sobre cada elemento de un arreglo sin viajes adicionales por la red
-
La función callback de
mapse ejecuta una vez en el cliente para registrar la operación (record-replay), se envía al servidor y allí se procesa en bloquelet friendsWithPhotos = friendsPromise.map(friend => { return {friend, photo: api.getUserPhoto(friend.id)} }) let results = await friendsWithPhotos -
A través de un lenguaje específico de dominio (DSL) limitado, se expresa como si fuera una función de JavaScript, pero en realidad usa el protocolo de Cap'n Web para optimizar múltiples llamadas
Estructura interna del protocolo y flujo de comunicación
- Transmite datos estructurados mediante JSON + preprocesamiento especial, con soporte para tipos especiales como arreglos y fechas
- Como protocolo simétrico, permite comunicación bidireccional sin distinción entre cliente y servidor
- Cada parte (por ejemplo, Alice y Bob) administra tablas de export/import y distingue referencias a objetos y funciones mediante IDs
- Mediante mensajes push/pull y asignación de IDs de Promise, es posible reflejar múltiples llamadas en un solo round trip
Estado actual y casos de uso
- Cap'n Web sigue siendo un open source experimental, pero ya se usa en servicios reales como los remote bindings de Cloudflare Wrangler
- Se prevén más publicaciones de blog y varios experimentos de frontend
- Se publica bajo licencia MIT y cualquiera puede adoptarlo libremente
- Ir al repositorio de GitHub
Aún no hay comentarios.