DesarrolladoresSDK de TypeScript
SDK de Hostwolf Apps para JavaScript y TypeScript
@hostwolf/apps-sdk es el cliente tipado oficial de la API de Hostwolf Apps. Con él puedes desplegar y gestionar tus apps desde código: proyectos, aplicaciones, despliegues y registros, variables de entorno, bases de datos gestionadas, autoescalado, volúmenes y más.
npm install https://app.hostwolf.net/dl/hostwolf-apps-sdk-0.2.1.tgz
El SDK se sirve desde app.hostwolf.net en lugar del registro de npm. El comando de arriba lo añade a tu package.json como "@hostwolf/apps-sdk": "https://app.hostwolf.net/dl/hostwolf-apps-sdk-0.2.1.tgz", y lo importas como @hostwolf/apps-sdk. pnpm, yarn y bun aceptan la misma URL. Cada versión tiene su propia URL; consulta Versiones.
Funciona en Node 18+, Bun y Deno, y no tiene dependencias en tiempo de ejecución.
Ver también: Referencia de la API REST · Servidor MCP · Registro de cambios · Consigue un token de API
Inicio rápido#
Crea un token de API en la consola en Claves y tokens → Tokens de API y luego:
import { HostwolfApps } from '@hostwolf/apps-sdk';
const hw = new HostwolfApps({ token: process.env.HOSTWOLF_APPS_TOKEN! });
const server = (await hw.servers.purchased()).find((s) => s.kind === 'app' && s.status === 'ready')!;
const { uuid: project } = await hw.projects.create({ name: 'shop' });
const app = await hw.applications.createFromGit({
project_uuid: project,
environment_name: 'production',
server_uuid: server.server_uuid!,
git_repository: 'https://github.com/acme/shop',
git_branch: 'main',
build_pack: 'nixpacks',
ports_exposes: '3000',
});
await hw.applications.setEnvs(app.uuid, { NODE_ENV: 'production' });
const deployment = await hw.deployAndWait(app.uuid);
console.log(deployment.status, await hw.applications.logs(app.uuid, { lines: 20 }));
Los recursos se identifican por su uuid. Los objetos de respuesta usan los nombres de campo propios de la API (snake_case), y sus tipos de TypeScript se exportan, p. ej. Application, Deployment y ManagedDatabase.
Opciones#
new HostwolfApps({
token: '12|abc...', // required
baseUrl: 'https://console.app.hostwolf.net', // default
timeoutMs: 30_000, // per request
maxRetries: 2, // 429s, and 502/503/504 on GET
maxRetryWaitMs: 10_000, // longer Retry-After waits fail fast instead
language: 'de', // error messages in en, de, fr, es or fi
fetch: customFetch, // optional
});
Proyectos y entornos#
REST: Endpoints de proyectos
| Método | Qué hace |
|---|---|
projects.list() / get(uuid) | Proyectos; get incluye los entornos |
projects.create({ name }) | Proyecto nuevo con un entorno production |
projects.update / delete | Cambia el nombre de un proyecto o elimínalo si está vacío |
projects.environments(uuid) | Entornos de un proyecto |
projects.environment(uuid, 'staging') | Un entorno (por nombre o uuid) con sus recursos |
projects.createEnvironment / updateEnvironment / deleteEnvironment | Gestionar entornos |
projects.sharedEnvs / environmentSharedEnvs | Variables compartidas por todas las apps de un proyecto o entorno |
Aplicaciones#
| Método | Qué hace |
|---|---|
applications.list({ tag }) | Todas las aplicaciones del equipo |
applications.get(uuid) | Una aplicación con sus ajustes |
applications.createFromGit(input) | Desde un repositorio público (nixpacks, railpack, dockerfile o static) |
applications.createFromPrivateRepo(input) | Desde un repositorio privado con una clave de despliegue (private_key_uuid) |
applications.createFromGithubApp(input) | Desde un repositorio de una GitHub App conectada (github_app_uuid); los despliegues por push funcionan sin configurar nada |
applications.createFromDockerfile(input) | Desde el texto de un Dockerfile, sin repositorio |
applications.createFromImage(input) | Desde una imagen ya construida como ghcr.io/acme/web |
applications.update(uuid, changes) | Nombre, dominios, puertos, rama, comandos de build e inicio, health check, límites |
applications.deploy(uuid, { force }) | Hace el build e inicia; devuelve el deployment_uuid |
applications.restart / stop | Reinicia sin rehacer el build, o detén |
applications.logs(uuid, { lines }) | Salida reciente del contenedor (el token necesita read:sensitive) |
applications.metrics(uuid, { minutes }) | Historial de CPU y memoria |
applications.rollbackImages / rollback(uuid, tag) | Vuelve a desplegar una imagen anterior |
applications.delete(uuid, { deleteVolumes }) | Elimina la app (y por defecto sus volúmenes) |
Las apps se ejecutan en tus servidores de aplicaciones. server_uuid es el campo server_uuid de servers.purchased() (o el uuid de servers.list()). Omite domains para obtener una dirección *.app.hostwolf.net generada.
Variables de entorno#
| Método | Qué hace |
|---|---|
applications.envs(uuid) | Variables; los valores son null salvo que el token tenga read:sensitive |
applications.setEnvs(uuid, { KEY: 'value' }) | Crea o actualiza varias a la vez |
applications.setEnv(uuid, { key, value, is_shown_once }) | Crea o actualiza una |
applications.createEnv / updateEnv | Creación estricta (409 si existe) y actualización |
applications.deleteEnv(uuid, envUuid) / deleteEnvByKey(uuid, key) | Elimina una variable |
teams.sharedEnvs() y similares | Variables de todo el equipo, usadas como {{team.KEY}} |
Los contenedores en ejecución ven las variables cambiadas tras el siguiente despliegue.
Despliegues#
REST: Endpoints de despliegues
const { deployment_uuid } = await hw.applications.deploy(app.uuid);
const done = await hw.deployments.wait(deployment_uuid!, {
timeoutMs: 10 * 60_000,
onStatus: (status) => console.log(status), // queued, in_progress, finished
});
wait y deployAndWait lanzan un HostwolfAppsError con el código deployment_failed (y las últimas líneas del registro en el mensaje), deployment_cancelled o timeout. Pasa throwOnFailure: false para recibir el despliegue fallido en su lugar.
| Método | Qué hace |
|---|---|
deployments.running() | Despliegues en cola o en curso ahora, en todo el equipo |
deployments.get(uuid) | Un despliegue, con su registro de build en logs |
parseDeploymentLogs(deployment) | El registro de build como líneas ({ output, type, timestamp }) |
deployments.forApplication(uuid, { skip, take }) | Una página de los despliegues de una app, los más recientes primero |
deployments.allForApplication(uuid) | Iterador asíncrono sobre todos ellos |
deployments.deploy({ uuid, tag, force }) | Despliega varias apps por uuid o etiqueta |
deployments.cancel(uuid) | Cancela un despliegue en cola o en curso |
Servidores#
| Método | Qué hace |
|---|---|
servers.purchased() / purchasedServer(uuid) | Servidores de aplicaciones y VPS que compraste: tamaño, ciudad, estado, precio y server_uuid |
servers.list() / get(uuid) | Servidores de aplicaciones en los que pueden ejecutarse las apps |
servers.resources(uuid) / domains(uuid) | Qué se ejecuta en un servidor, y sus dominios |
servers.metrics(uuid, { minutes }) | Historial de CPU y memoria |
servers.destinations(uuid) | Las redes Docker de un servidor (para varios servidores) |
servers.traffic(uuid, { month }) | Tráfico saliente de un servidor comprado y lo que lleva facturado (1 TB al mes incluido) |
servers.teamTraffic({ month }) | Lo mismo para todos los servidores del equipo, con el total |
servers.regions({ size }) | Ciudades con el precio mensual por tamaño y dónde puede añadir servidores el autoescalado |
Comprar y cancelar servidores solo es posible en la consola, así que un token de API nunca puede comprar un servidor.
Varios servidores#
Ejecuta copias de una app en varios de tus servidores de aplicaciones. Hostwolf hace el build de la imagen una vez, inicia una copia en cada servidor y reparte la carga entre ellas.
await hw.applications.addServer(app.uuid, otherServer.server_uuid!);
await hw.deployAndWait(app.uuid);
console.log(await hw.applications.destinations(app.uuid)); // primary + copies
removeDestination(appUuid, destinationUuid) vuelve a detener la copia en ese servidor.
Bases de datos gestionadas#
PostgreSQL, MySQL/MariaDB y Redis en servidores de bases de datos de Hostwolf, separados de tus servidores de aplicaciones, con TLS, copias de seguridad nocturnas fuera del servidor y recuperación a un momento concreto para PostgreSQL.
const plans = await hw.databases.catalog(); // engines, plans, pool sizes, regions, prices
const db = await hw.databases.create({ engine: 'postgres', plan: 'small', region: 'vultr:ams' });
await hw.databases.waitUntilReady(db.uuid);
await hw.databases.attach(db.uuid, app.uuid); // DATABASE_URL (pooled) + DATABASE_DIRECT_URL
await hw.deployAndWait(app.uuid);
Una base de datos se factura por hora en tu suscripción de servidor desde el momento en que se crea. create envía una Idempotency-Key, así que una llamada repetida nunca crea una segunda base de datos; pasa la tuya con { idempotencyKey }.
connection(uuid) devuelve dos URLs. url pasa por el pooler de conexiones (PgBouncer en el 6432, ProxySQL en el 6033): úsala en tu app. direct_url llega directamente a la base de datos: úsala para migraciones, LISTEN/NOTIFY, ajustes de sesión y transacciones largas. Redis tiene una sola URL.
| Método | Qué hace |
|---|---|
databases.list() / get(uuid) | Tus bases de datos con motor, plan, región, estado, precio, límites, upgrades y advice |
databases.connection(uuid) | Las URLs con pool y directa con la contraseña (el token necesita read:sensitive) |
databases.attach(uuid, appUuid, { envKey, overwrite }) | Guarda la URL con pool como DATABASE_URL y la directa como DATABASE_DIRECT_URL (Redis: REDIS_URL). Si la app ya las tiene con otro valor, lanza conflict con error.conflicts y error.suggestedEnvKey; pasa envKey u overwrite: true |
databases.upgrade(uuid, plan, { confirm: true }) | Pasa a un plan más grande de upgrades; la URL sigue siendo la misma |
databases.setEvictionPolicy(uuid, policy) | Redis: allkeys-lru para una caché, noeviction para colas (se aplica en vivo) |
databases.backup(uuid) | Inicia ahora una copia de seguridad extra |
databases.delete(uuid) | Elimínala y detén la facturación; soporte puede restaurarla durante 7 días |
upgrade sin { confirm: true } no cambia nada: lanza un HostwolfAppsError con el código confirm_required, y error.quote contiene el nuevo precio mensual para que lo muestres antes.
Autoescalado#
const { policy, blocker, status } = await hw.autoscaling.get(app.uuid);
await hw.autoscaling.enable(app.uuid, { min_copies: 1, max_copies: 4, monthly_cap_usd: 40 });
El autoescalado añade servidores de aplicaciones cerca de tus visitantes cuando la CPU se mantiene alta, y los quita cuando baja. Los servidores añadidos se facturan por hora al precio normal, hasta monthly_cap_usd. blocker dice por qué aún no se puede activar (por ejemplo, no hay ninguna suscripción de servidor activa). Los campos que omitas conservan su valor; disable(uuid) lo desactiva. cities lista los ids de región que acepta allowed_cities para la app, con el precio por hora allí; servers.regions() lista todas las ciudades.
Volúmenes de almacenamiento#
Almacenamiento en bloque para tus servidores de aplicaciones y VPS: de 10 a 1000 GB, montado en /mnt/volume-<id> en el servidor y facturado por hora. Las llamadas que aumentan la factura necesitan { confirm: true }; sin él no cambian nada y lanzan el código confirm_required con el precio en error.quote.
const offer = await hw.volumes.offer(server.uuid, { sizeGb: 50 }); // sizes allowed here and the price
const volume = await hw.volumes.create(server.uuid, { size_gb: 50, autogrow_max_gb: 200 }, { confirm: true });
await hw.volumes.waitFor(volume.id); // mounted in about a minute
await hw.volumes.resize(server.uuid, volume.id, 100, { confirm: true });
| Método | Qué hace |
|---|---|
volumes.list(serverUuid) / listAll({ status }) / get(id) | Los volúmenes de un servidor, o todos los volúmenes del equipo (status: 'detached' para los desvinculados) |
volumes.offer(serverUuid, { sizeGb }) | Si la ciudad del servidor vende volúmenes, los tamaños y un precio |
volumes.create(serverUuid, { size_gb, backups, autogrow_max_gb }, { confirm }) | Añade un volumen (copias de seguridad activadas por defecto) |
volumes.resize / setBackups / setAutogrow / update | Ampliar (nunca reducir), activar o desactivar las copias de seguridad, fijar el límite de crecimiento automático |
volumes.detach(serverUuid, id) | Desmóntalo y desvincúlalo; conserva sus archivos y se sigue facturando |
volumes.attach(id, serverUuid, { confirm }) | Vincula un volumen desvinculado a un servidor de la misma ciudad |
volumes.backups(id) / backupNow(serverUuid, id) | Copias de seguridad que se pueden restaurar, o una ahora |
volumes.restore(serverUuid, id, backupId, 'same' | 'new', { confirm }) | Restaura en el propio volumen o en uno nuevo |
volumes.restoreDetached(id, backupId, serverUuid, { confirm }) | Restaura la copia de seguridad de un volumen desvinculado en un volumen nuevo |
volumes.delete(id, { confirm: true }) | Elimínalo junto con sus archivos |
Cuando se elimina un servidor, sus volúmenes se desvinculan y se conservan 7 días, sin coste (kept_until); mientras tanto, vincula uno a otro servidor de la misma ciudad, o se eliminará.
Volúmenes de apps#
| Método | Qué hace |
|---|---|
applications.storages(uuid) | Volúmenes persistentes y archivos montados |
applications.createStorage(uuid, { type: 'persistent', name, mount_path }) | Añade un volumen (vuelve a desplegar para montarlo) |
applications.updateStorage / deleteStorage | Cambia o elimina una |
applications.setVolumeBackup(uuid, storageUuid, { frequency: 'daily' }) | Haz copias de seguridad de un volumen según una programación |
applications.runVolumeBackup / deleteVolumeBackup | Haz una copia ahora o elimina la programación |
Equipos#
| Método | Qué hace |
|---|---|
teams.current() / members() | El equipo del token y sus miembros |
teams.update({ name }) | Cambia el nombre del equipo |
teams.auditEvents({ page }) / allAuditEvents() | El registro de auditoría del equipo (administradores y propietarios) |
Paginación#
Las listas que la API pagina vienen de dos formas: un método devuelve una sola página, y un método all… devuelve un iterador asíncrono sobre todos los elementos. collect convierte un iterador en un array:
import { collect } from '@hostwolf/apps-sdk';
const deployments = await collect(hw.deployments.allForApplication(app.uuid), 100);
for await (const event of hw.teams.allAuditEvents({ action: 'deploy' })) console.log(event.description);
Errores#
Las llamadas fallidas lanzan un HostwolfAppsError.
import { HostwolfAppsError } from '@hostwolf/apps-sdk';
try {
await hw.applications.createFromDockerfile({ project_uuid, server_uuid, dockerfile });
} catch (e) {
if (e instanceof HostwolfAppsError && e.isValidation) console.log(e.errors); // { field: ['message'] }
else throw e;
}
Tiene estas propiedades:
status: el estado HTTP, o 0 para errores de red, tiempos de espera agotados y despliegues fallidos.code: un código corto comovalidation_failed,unauthenticated,forbidden,not_found,rate_limited,confirm_required,deployment_failedotimeout.quote: conconfirm_required, el precio que ha calculado la API (muéstralo y luego vuelve a llamar con{ confirm: true }).retryAfter: en un 429, los segundos que faltan para poder repetir la llamada. Las esperas cortas se reintentan por ti; las más largas fallan al momento con este campo definido.message: una descripción legible, incluidos los errores por campo.errors: errores por campo de un 422, como{ field: ['message'] }.body: el cuerpo de la respuesta. Un 403 por algo que Hostwolf hace por ti (como añadir servidores) incluye un enlacedocs.- Indicadores rápidos:
isNotFound,isRateLimited,isValidation,isUnauthorizedeisConfirmRequired.
Peticiones directas#
Si un endpoint aún no tiene método, llámalo directamente:
await hw.http.get(`/applications/${uuid}/scheduled-tasks`);
await hw.http.post(`/applications/${uuid}/scheduled-tasks`, { name: 'cleanup', command: 'npm run cleanup', frequency: '0 3 * * *' });
Las rutas son relativas a /api/v1. Consulta la referencia de la API para ver todos los endpoints.
Versiones#
| Archivo | Uso |
|---|---|
https://app.hostwolf.net/dl/hostwolf-apps-sdk- | Una versión fija. Úsala en package.json. |
https://app.hostwolf.net/dl/hostwolf-apps-sdk.tgz | Siempre la versión más reciente. Los gestores de paquetes guardan en caché por URL, así que es mejor usar URLs con versión. |
https://app.hostwolf.net/dl/index.json | Versiones actuales, con hashes SHA-256 y de integridad de npm. |
Para actualizar, instala la URL con la versión más nueva.
Licencia#
MIT