encore-infrastructure

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Encore Infrastructure Declaration

Encore基础设施声明

Instructions

说明

Encore.ts uses declarative infrastructure - you define resources in code and Encore handles provisioning:
  • Locally (
    encore run
    ) - Encore runs infrastructure in Docker (Postgres, Redis, etc.)
  • Production - Deploy via Encore Cloud to your AWS/GCP, or self-host using generated infrastructure config
Encore.ts采用声明式基础设施方式——你在代码中定义资源,Encore负责处理资源部署:
  • 本地环境
    encore run
    )——Encore在Docker中运行基础设施(Postgres、Redis等)
  • 生产环境——通过Encore Cloud部署到你的AWS/GCP,或使用生成的基础设施配置进行自托管

Critical Rule

重要规则

All infrastructure must be declared at package level (top of file), not inside functions.
所有基础设施必须在包级别(文件顶部)声明,不能在函数内部。

Databases (PostgreSQL)

数据库(PostgreSQL)

typescript
import { SQLDatabase } from "encore.dev/storage/sqldb";

// CORRECT: Package level
const db = new SQLDatabase("mydb", {
  migrations: "./migrations",
});

// WRONG: Inside function
async function setup() {
  const db = new SQLDatabase("mydb", { migrations: "./migrations" });
}
typescript
import { SQLDatabase } from "encore.dev/storage/sqldb";

// 正确:包级别声明
const db = new SQLDatabase("mydb", {
  migrations: "./migrations",
});

// 错误:在函数内部声明
async function setup() {
  const db = new SQLDatabase("mydb", { migrations: "./migrations" });
}

Migrations

迁移

Create migrations in the
migrations/
directory:
service/
├── encore.service.ts
├── api.ts
├── db.ts
└── migrations/
    ├── 001_create_users.up.sql
    └── 002_add_email_index.up.sql
Migration naming:
{number}_{description}.up.sql
migrations/
目录中创建迁移文件:
service/
├── encore.service.ts
├── api.ts
├── db.ts
└── migrations/
    ├── 001_create_users.up.sql
    └── 002_add_email_index.up.sql
迁移文件命名规则:
{编号}_{描述}.up.sql

Pub/Sub

Pub/Sub

Topics

主题

typescript
import { Topic } from "encore.dev/pubsub";

interface OrderCreatedEvent {
  orderId: string;
  userId: string;
  total: number;
}

// Package level declaration
export const orderCreated = new Topic<OrderCreatedEvent>("order-created", {
  deliveryGuarantee: "at-least-once",
});
typescript
import { Topic } from "encore.dev/pubsub";

interface OrderCreatedEvent {
  orderId: string;
  userId: string;
  total: number;
}

// 包级别声明
export const orderCreated = new Topic<OrderCreatedEvent>("order-created", {
  deliveryGuarantee: "at-least-once",
});

Publishing

发布消息

typescript
await orderCreated.publish({
  orderId: "123",
  userId: "user-456",
  total: 99.99,
});
typescript
await orderCreated.publish({
  orderId: "123",
  userId: "user-456",
  total: 99.99,
});

Subscriptions

订阅

typescript
import { Subscription } from "encore.dev/pubsub";

const _ = new Subscription(orderCreated, "send-confirmation-email", {
  handler: async (event) => {
    await sendEmail(event.userId, event.orderId);
  },
});
typescript
import { Subscription } from "encore.dev/pubsub";

const _ = new Subscription(orderCreated, "send-confirmation-email", {
  handler: async (event) => {
    await sendEmail(event.userId, event.orderId);
  },
});

Cron Jobs

定时任务(Cron Jobs)

typescript
import { CronJob } from "encore.dev/cron";
import { api } from "encore.dev/api";

// The endpoint to call
export const cleanupExpiredSessions = api(
  { expose: false },
  async (): Promise<void> => {
    // Cleanup logic
  }
);

// Package level cron declaration
const _ = new CronJob("cleanup-sessions", {
  title: "Clean up expired sessions",
  schedule: "0 * * * *",  // Every hour
  endpoint: cleanupExpiredSessions,
});
typescript
import { CronJob } from "encore.dev/cron";
import { api } from "encore.dev/api";

// 要调用的端点
export const cleanupExpiredSessions = api(
  { expose: false },
  async (): Promise<void> => {
    // 清理逻辑
  }
);

// 包级别定时任务声明
const _ = new CronJob("cleanup-sessions", {
  title: "清理过期会话",
  schedule: "0 * * * *",  // 每小时一次
  endpoint: cleanupExpiredSessions,
});

Schedule Formats

调度格式

FormatExampleDescription
every
"1h"
,
"30m"
Simple interval (must divide 24h evenly)
schedule
"0 9 * * 1"
Cron expression (9am every Monday)
格式示例描述
every
"1h"
,
"30m"
简单时间间隔(必须能整除24小时)
schedule
"0 9 * * 1"
Cron表达式(每周一上午9点)

Object Storage

对象存储

typescript
import { Bucket } from "encore.dev/storage/objects";

// Package level
export const uploads = new Bucket("user-uploads", {
  versioned: false,
});

// Public bucket
export const publicAssets = new Bucket("public-assets", {
  public: true,
  versioned: false,
});
typescript
import { Bucket } from "encore.dev/storage/objects";

// 包级别声明
export const uploads = new Bucket("user-uploads", {
  versioned: false,
});

// 公共存储桶
export const publicAssets = new Bucket("public-assets", {
  public: true,
  versioned: false,
});

Operations

操作示例

typescript
// Upload
await uploads.upload("path/to/file.jpg", buffer, {
  contentType: "image/jpeg",
});

// Download
const data = await uploads.download("path/to/file.jpg");

// Check existence
const exists = await uploads.exists("path/to/file.jpg");

// Delete
await uploads.remove("path/to/file.jpg");

// Public URL (only for public buckets)
const url = publicAssets.publicUrl("image.jpg");
typescript
// 上传
await uploads.upload("path/to/file.jpg", buffer, {
  contentType: "image/jpeg",
});

// 下载
const data = await uploads.download("path/to/file.jpg");

// 检查是否存在
const exists = await uploads.exists("path/to/file.jpg");

// 删除
await uploads.remove("path/to/file.jpg");

// 公共URL(仅适用于公共存储桶)
const url = publicAssets.publicUrl("image.jpg");

Secrets

密钥(Secrets)

typescript
import { secret } from "encore.dev/config";

// Package level
const stripeKey = secret("StripeSecretKey");

// Usage (call as function)
const key = stripeKey();
Set secrets via CLI:
bash
encore secret set --type prod StripeSecretKey
typescript
import { secret } from "encore.dev/config";

// 包级别声明
const stripeKey = secret("StripeSecretKey");

// 使用方式(作为函数调用)
const key = stripeKey();
通过CLI设置密钥:
bash
encore secret set --type prod StripeSecretKey

Guidelines

指南

  • Infrastructure declarations MUST be at package level
  • Use descriptive names for resources
  • Keep migrations sequential and numbered
  • Subscription handlers must be idempotent (at-least-once delivery)
  • Secrets are accessed by calling the secret as a function
  • Cron endpoints should be
    expose: false
    (internal only)
  • 基础设施声明必须在包级别
  • 为资源使用描述性名称
  • 保持迁移文件按顺序编号
  • 订阅处理程序必须是幂等的(至少一次投递)
  • 通过调用密钥函数来访问密钥
  • 定时任务端点应设置为
    expose: false
    (仅内部访问)