Un servicio conectado con WU
Publica un contrato, autoriza a un cliente y recorre solicitudes RPC con versiones, validación, cancelación y limpieza.
Del cliente a una respuesta
Si es tu primera integración RPC, empieza por Una suma entre aplicaciones. Esa receta incluye todos los archivos para tu workspace. Este capítulo amplía el patrón con descubrimiento de contratos, cancelación y el servicio ejecutable del laboratorio.
En Servicios y RPC, dos aplicaciones WU colaboran con permisos diferentes. El proveedor publica qué servicio ofrece y atiende un canal; el cliente consume ese contrato y solicita una respuesta. El recorrido permite probar datos válidos, errores, incompatibilidad de versiones, cancelación y retirada del proveedor.
Cliente WU
→ consume('docs:backend:service', '^1.0.0')
→ request('docs:backend:request', solicitud)
→ handler del proveedor WU
→ respuesta { status, headers, body, execution }
Las solicitudes viajan por el RPC de WU dentro del runtime. status, headers y body son miembros del contrato de respuesta; no significan que el portal haya abierto una conexión HTTP o un puerto de servidor.
Solicitar y conceder la autoridad necesaria
Cada manifest declara las capacidades que su app solicita. El proveedor necesita rpc.provide:docs:backend:*; el cliente, rpc.call:docs:backend:*. El shell concede esos scopes al inicializar ambas aplicaciones:
await wu.init({
sandbox: 'module',
apps: [
{
name: 'backend-service',
url: '/microfrontends/backend-service',
grants: ['rpc.provide:docs:backend:*'],
},
{
name: 'backend-client',
url: '/microfrontends/backend-client',
grants: ['rpc.call:docs:backend:*'],
},
],
});
Las requests del manifest y los grants del shell deben coincidir. El permiso efectivo procede de su intersección. El código de la app utiliza context.wu, la facade scoped recibida en mount; no necesita capturar el singleton privilegiado del host. La referencia de permisos desarrolla los scopes y su revocación.
Publicar el canal y el contrato
Dentro del montaje del proveedor, appWu es esa facade scoped y handleRequest implementa el servicio:
const release = appWu.handle(
'docs:backend:request',
handleRequest,
{ version: '1.0.0', allow: ['backend-client'] },
);
const contract = appWu.provide(
'docs:backend:service',
{ channel: 'docs:backend:request', method: 'POST' },
{
version: '1.0.0',
shape: { channel: 'string', method: 'string' },
},
);
handle registra quién responde al canal. provide publica los datos que necesita el consumidor para descubrirlo. La allowlist del handler limita los solicitantes a backend-client; disponer de un grant no elimina esa comprobación adicional. shape comprueba miembros del contrato y sus tipos, mientras que la validación del cuerpo pertenece al servicio.
En WU Framework 4.0.0, wu.handle(channel, handler, options) y wu.eventBus.handle(channel, handler, options) conservan versión y allowlist. Dentro de una microapp usa context.wu.handle: además de esas opciones, aplica la identidad y los grants de la app. Consulta la página de versiones para identificar el runtime del laboratorio.
El nombre publicado con provide pertenece al primer proveedor. Otra app, o el host al intentar sustituirla, recibe WU_CAPABILITY_OWNED hasta que el propietario ejecute revoke o se desmonte con unmount. La versión del contrato no permite saltarse esa propiedad.
Consumir y llamar desde el cliente
El cliente obtiene un proxy del contrato y solicita un rango compatible. Acceder a api.channel resuelve el proveedor disponible y comprueba ese rango:
const api = appWu.consume('docs:backend:service', '^1.0.0');
const channel = api.channel;
const controller = new AbortController();
const pending = appWu.request(channel, {
method: api.method,
path: '/validate',
body: JSON.stringify({ name: 'catalog', entry: 'entry.js' }),
}, {
version: '^1.0.0',
timeout: 3500,
signal: controller.signal,
});
const response = await pending;
console.log(response.status, response.body);
Hay dos comprobaciones de compatibilidad: la del contrato consumido y la del handler RPC. El escenario de versión incompatible solicita ^2.0.0 al contrato 1.0.0; falla al resolverlo, antes de iniciar trabajo en el servicio. Un proveedor retirado tampoco puede atender una solicitud nueva. Revisa eventos, RPC y contratos para sus firmas y errores.
Diferenciar una respuesta de un rechazo
El servicio espera POST, una ruta conocida y un cuerpo string de hasta 4096 bytes UTF-8.
| Solicitud | Respuesta del servicio |
|---|---|
/validate con JSON válido y name/entry presentes |
200 con ok: true |
/validate con un manifest que no cumple esas reglas |
422 con errores de validación |
/uppercase |
200 con el texto ASCII convertido a mayúsculas |
/failure |
500 para el fallo controlado del servicio |
/slow sin cancelar |
504 al vencer el límite de trabajo de 2500 ms |
| Ruta desconocida | 404 sin iniciar el trabajo |
| Método distinto de POST | 405 |
| Estructura de solicitud inválida | 400 |
| Cuerpo que excede el límite | 413 |
Estas respuestas resuelven la promesa RPC con un objeto: el cliente debe comprobar response.status. La incompatibilidad de contrato, la ausencia del proveedor o la cancelación se manejan como rechazos, mediante try/catch.
La receta de manifest valida únicamente name y entry. Su resultado declara scope: "name+entry" y completeValidation: false; no comprueba el manifest entero, permisos, rutas publicadas ni existencia del entry. Consulta configuración y manifests y WU doctor para continuar el diagnóstico de un proyecto.
Cancelar y desmontar sin conservar trabajo activo
Mientras la solicitud siga pendiente, el cliente puede ejecutar:
controller.abort();
El handler recibe el contexto RPC, que incluye signal. En este ejemplo, el proveedor conecta esa señal con la interrupción de su ejecutor. Un handler que ignore signal puede seguir trabajando aunque el cliente haya dejado de esperar.
El plazo RPC de 3500 ms y el límite de trabajo de 2500 ms tienen efectos diferentes: el primero limita la espera de WU; el segundo permite al servicio terminar el trabajo y responder 504. La interfaz incluye una receta lenta para observar cancelación antes de ambos plazos.
Al desmontar el proveedor, la implementación detiene sus solicitudes activas y retira lo que publicó:
backend.dispose();
release();
contract.revoke();
WU también revoca la autoridad asociada con la app. Al montar de nuevo se registran un handler y un contrato nuevos. Cada petición libera su listener de abort, temporizador y ejecutor al finalizar, fallar o cancelar. La guía de lifecycle explica cómo vincular esa propiedad de recursos con mount y unmount.
Leer y comprobar el ejemplo
El código del proveedor muestra el manejo de solicitudes y su limpieza. Para ejecutar las comprobaciones desde el repositorio del portal:
npm ci
npm run build
npm run dev:shell
# En otra terminal:
npm run test:backend
La prueba recorre respuestas, versión incompatible, revocación, remontaje, timeout, cancelación y desmontaje durante una solicitud. También verifica que la ejecución no conserve Workers al terminar y que el recorrido no produzca errores de página.
Implementación del ejemplo
El proveedor utiliza un programa WASI compilado e incluido en el proyecto, ejecutado en un Worker. Esto permite procesar datos reales y detener el trabajo desde el navegador. El host y los archivos virtuales pertenecen a la implementación del servicio; el contrato público estudiado aquí es WU. El framework no incorpora un motor WASI general.
Puedes sustituir esa implementación por un servicio autorizado de tu aplicación conservando un contrato compatible y probando sus respuestas, errores y limpieza. Un servicio remoto debe volver a validar identidad, permisos y datos. Para renderizar páginas en Node o conectar un cliente MCP externo siguen siendo necesarios sus procesos y transportes: consulta SSR e IA/MCP.