API 与自动化 #

Platform API 概述 #

Heroku Platform API 允许通过 REST API 管理应用的各个方面,实现自动化运维。

API 特点 #

特点 说明
RESTful 标准 REST API
JSON 格式 请求和响应都是 JSON
OAuth 认证 使用 Bearer Token
速率限制 每小时 4500 请求

API 基础 URL #

text
https://api.heroku.com

认证 #

获取 API Token #

bash
# 创建 API Token
heroku authorizations:create

# 输出示例
# Client:      <none>
# ID:          12345678-1234-1234-1234-123456789012
# Description: Long-lived user authorization
# Scope:       global
# Token:       xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# Updated at:  Mon Jan 15 2024 10:00:00 GMT+0000

# 或在 Dashboard 创建
# https://dashboard.heroku.com/account/applications

使用 API Token #

bash
# 使用 curl
curl -n https://api.heroku.com/apps \
  -H "Accept: application/vnd.heroku+json; version=3"

# 使用 Token
curl https://api.heroku.com/apps \
  -H "Accept: application/vnd.heroku+json; version=3" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

OAuth 认证 #

bash
# 创建 OAuth 客户端
heroku clients:create "My App" https://example.com/auth/callback

# 输出示例
# === My App
# Client ID:     xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# Client Secret: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

应用管理 API #

创建应用 #

bash
curl -X POST https://api.heroku.com/apps \
  -H "Accept: application/vnd.heroku+json; version=3" \
  -H "Authorization: Bearer $HEROKU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-awesome-app",
    "region": "us"
  }'
javascript
// Node.js 示例
const Heroku = require('heroku-client');
const heroku = new Heroku({ token: process.env.HEROKU_API_KEY });

async function createApp(name) {
  const app = await heroku.post('/apps', {
    body: { name, region: 'us' }
  });
  return app;
}

获取应用信息 #

bash
curl https://api.heroku.com/apps/my-app \
  -H "Accept: application/vnd.heroku+json; version=3" \
  -H "Authorization: Bearer $HEROKU_API_KEY"
javascript
async function getAppInfo(appName) {
  const app = await heroku.get(`/apps/${appName}`);
  return app;
}

列出应用 #

bash
curl https://api.heroku.com/apps \
  -H "Accept: application/vnd.heroku+json; version=3" \
  -H "Authorization: Bearer $HEROKU_API_KEY"
javascript
async function listApps() {
  const apps = await heroku.get('/apps');
  return apps;
}

删除应用 #

bash
curl -X DELETE https://api.heroku.com/apps/my-app \
  -H "Accept: application/vnd.heroku+json; version=3" \
  -H "Authorization: Bearer $HEROKU_API_KEY"
javascript
async function deleteApp(appName) {
  await heroku.delete(`/apps/${appName}`);
}

Dyno 管理 API #

列出 Dynos #

bash
curl https://api.heroku.com/apps/my-app/dynos \
  -H "Accept: application/vnd.heroku+json; version=3" \
  -H "Authorization: Bearer $HEROKU_API_KEY"
javascript
async function listDynos(appName) {
  const dynos = await heroku.get(`/apps/${appName}/dynos`);
  return dynos;
}

扩展 Dyno #

bash
curl -X PATCH https://api.heroku.com/apps/my-app/formation/web \
  -H "Accept: application/vnd.heroku+json; version=3" \
  -H "Authorization: Bearer $HEROKU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"quantity": 3}'
javascript
async function scaleDynos(appName, type, quantity) {
  const formation = await heroku.patch(`/apps/${appName}/formation/${type}`, {
    body: { quantity }
  });
  return formation;
}

重启 Dyno #

bash
curl -X DELETE https://api.heroku.com/apps/my-app/dynos/web.1 \
  -H "Accept: application/vnd.heroku+json; version=3" \
  -H "Authorization: Bearer $HEROKU_API_KEY"
javascript
async function restartDyno(appName, dynoName) {
  await heroku.delete(`/apps/${appName}/dynos/${dynoName}`);
}

配置管理 API #

获取配置变量 #

bash
curl https://api.heroku.com/apps/my-app/config-vars \
  -H "Accept: application/vnd.heroku+json; version=3" \
  -H "Authorization: Bearer $HEROKU_API_KEY"
javascript
async function getConfigVars(appName) {
  const configVars = await heroku.get(`/apps/${appName}/config-vars`);
  return configVars;
}

设置配置变量 #

bash
curl -X PATCH https://api.heroku.com/apps/my-app/config-vars \
  -H "Accept: application/vnd.heroku+json; version=3" \
  -H "Authorization: Bearer $HEROKU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"NODE_ENV": "production", "DEBUG": "false"}'
javascript
async function setConfigVars(appName, vars) {
  const configVars = await heroku.patch(`/apps/${appName}/config-vars`, {
    body: vars
  });
  return configVars;
}

部署 API #

创建构建 #

bash
curl -X POST https://api.heroku.com/apps/my-app/builds \
  -H "Accept: application/vnd.heroku+json; version=3" \
  -H "Authorization: Bearer $HEROKU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source_blob": {
      "url": "https://github.com/user/repo/archive/main.tar.gz",
      "version": "abc123"
    }
  }'
javascript
async function createBuild(appName, sourceUrl, version) {
  const build = await heroku.post(`/apps/${appName}/builds`, {
    body: {
      source_blob: {
        url: sourceUrl,
        version
      }
    }
  });
  return build;
}

获取构建状态 #

bash
curl https://api.heroku.com/apps/my-app/builds/build-id \
  -H "Accept: application/vnd.heroku+json; version=3" \
  -H "Authorization: Bearer $HEROKU_API_KEY"
javascript
async function getBuildStatus(appName, buildId) {
  const build = await heroku.get(`/apps/${appName}/builds/${buildId}`);
  return build;
}

自动化脚本 #

自动部署脚本 #

javascript
// scripts/deploy.js
const Heroku = require('heroku-client');
const heroku = new Heroku({ token: process.env.HEROKU_API_KEY });

async function deploy(appName, sourceUrl) {
  console.log(`Deploying to ${appName}...`);
  
  // 创建构建
  const build = await heroku.post(`/apps/${appName}/builds`, {
    body: {
      source_blob: {
        url: sourceUrl
      }
    }
  });
  
  console.log(`Build created: ${build.id}`);
  
  // 等待构建完成
  let status = 'pending';
  while (status === 'pending') {
    await new Promise(resolve => setTimeout(resolve, 5000));
    const buildStatus = await heroku.get(`/apps/${appName}/builds/${build.id}`);
    status = buildStatus.status;
    console.log(`Build status: ${status}`);
  }
  
  if (status === 'succeeded') {
    console.log('Deployment successful!');
  } else {
    throw new Error(`Deployment failed: ${status}`);
  }
}

deploy('my-app', 'https://github.com/user/repo/archive/main.tar.gz')
  .catch(console.error);

自动扩展脚本 #

javascript
// scripts/scale.js
const Heroku = require('heroku-client');
const heroku = new Heroku({ token: process.env.HEROKU_API_KEY });

async function scaleBasedOnTime(appName) {
  const hour = new Date().getHours();
  const isBusinessHours = hour >= 9 && hour < 18;
  
  const quantity = isBusinessHours ? 3 : 1;
  
  console.log(`Scaling ${appName} to ${quantity} dynos...`);
  
  await heroku.patch(`/apps/${appName}/formation/web`, {
    body: { quantity }
  });
  
  console.log('Scaling complete');
}

scaleBasedOnTime('my-app').catch(console.error);

监控脚本 #

javascript
// scripts/monitor.js
const Heroku = require('heroku-client');
const heroku = new Heroku({ token: process.env.HEROKU_API_KEY });

async function monitorApps() {
  const apps = await heroku.get('/apps');
  
  for (const app of apps) {
    const dynos = await heroku.get(`/apps/${app.name}/dynos`);
    const webDynos = dynos.filter(d => d.type === 'web');
    
    console.log(`\n${app.name}:`);
    console.log(`  Web Dynos: ${webDynos.length}`);
    
    for (const dyno of webDynos) {
      console.log(`  - ${dyno.name}: ${dyno.state}`);
    }
  }
}

monitorApps().catch(console.error);

GitHub Actions 集成 #

完整自动化工作流 #

yaml
# .github/workflows/heroku-automation.yml
name: Heroku Automation

on:
  schedule:
    - cron: '0 9 * * 1-5'  # 工作日 9:00
    - cron: '0 18 * * 1-5' # 工作日 18:00

jobs:
  scale-up:
    runs-on: ubuntu-latest
    if: github.event.schedule == '0 9 * * 1-5'
    steps:
      - name: Scale up
        run: |
          curl -X PATCH https://api.heroku.com/apps/my-app/formation/web \
            -H "Accept: application/vnd.heroku+json; version=3" \
            -H "Authorization: Bearer ${{ secrets.HEROKU_API_KEY }}" \
            -H "Content-Type: application/json" \
            -d '{"quantity": 3}'
  
  scale-down:
    runs-on: ubuntu-latest
    if: github.event.schedule == '0 18 * * 1-5'
    steps:
      - name: Scale down
        run: |
          curl -X PATCH https://api.heroku.com/apps/my-app/formation/web \
            -H "Accept: application/vnd.heroku+json; version=3" \
            -H "Authorization: Bearer ${{ secrets.HEROKU_API_KEY }}" \
            -H "Content-Type: application/json" \
            -d '{"quantity": 1}'

SDK 使用 #

Node.js SDK #

bash
npm install heroku-client
javascript
const Heroku = require('heroku-client');
const heroku = new Heroku({ token: process.env.HEROKU_API_KEY });

// 获取应用列表
const apps = await heroku.get('/apps');

// 创建应用
const app = await heroku.post('/apps', {
  body: { name: 'my-new-app' }
});

// 更新配置
await heroku.patch('/apps/my-app/config-vars', {
  body: { NODE_ENV: 'production' }
});

// 扩展 Dyno
await heroku.patch('/apps/my-app/formation/web', {
  body: { quantity: 2 }
});

Python SDK #

bash
pip install heroku3
python
from heroku3 import Heroku

heroku = Heroku(api_key=os.environ.get('HEROKU_API_KEY'))

# 获取应用列表
apps = heroku.apps()

# 获取特定应用
app = heroku.app('my-app')

# 获取 Dynos
dynos = app.dynos()

# 扩展 Dyno
app.process_formation()['web'].scale(3)

最佳实践 #

1. API Token 管理 #

bash
# 为不同用途创建不同的 Token
heroku authorizations:create -d "CI/CD Pipeline" -s deploy
heroku authorizations:create -d "Monitoring" -s read

# 定期轮换 Token
heroku authorizations:revoke <authorization-id>

2. 错误处理 #

javascript
async function safeApiCall(fn, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.statusCode === 429) {
        // 速率限制,等待后重试
        await new Promise(resolve => setTimeout(resolve, 60000));
        continue;
      }
      throw error;
    }
  }
}

3. 日志记录 #

javascript
async function deployWithLogging(appName, sourceUrl) {
  console.log(`[${new Date().toISOString()}] Starting deployment to ${appName}`);
  
  try {
    const build = await createBuild(appName, sourceUrl);
    console.log(`[${new Date().toISOString()}] Build created: ${build.id}`);
    
    // 等待构建完成
    await waitForBuild(appName, build.id);
    
    console.log(`[${new Date().toISOString()}] Deployment successful`);
  } catch (error) {
    console.error(`[${new Date().toISOString()}] Deployment failed:`, error);
    throw error;
  }
}

下一步 #

API 与自动化掌握后,接下来学习 Node.js 应用部署 开始实战!

最后更新:2026-03-28