DéveloppeursSDK TypeScript

SDK Hostwolf Apps pour JavaScript et TypeScript

@hostwolf/apps-sdk est le client typé officiel de l’API Hostwolf Apps. Il vous permet de déployer et de faire tourner vos apps depuis votre code : projets, applications, déploiements et journaux, variables d’environnement, bases de données managées, mise à l’échelle automatique, volumes et plus encore.

npm install https://app.hostwolf.net/dl/hostwolf-apps-sdk-0.2.1.tgz

Le SDK est servi depuis app.hostwolf.net plutôt que depuis le registre npm. La commande ci-dessus l’ajoute à votre package.json sous la forme "@hostwolf/apps-sdk": "https://app.hostwolf.net/dl/hostwolf-apps-sdk-0.2.1.tgz", et vous l’importez sous le nom @hostwolf/apps-sdk. pnpm, yarn et bun acceptent la même URL. Chaque version a sa propre URL ; voir Versions.

Il fonctionne avec Node 18+, Bun et Deno, et n’a aucune dépendance à l’exécution.

Démarrage rapide#

Créez un jeton d’API dans la console sous Clés et jetons → Jetons d’API, puis :

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 }));

Les ressources sont désignées par leur uuid. Les objets renvoyés utilisent les noms de champs de l’API (snake_case), et leurs types TypeScript sont exportés, par ex. Application, Deployment et ManagedDatabase.

Options#

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
});

Projets et environnements#

MéthodeCe que fait l’appel
projects.list() / get(uuid)Projets ; get inclut les environnements
projects.create({ name })Nouveau projet avec un environnement production
projects.update / deleteRenommer, ou supprimer un projet vide
projects.environments(uuid)Environnements d’un projet
projects.environment(uuid, 'staging')Un environnement (par nom ou uuid) avec ses ressources
projects.createEnvironment / updateEnvironment / deleteEnvironmentGérer les environnements
projects.sharedEnvs / environmentSharedEnvsVariables partagées par toutes les apps d’un projet ou d’un environnement

Applications#

MéthodeCe que fait l’appel
applications.list({ tag })Toutes les applications de l’équipe
applications.get(uuid)Une application avec ses réglages
applications.createFromGit(input)À partir d’un dépôt public (nixpacks, railpack, dockerfile ou static)
applications.createFromPrivateRepo(input)À partir d’un dépôt privé avec une clé de déploiement (private_key_uuid)
applications.createFromGithubApp(input)À partir d’un dépôt d’une GitHub App connectée (github_app_uuid) ; les déploiements par push fonctionnent d’emblée
applications.createFromDockerfile(input)À partir du texte d’un Dockerfile, sans dépôt
applications.createFromImage(input)À partir d’une image préconstruite comme ghcr.io/acme/web
applications.update(uuid, changes)Nom, domaines, ports, branche, commandes de build et de démarrage, contrôle de santé, limites
applications.deploy(uuid, { force })Faire le build et démarrer ; renvoie le deployment_uuid
applications.restart / stopRedémarrer sans rebuild, ou arrêter
applications.logs(uuid, { lines })Sortie récente du conteneur (le jeton nécessite read:sensitive)
applications.metrics(uuid, { minutes })Historique du CPU et de la mémoire
applications.rollbackImages / rollback(uuid, tag)Redéployer une image précédente
applications.delete(uuid, { deleteVolumes })Supprimer l’app (et, par défaut, ses volumes)

Les apps tournent sur vos serveurs d’applications. server_uuid est le champ server_uuid de servers.purchased() (ou l’uuid de servers.list()). Omettez domains pour obtenir une adresse *.app.hostwolf.net générée.

Variables d’environnement#

MéthodeCe que fait l’appel
applications.envs(uuid)Variables ; les valeurs valent null sauf si le jeton a read:sensitive
applications.setEnvs(uuid, { KEY: 'value' })En créer ou en mettre à jour plusieurs d’un coup
applications.setEnv(uuid, { key, value, is_shown_once })En créer ou en mettre à jour une
applications.createEnv / updateEnvCréation stricte (409 si elle existe) et mise à jour
applications.deleteEnv(uuid, envUuid) / deleteEnvByKey(uuid, key)Supprimer une variable
teams.sharedEnvs() et méthodes associéesVariables à l’échelle de l’équipe, utilisées sous la forme {{team.KEY}}

Les conteneurs en cours d’exécution voient les variables modifiées après le prochain déploiement.

Déploiements#

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 et deployAndWait lèvent une HostwolfAppsError avec le code deployment_failed (et les dernières lignes du journal dans le message), deployment_cancelled ou timeout. Passez throwOnFailure: false pour récupérer plutôt le déploiement échoué.

MéthodeCe que fait l’appel
deployments.running()Déploiements en file d’attente ou en cours, pour toute l’équipe
deployments.get(uuid)Un déploiement, avec son journal de build dans logs
parseDeploymentLogs(deployment)Le journal de build sous forme de lignes ({ output, type, timestamp })
deployments.forApplication(uuid, { skip, take })Une page des déploiements d’une app, du plus récent au plus ancien
deployments.allForApplication(uuid)Itérateur asynchrone sur l’ensemble
deployments.deploy({ uuid, tag, force })Déployer plusieurs apps par uuid ou par tag
deployments.cancel(uuid)Annuler un déploiement en file d’attente ou en cours

Serveurs#

MéthodeCe que fait l’appel
servers.purchased() / purchasedServer(uuid)Serveurs d’applications et VPS achetés : taille, ville, état, prix et server_uuid
servers.list() / get(uuid)Serveurs d’applications sur lesquels les apps peuvent tourner
servers.resources(uuid) / domains(uuid)Ce qui tourne sur un serveur, et ses domaines
servers.metrics(uuid, { minutes })Historique du CPU et de la mémoire
servers.destinations(uuid)Les réseaux Docker d’un serveur (pour le multi-serveur)
servers.traffic(uuid, { month })Le trafic sortant d’un serveur acheté et sa facture à ce jour (1 To par mois inclus)
servers.teamTraffic({ month })La même chose pour tous les serveurs de l’équipe, avec le total
servers.regions({ size })Les villes avec le prix mensuel par taille, et celles où la mise à l’échelle automatique peut ajouter des serveurs

L’achat et la résiliation de serveurs ne sont possibles que dans la console ; un jeton d’API ne peut donc jamais acheter de serveur.

Multi-serveur#

Faites tourner des copies d’une même app sur plusieurs de vos serveurs d’applications. Hostwolf fait le build de l’image une seule fois, démarre une copie sur chaque serveur et répartit la charge entre elles.

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) arrête de nouveau la copie sur ce serveur.

Bases de données managées#

PostgreSQL, MySQL/MariaDB et Redis sur des serveurs de bases de données Hostwolf, séparés de vos serveurs d’applications, avec TLS, sauvegardes nocturnes hors site et restauration à un instant donné pour 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);

Une base de données est facturée à l’heure sur votre abonnement serveur dès sa création. create envoie une Idempotency-Key, si bien qu’un appel relancé ne crée jamais de seconde base ; passez la vôtre avec { idempotencyKey }.

connection(uuid) renvoie deux URL. url passe par le pooler de connexions (PgBouncer sur le port 6432, ProxySQL sur le 6033) : utilisez-la dans votre app. direct_url atteint directement la base de données : utilisez-la pour les migrations, LISTEN/NOTIFY, les réglages de session et les transactions longues. Redis n’a qu’une URL.

MéthodeCe que fait l’appel
databases.list() / get(uuid)Vos bases de données avec moteur, offre, région, état, prix, limites, upgrades et advice
databases.connection(uuid)Les URL poolée et directe avec le mot de passe (le jeton doit avoir read:sensitive)
databases.attach(uuid, appUuid, { envKey, overwrite })Enregistrer l’URL poolée sous DATABASE_URL et l’URL directe sous DATABASE_DIRECT_URL (Redis : REDIS_URL). Si l’application les a déjà avec une autre valeur, l’appel lève conflict avec error.conflicts et error.suggestedEnvKey ; passez envKey ou overwrite: true
databases.upgrade(uuid, plan, { confirm: true })Passer à une offre supérieure parmi upgrades ; l’URL reste la même
databases.setEvictionPolicy(uuid, policy)Redis : allkeys-lru pour un cache, noeviction pour des files d’attente (appliqué à chaud)
databases.backup(uuid)Lancer une sauvegarde supplémentaire maintenant
databases.delete(uuid)La supprimer et arrêter la facturation ; le support peut la restaurer pendant 7 jours

upgrade sans { confirm: true } ne change rien : il lève une HostwolfAppsError avec le code confirm_required, et error.quote contient le nouveau prix mensuel à afficher d’abord.

Mise à l’échelle automatique#

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 });

La mise à l’échelle automatique ajoute des serveurs d’applications près de vos visiteurs quand le CPU reste élevé, et les retire quand il baisse. Les serveurs ajoutés sont facturés à l’heure au prix normal, jusqu’à monthly_cap_usd. blocker indique pourquoi elle ne peut pas encore être activée (par exemple, aucun abonnement serveur actif). Les champs que vous omettez gardent leur valeur ; disable(uuid) la désactive. cities liste les identifiants de région qu’accepte allowed_cities pour l’app, avec le prix horaire à cet endroit ; servers.regions() liste toutes les villes.

Volumes de stockage#

Du stockage en bloc pour vos serveurs d’applications et VPS : de 10 à 1000 Go, monté sur /mnt/volume-<id> sur le serveur, facturé à l’heure. Les appels qui augmentent la facture nécessitent { confirm: true } ; sans cela, ils ne changent rien et lèvent le code confirm_required avec le prix dans 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éthodeCe que fait l’appel
volumes.list(serverUuid) / listAll({ status }) / get(id)Les volumes d’un serveur, ou tous les volumes de l’équipe (status: 'detached' pour les volumes détachés)
volumes.offer(serverUuid, { sizeGb })Si la ville du serveur propose des volumes, les tailles et un prix
volumes.create(serverUuid, { size_gb, backups, autogrow_max_gb }, { confirm })Ajouter un volume (sauvegardes activées par défaut)
volumes.resize / setBackups / setAutogrow / updateAgrandir (jamais réduire), activer ou désactiver les sauvegardes, régler la limite de croissance automatique
volumes.detach(serverUuid, id)Le démonter et le détacher ; il conserve ses fichiers et reste facturé
volumes.attach(id, serverUuid, { confirm })Rattacher un volume détaché à un serveur de la même ville
volumes.backups(id) / backupNow(serverUuid, id)Les sauvegardes restaurables, ou en lancer une maintenant
volumes.restore(serverUuid, id, backupId, 'same' | 'new', { confirm })Restaurer dans le volume lui-même ou dans un nouveau
volumes.restoreDetached(id, backupId, serverUuid, { confirm })Restaurer la sauvegarde d’un volume détaché dans un nouveau volume
volumes.delete(id, { confirm: true })Le supprimer avec ses fichiers

Quand un serveur est supprimé, ses volumes sont détachés et conservés gratuitement pendant 7 jours (kept_until) ; rattachez-en un à un autre serveur de la même ville d’ici là, sinon il est supprimé.

Volumes d’app#

MéthodeCe que fait l’appel
applications.storages(uuid)Volumes persistants et fichiers montés
applications.createStorage(uuid, { type: 'persistent', name, mount_path })Ajouter un volume (redéployez pour le monter)
applications.updateStorage / deleteStorageEn modifier ou en supprimer une
applications.setVolumeBackup(uuid, storageUuid, { frequency: 'daily' })Sauvegarder un volume selon un planning
applications.runVolumeBackup / deleteVolumeBackupSauvegarder maintenant, ou supprimer le planning

Équipes#

MéthodeCe que fait l’appel
teams.current() / members()L’équipe du jeton et ses membres
teams.update({ name })Renommer l’équipe
teams.auditEvents({ page }) / allAuditEvents()Le journal d’audit de l’équipe (administrateurs et propriétaires)

Pagination#

Les listes paginées par l’API sont proposées de deux façons : un appel renvoie une seule page, et une méthode all… renvoie un itérateur asynchrone sur tous les éléments. collect transforme un itérateur en tableau :

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);

Erreurs#

Les appels qui échouent lèvent une 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;
}

Elle possède ces propriétés :

  • status : le statut HTTP, ou 0 pour les erreurs réseau, les délais dépassés et les déploiements échoués.
  • code : un code court comme validation_failed, unauthenticated, forbidden, not_found, rate_limited, confirm_required, deployment_failed ou timeout.
  • quote : avec confirm_required, le prix annoncé par l’API (affichez-le, puis relancez l’appel avec { confirm: true }).
  • retryAfter : sur une 429, le nombre de secondes avant que l’appel soit de nouveau autorisé. Les attentes courtes sont relancées pour vous ; les plus longues échouent tout de suite avec ce champ renseigné.
  • message : une description lisible, erreurs par champ comprises.
  • errors : les erreurs par champ d’une 422, sous la forme { field: ['message'] }.
  • body : le corps de la réponse. Une 403 sur une action que Hostwolf gère pour vous (comme l’ajout de serveurs) contient un lien docs.
  • Indicateurs raccourcis : isNotFound, isRateLimited, isValidation, isUnauthorized et isConfirmRequired.

Requêtes brutes#

Si un endpoint n’a pas encore de méthode dédiée, appelez-le directement :

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 * * *' });

Les chemins sont relatifs à /api/v1. Consultez la référence de l’API pour tous les endpoints.

Versions#

FichierUtilisation
https://app.hostwolf.net/dl/hostwolf-apps-sdk-.tgzUne version figée. À utiliser dans package.json.
https://app.hostwolf.net/dl/hostwolf-apps-sdk.tgzToujours la dernière version. Les gestionnaires de paquets mettent en cache selon l’URL ; préférez donc les URL versionnées.
https://app.hostwolf.net/dl/index.jsonVersions actuelles, avec empreintes SHA-256 et d’intégrité npm.

Pour mettre à jour, installez l’URL versionnée la plus récente.

Licence#

MIT

Bloqué, ou il vous manque un endpoint ? Dites-le-nous sur app.hostwolf.net/contact ou à [email protected].