# Un servicio conectado con WU

Publica un contrato, autoriza a un cliente y recorre solicitudes RPC con versiones, validación, cancelación y limpieza.

<span class="section-anchor" id="section-1"></span>

## Del cliente a una respuesta

Si es tu primera integración RPC, empieza por [Una suma entre aplicaciones](/es/docs/guides/rpc-between-apps/). 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](/es/labs/backend/), 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.

```text
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.

<span class="section-anchor" id="section-2"></span>

## 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:

```js
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](/es/docs/framework/security/) desarrolla los scopes y su revocación.

<span class="section-anchor" id="section-3"></span>

## Publicar el canal y el contrato

Dentro del montaje del proveedor, `appWu` es esa facade scoped y `handleRequest` implementa el servicio:

```js
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](/es/sources/) 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.

<span class="section-anchor" id="section-4"></span>

## 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:

```js
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](/es/docs/framework/communication/) para sus firmas y errores.

<span class="section-anchor" id="section-5"></span>

## 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](/es/docs/framework/configuration/) y [WU doctor](/es/docs/cli/doctor/) para continuar el diagnóstico de un proyecto.

<span class="section-anchor" id="section-6"></span>

## Cancelar y desmontar sin conservar trabajo activo

Mientras la solicitud siga pendiente, el cliente puede ejecutar:

```js
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ó:

```js
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](/es/docs/framework/lifecycle/) explica cómo vincular esa propiedad de recursos con `mount` y `unmount`.

<span class="section-anchor" id="section-7"></span>

## Leer y comprobar el ejemplo

El [código del proveedor](/microfrontends/backend-service/service.js.txt) muestra el manejo de solicitudes y su limpieza. Para ejecutar las comprobaciones desde el repositorio del portal:

```sh
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.

<span class="section-anchor" id="section-8"></span>

## 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](/es/docs/framework/server-recipes/) e [IA/MCP](/es/docs/framework/ai-recipes/).