encore-go-infrastructure

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Encore Go Infrastructure Declaration

Encore Go 基础设施声明

Instructions

说明

Encore Go uses declarative infrastructure - you define resources as package-level variables 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 Go 采用声明式基础设施——你将资源定义为包级变量,Encore 会负责资源部署:
  • 本地环境
    encore run
    )——Encore 在 Docker 中运行基础设施(Postgres、Redis 等)
  • 生产环境——通过 Encore Cloud 部署到你的 AWS/GCP,或使用生成的基础设施配置进行自托管

Critical Rule

核心规则

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

Databases (PostgreSQL)

数据库(PostgreSQL)

go
package user

import "encore.dev/storage/sqldb"

// CORRECT: Package level
var db = sqldb.NewDatabase("userdb", sqldb.DatabaseConfig{
    Migrations: "./migrations",
})

// WRONG: Inside function
func setup() {
    db := sqldb.NewDatabase("userdb", sqldb.DatabaseConfig{...})
}
go
package user

import "encore.dev/storage/sqldb"

// CORRECT: Package level
var db = sqldb.NewDatabase("userdb", sqldb.DatabaseConfig{
    Migrations: "./migrations",
})

// WRONG: Inside function
func setup() {
    db := sqldb.NewDatabase("userdb", sqldb.DatabaseConfig{...})
}

Migrations

迁移

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

Pub/Sub

发布/订阅(Pub/Sub)

Topics

主题

go
package events

import "encore.dev/pubsub"

type OrderCreatedEvent struct {
    OrderID string `json:"order_id"`
    UserID  string `json:"user_id"`
    Total   int    `json:"total"`
}

// Package level declaration
var OrderCreated = pubsub.NewTopic[*OrderCreatedEvent]("order-created", pubsub.TopicConfig{
    DeliveryGuarantee: pubsub.AtLeastOnce,
})
go
package events

import "encore.dev/pubsub"

type OrderCreatedEvent struct {
    OrderID string `json:"order_id"`
    UserID  string `json:"user_id"`
    Total   int    `json:"total"`
}

// Package level declaration
var OrderCreated = pubsub.NewTopic[*OrderCreatedEvent]("order-created", pubsub.TopicConfig{
    DeliveryGuarantee: pubsub.AtLeastOnce,
})

Publishing

发布消息

go
msgID, err := events.OrderCreated.Publish(ctx, &events.OrderCreatedEvent{
    OrderID: "123",
    UserID:  "user-456",
    Total:   9999,
})
go
msgID, err := events.OrderCreated.Publish(ctx, &events.OrderCreatedEvent{
    OrderID: "123",
    UserID:  "user-456",
    Total:   9999,
})

Subscriptions

订阅

go
package notifications

import (
    "context"
    "myapp/events"
    "encore.dev/pubsub"
)

var _ = pubsub.NewSubscription(events.OrderCreated, "send-confirmation-email",
    pubsub.SubscriptionConfig[*events.OrderCreatedEvent]{
        Handler: sendConfirmationEmail,
    },
)

func sendConfirmationEmail(ctx context.Context, event *events.OrderCreatedEvent) error {
    // Send email...
    return nil
}
go
package notifications

import (
    "context"
    "myapp/events"
    "encore.dev/pubsub"
)

var _ = pubsub.NewSubscription(events.OrderCreated, "send-confirmation-email",
    pubsub.SubscriptionConfig[*events.OrderCreatedEvent]{
        Handler: sendConfirmationEmail,
    },
)

func sendConfirmationEmail(ctx context.Context, event *events.OrderCreatedEvent) error {
    // 发送邮件...
    return nil
}

Cron Jobs

Cron 任务

go
package cleanup

import (
    "context"
    "encore.dev/cron"
)

// The cron job declaration
var _ = cron.NewJob("cleanup-sessions", cron.JobConfig{
    Title:    "Clean up expired sessions",
    Schedule: "0 * * * *",  // Every hour
    Endpoint: CleanupExpiredSessions,
})

//encore:api private
func CleanupExpiredSessions(ctx context.Context) error {
    // Cleanup logic
    return nil
}
go
package cleanup

import (
    "context"
    "encore.dev/cron"
)

// The cron job declaration
var _ = cron.NewJob("cleanup-sessions", cron.JobConfig{
    Title:    "Clean up expired sessions",
    Schedule: "0 * * * *",  // 每小时执行一次
    Endpoint: CleanupExpiredSessions,
})

//encore:api private
func CleanupExpiredSessions(ctx context.Context) error {
    // 清理逻辑
    return nil
}

Schedule Formats

调度格式

FormatExampleDescription
Cron expression
"0 9 * * 1"
9am every Monday
Every interval
"every 1h"
Every hour
Every interval
"every 30m"
Every 30 minutes
格式示例描述
Cron表达式
"0 9 * * 1"
每周一上午9点
固定间隔
"every 1h"
每小时一次
固定间隔
"every 30m"
每30分钟一次

Object Storage

对象存储

go
package uploads

import "encore.dev/storage/objects"

// Package level
var Uploads = objects.NewBucket("user-uploads", objects.BucketConfig{})

// Public bucket
var PublicAssets = objects.NewBucket("public-assets", objects.BucketConfig{
    Public: true,
})
go
package uploads

import "encore.dev/storage/objects"

// Package level
var Uploads = objects.NewBucket("user-uploads", objects.BucketConfig{})

// 公开存储桶
var PublicAssets = objects.NewBucket("public-assets", objects.BucketConfig{
    Public: true,
})

Operations

操作示例

go
// Upload
attrs, err := uploads.Uploads.Upload(ctx, "path/to/file.jpg", bytes.NewReader(data),
    objects.UploadOptions{
        ContentType: "image/jpeg",
    },
)

// Download
reader, err := uploads.Uploads.Download(ctx, "path/to/file.jpg")
defer reader.Close()
data, _ := io.ReadAll(reader)

// Check existence
attrs, err := uploads.Uploads.Attrs(ctx, "path/to/file.jpg")
// err == objects.ErrObjectNotFound if doesn't exist

// Delete
err := uploads.Uploads.Remove(ctx, "path/to/file.jpg")

// Public URL (only for public buckets)
url := uploads.PublicAssets.PublicURL("image.jpg")
go
// 上传文件
attrs, err := uploads.Uploads.Upload(ctx, "path/to/file.jpg", bytes.NewReader(data),
    objects.UploadOptions{
        ContentType: "image/jpeg",
    },
)

// 下载文件
reader, err := uploads.Uploads.Download(ctx, "path/to/file.jpg")
defer reader.Close()
data, _ := io.ReadAll(reader)

// 检查文件是否存在
attrs, err := uploads.Uploads.Attrs(ctx, "path/to/file.jpg")
// 若不存在,err == objects.ErrObjectNotFound

// 删除文件
err := uploads.Uploads.Remove(ctx, "path/to/file.jpg")

// 获取公开URL(仅适用于公开存储桶)
url := uploads.PublicAssets.PublicURL("image.jpg")

Secrets

密钥管理

go
package email

import "encore.dev/config"

var secrets struct {
    SendGridAPIKey config.String
    SMTPPassword   config.String
}

func sendEmail() error {
    apiKey := secrets.SendGridAPIKey()
    // Use the secret...
}
Set secrets via CLI:
bash
encore secret set --type prod SendGridAPIKey
go
package email

import "encore.dev/config"

var secrets struct {
    SendGridAPIKey config.String
    SMTPPassword   config.String
}

func sendEmail() error {
    apiKey := secrets.SendGridAPIKey()
    // 使用密钥...
    return nil
}
通过CLI设置密钥:
bash
encore secret set --type prod SendGridAPIKey

Config Values

配置值

go
package myservice

import "encore.dev/config"

var cfg struct {
    MaxRetries config.Int
    BaseURL    config.String
    Debug      config.Bool
}

func doSomething() {
    if cfg.Debug() {
        log.Println("Debug mode enabled")
    }
}
go
package myservice

import "encore.dev/config"

var cfg struct {
    MaxRetries config.Int
    BaseURL    config.String
    Debug      config.Bool
}

func doSomething() {
    if cfg.Debug() {
        log.Println("Debug mode enabled")
    }
}

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 them as functions
  • Cron endpoints should be
    private
    (internal only)
  • Each service typically has its own database
  • 基础设施声明必须在包级别
  • 为资源使用具有描述性的名称
  • 保持迁移文件按顺序编号
  • 订阅处理程序必须是幂等的(至少一次投递)
  • 通过调用函数的方式访问密钥
  • Cron 端点应设置为
    private
    (仅内部访问)
  • 每个服务通常拥有独立的数据库