On this page
从 subhosting API v1 迁移到 v2
本指南介绍如何将 Deno Deploy subhosting API v1
(api.deno.com/v1) 迁移到 v2 (api.deno.com/v2)。v1 API 将于
2026 年 7 月 20 日 关闭。有关通用平台迁移(应用、域名、环境变量),请参阅
Deploy Classic 迁移指南。
完整的 v2 API 参考文档:api.deno.com/v2/docs
官方 SDK:
- TypeScript/JavaScript: @deno/sandbox
- Python: PyPI 上的 deno-sandbox (源代码)
这些 SDK 的品牌名是 "sandbox",但可用于 subhosting 场景。
为什么要迁移到 v2 Jump to heading
v2 运行在一个完全不同的新平台上,并带来了显著改进:
- 不再有每个请求的 CPU 时间限制(不再出现
TIME_LIMIT错误) - 可配置的内存限制,最高 4 GB(v1 固定为 512 MB)
- 可将自定义 OpenTelemetry 导出(日志、指标、追踪)发送到你自己的端点
- 自定义构建步骤和框架支持(Next.js、Astro、SvelteKit 等)
- 内置 HTTP 缓存
- 通过 layers 在不重新部署的情况下,跨多个应用即时批量更新配置
- Web 应用防火墙(WAF)
关键概念变化 Jump to heading
| v1 | v2 | 备注 |
|---|---|---|
| Organization | Organization | 无变化 |
| Project | App | 一个函数对应一个 app |
| Deployment | Revision | 代码 + 配置的不可变快照 |
| Project name | App slug | 用于 URL;在组织内唯一 |
| — | Labels | 用于分组/筛选 app 的键值对 |
| — | Layers | 跨 app 共享的配置(环境变量) |
| — | Config | 构建和运行时配置 |
| — | Timeline | app 内的部署目标 |
架构变化:一个函数对应一个 app Jump to heading
在 v1 中,你可能使用一个包含多个活动部署的项目来表示不同的函数。在 v2 中,请为每个函数创建一个单独的 app。
v2 引入了 timelines。每个 timeline 在任意时刻只有一个活动 revision——部署新的 revision 会替换之前的 revision。通过 API 部署时,revision 会自动标记为 production,因此每个 app 实际上只有一个活动 revision。
这意味着 v2 的 revisions 并不与 v1 的 deployments 一一对应。在 v1 中,同一个项目内可以同时存在多个活动部署。在 v2 中,每个 timeline 只有一个活动 revision。
推荐做法:
- 为每个函数创建一个 app,并使用具有描述性的 slug(例如
my-service-auth、my-service-billing) - 使用 labels 对相关 app 分组——可通过
GET /apps?labels[service]=my-service过滤 - 使用 layers 在多个 app 之间共享环境变量
配置继承 Jump to heading
在 v1 中,每个部署都是自包含的:环境变量、入口点以及其他选项都必须在每次 deploy 请求中指定。
在 v2 中,app 会携带配置,并由 revision 继承:
- 构建配置 (
config):安装命令、构建命令、运行时入口点。只需在 app 上设置一次,所有 revision 都会继承。 - 环境变量 (
env_vars):可在 app 或 layer 上设置,所有 revision 都会继承。 - Layers:设置在 app 上,所有 revision 都会继承。
你可以通过在 deploy 请求中包含这些字段,按 revision 进行覆盖。若省略,则使用 app 的配置。这意味着在初始设置后,通常只需要发送 assets:
{
"assets": {
"main.ts": {
"kind": "file",
"content": "Deno.serve((req) => new Response('Hello'));"
}
}
}
身份验证 Jump to heading
| Aspect | v1 | v2 |
|---|---|---|
| Token prefix | dd... |
ddo_... |
| Header | Authorization: Bearer <token> |
Authorization: Bearer <token> |
| Scope | Organization | Organization |
| Base URL | https://api.deno.com/v1 |
https://api.deno.com/v2 |
请在 Deno Deploy 控制台的组织设置中创建 v2 token。
端点映射 Jump to heading
列出 projects / apps Jump to heading
v1: GET /organizations/{orgId}/projects
v2: GET /apps
v2 API 会根据你的 token 自动作用于对应的组织。URL 中不需要组织 ID。
# v1
curl -H "Authorization: Bearer $TOKEN" \
"https://api.deno.com/v1/organizations/$ORG_ID/projects?page=1&limit=20"
# v2
curl -H "Authorization: Bearer $TOKEN" \
"https://api.deno.com/v2/apps?limit=30"
| Aspect | v1 | v2 |
|---|---|---|
| Pagination | page + limit(基于页码) |
cursor + limit(基于游标) |
| Filtering | q(按名称/ID 搜索) |
labels、layer |
| Response | Project 对象数组 | AppListItem 对象数组 |
响应字段映射:
| v1 Project | v2 AppListItem |
|---|---|
id (UUID) |
id (UUID) |
name |
slug |
description |
不可用 |
createdAt |
created_at |
updatedAt |
updated_at |
| — | labels(对象) |
| — | layers({id, slug} 数组) |
v2 对所有字段名使用 snake_case(v1 使用 camelCase)。
获取 project / app 详情 Jump to heading
v1: GET /projects/{projectId}
v2: GET /apps/{app}
v2 端点接受 app 的 UUID 或 slug。
# v1
curl -H "Authorization: Bearer $TOKEN" \
"https://api.deno.com/v1/projects/$PROJECT_ID"
# v2
curl -H "Authorization: Bearer $TOKEN" \
"https://api.deno.com/v2/apps/my-app-slug"
v2 响应包含 env_vars、config、labels 和 layers。
创建 deployment / revision Jump to heading
v1: POST /projects/{projectId}/deployments
v2: POST /apps/{app}/deploy
这是最重要的 API 变更。
v1 请求 Jump to heading
{
"entryPointUrl": "main.ts",
"assets": {
"main.ts": {
"content": "Deno.serve((req) => new Response('Hello'));",
"encoding": "utf-8"
}
},
"envVars": {
"MY_VAR": "my_value"
}
}
v2 请求 Jump to heading
{
"assets": {
"main.ts": {
"kind": "file",
"content": "Deno.serve((req) => new Response('Hello'));",
"encoding": "utf-8"
}
},
"config": {
"install": "deno install",
"runtime": {
"type": "dynamic",
"entrypoint": "main.ts"
}
},
"env_vars": [
{ "key": "MY_VAR", "value": "my_value" }
]
}
主要差异:
| Aspect | v1 | v2 |
|---|---|---|
| Entrypoint | 顶层 entryPointUrl |
config.runtime.entrypoint |
| Assets | content+encoding 或 gitSha1 |
kind: "file" 搭配 content+encoding,或 kind: "symlink" 搭配 target |
| Env vars | 对象 {"KEY": "value"} |
数组 [{"key": "KEY", "value": "value"}] |
| Import map | importMapUrl 字段 |
不需要;Deno 会自动发现 deno.json |
| Lock file | lockFileUrl 字段 |
不需要;由 deno install 处理 |
| Compiler options | compilerOptions 字段 |
不需要;使用 deno.json |
| Build config | 无(仅导入缓存) | config.install 和 config.build |
| Response status | 200 |
202 Accepted(构建是异步的) |
| Databases/KV | databases 字段 |
不可用 |
| Request timeout | requestTimeout 字段 |
不可用 |
| Permissions | permissions 字段 |
不可用 |
构建配置: 在 v1 中,"build" 步骤仅用于缓存导入。在 v2 中,对应的是 config.install: "deno install" 搭配 config.build: null。
不需要框架预设(将 config.framework 保持为 "")。
配置继承: config、env_vars 和 layers 在 deploy 请求中都是可选的。若省略,revision 将继承 app 的配置。请在创建 app 时一次性设置这些内容,之后的部署只需发送 assets。
状态值 Jump to heading
| v1 | v2 | 含义 |
|---|---|---|
pending |
queued |
构建尚未开始 |
| (none) | building |
构建中 |
success |
succeeded |
已部署并生效 |
failed |
failed |
构建失败 |
| (none) | skipped |
构建已跳过 |
获取 deployment / revision 状态 Jump to heading
v1: GET /deployments/{deploymentId}
v2: GET /revisions/{revisionId}
# v1
curl -H "Authorization: Bearer $TOKEN" \
"https://api.deno.com/v1/deployments/$DEPLOYMENT_ID"
# v2
curl -H "Authorization: Bearer $TOKEN" \
"https://api.deno.com/v2/revisions/$REVISION_ID"
当状态为 failed 时,v2 响应会包含 failure_reason。
列出 deployments / revisions Jump to heading
v1: GET /projects/{projectId}/deployments
v2: GET /apps/{app}/revisions
# v1
curl -H "Authorization: Bearer $TOKEN" \
"https://api.deno.com/v1/projects/$PROJECT_ID/deployments?page=1&limit=20"
# v2
curl -H "Authorization: Bearer $TOKEN" \
"https://api.deno.com/v2/apps/my-app-slug/revisions?limit=30"
| Aspect | v1 | v2 |
|---|---|---|
| Pagination | page + limit |
cursor + limit |
| Filtering | q(按 ID 搜索) |
status |
追踪构建日志 Jump to heading
v1: GET /deployments/{deploymentId}/build_logs(轮询)
v2 提供两种方式:
流式获取 revision 进度(推荐) Jump to heading
GET /revisions/{revisionId}/progress
在构建进展时发出事件。会在 revision 到达终止状态(succeeded、failed 或 skipped)时结束。替代了 v1 中循环轮询 deployment 状态的模式。
curl -H "Authorization: Bearer $TOKEN" \
-H "Accept: text/event-stream" \
"https://api.deno.com/v2/revisions/$REVISION_ID/progress"
流式获取构建日志 Jump to heading
GET /revisions/{revisionId}/build_logs
curl -H "Authorization: Bearer $TOKEN" \
-H "Accept: text/event-stream" \
"https://api.deno.com/v2/revisions/$REVISION_ID/build_logs"
| Aspect | v1 | v2 |
|---|---|---|
| Formats | application/x-ndjson、application/json |
text/event-stream(SSE)、application/x-ndjson |
| Log fields | level、message |
timestamp、level、message、step、timeline |
| Filter by step | 不支持 | step 参数(preparing/installing/building/deploying) |
查询 app 日志 Jump to heading
v1: GET /deployments/{deploymentId}/app_logs
v2: GET /apps/{app}/logs
在 v1 中,日志仅限于单个 deployment。v2 中,日志作用于整个 app,并可选按 revision_id 过滤。
# v1
curl -H "Authorization: Bearer $TOKEN" \
"https://api.deno.com/v1/deployments/$DEPLOYMENT_ID/app_logs?since=2024-01-01T00:00:00Z&until=2024-01-01T01:00:00Z"
# v2
curl -H "Authorization: Bearer $TOKEN" \
"https://api.deno.com/v2/apps/my-app-slug/logs?start=2024-01-01T00:00:00Z&end=2024-01-01T01:00:00Z"
参数映射:
| v1 | v2 | 备注 |
|---|---|---|
since |
start |
v2 中必需 |
until |
end |
可选 |
limit |
limit |
v1:最大 10000。v2:最大 1000 |
order |
(none) | v2 返回按时间顺序的结果 |
q |
query |
文本搜索 |
level |
level |
v1:warning。v2:warn |
region |
(none) | v2 不支持按区域过滤 |
响应字段映射:
| v1 field | v2 field |
|---|---|
time |
timestamp |
level |
level |
message |
message |
region |
region |
| — | revision_id |
| — | trace_id |
| — | span_id |
v2 会将日志封装在一个带有 next_cursor 的对象中,以便分页:
{
"logs": [
{
"timestamp": "2024-01-01T00:00:01Z",
"level": "info",
"message": "Listening on http://0.0.0.0:8000",
"region": "us-east-1",
"revision_id": "abcdef12"
}
],
"next_cursor": "eyJsYXN0X3RzIjoiMjAy..."
}
流式传输: 在 v1 中,省略 since 和 until 可启用实时流式传输。
在 v2 中,省略 end 参数即可流式传输。将 Accept 头设置为
text/event-stream 可使用 SSE 格式,或设置为 application/x-ndjson 可使用 NDJSON。
如果在没有 end 的情况下请求 application/json,将返回错误。
使用标签对应用分组 Jump to heading
标签取代了 v1 中将多个部署归组到单个 项目下的模式:
# 使用标签创建应用
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"https://api.deno.com/v2/apps" \
-d '{
"slug": "my-service-auth",
"labels": {
"service": "my-service",
"function": "auth"
},
"config": {
"install": "deno install",
"runtime": {
"type": "dynamic",
"entrypoint": "main.ts"
}
}
}'
# 列出某个服务中的所有应用
curl -H "Authorization: Bearer $TOKEN" \
"https://api.deno.com/v2/apps?labels[service]=my-service"
使用层进行共享配置 Jump to heading
层允许你在多个应用之间共享环境变量。这在 v1 中是不可能的, 因为环境变量必须在每次部署请求中重复传入。
# 创建一个包含共享环境变量的层
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"https://api.deno.com/v2/layers" \
-d '{
"slug": "my-service-shared",
"env_vars": [
{"key": "DATABASE_URL", "value": "postgres://...", "secret": true},
{"key": "API_KEY", "value": "sk-...", "secret": true}
]
}'
# 创建一个使用该层的应用
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"https://api.deno.com/v2/apps" \
-d '{
"slug": "my-service-auth",
"layers": ["my-service-shared"],
"labels": {"service": "my-service"},
"config": {
"install": "deno install",
"runtime": {
"type": "dynamic",
"entrypoint": "main.ts"
}
}
}'
当某个层的环境变量更新时,使用该层的所有应用会立即获取到 这些变化。
迁移清单 Jump to heading
- 在 Deno Deploy 仪表板中创建一个 v2 组织令牌(
ddo_前缀)。 - 每个函数创建一个应用,而不是将多个函数部署到一个 项目中。使用标签对相关应用进行分组。
- 设置层,用于在多个函数之间共享环境变量。
- 更新部署请求:
- 将
entryPointUrl移动到config.runtime.entrypoint - 将
config.install设置为"deno install" - 将
envVars对象转换为env_vars数组,或设置在应用/层上 - 为资源添加
kind: "file"
- 将
- 更新构建日志尾随,改为使用
GET /revisions/{id}/progress(SSE) 而不是轮询。 - 更新日志查询:
since/until→start/endwarning→warn- 日志响应体中的
time→timestamp - 处理
{logs: [...], next_cursor: ...}响应包装
- 分页方式更新:从基于页码改为基于游标。
- 更新状态值:
pending→queued,success→succeeded。 - 将字段名从 camelCase 更新为 snake_case。