encore-go-infrastructure

Par encoredev · skills

Déclarez votre infrastructure avec Encore Go.

npx skills add https://github.com/encoredev/skills --skill encore-go-infrastructure

Déclaration d'Infrastructure Encore Go

Instructions

Encore Go utilise une infrastructure déclarative - vous définissez des ressources comme des variables au niveau du package et Encore gère le provisioning :

  • Localement (encore run) - Encore exécute l'infrastructure dans Docker (Postgres, Redis, etc.)
  • Production - Déployez via Encore Cloud sur votre AWS/GCP, ou auto-hébergez en utilisant la configuration d'infrastructure générée

Règle Critique

Toute infrastructure doit être déclarée au niveau du package, jamais à l'intérieur de fonctions.

Bases de Données (PostgreSQL)

package user

import "encore.dev/storage/sqldb"

// CORRECT : Niveau du package
var db = sqldb.NewDatabase("userdb", sqldb.DatabaseConfig{
    Migrations: "./migrations",
})

// FAUX : À l'intérieur d'une fonction
func setup() {
    db := sqldb.NewDatabase("userdb", sqldb.DatabaseConfig{...})
}

Migrations

Créez les migrations dans le répertoire migrations/ :

user/
├── user.go
├── db.go
└── migrations/
    ├── 1_create_users.up.sql
    └── 2_add_email_index.up.sql

Nommage des migrations : {number}_{description}.up.sql

Pub/Sub

Topics

package events

import "encore.dev/pubsub"

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

// Déclaration au niveau du package
var OrderCreated = pubsub.NewTopic[*OrderCreatedEvent]("order-created", pubsub.TopicConfig{
    DeliveryGuarantee: pubsub.AtLeastOnce,
})

Publication

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

Souscriptions

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 {
    // Envoyer l'email...
    return nil
}

Références de Topics

Transmettez l'accès au topic au code de bibliothèque tout en maintenant l'analyse statique :

// Créer une référence avec permission de publication
ref := pubsub.TopicRef[pubsub.Publisher[*OrderCreatedEvent]](OrderCreated)

// Utiliser la référence dans le code de bibliothèque
func publishEvent(ref pubsub.Publisher[*OrderCreatedEvent], event *OrderCreatedEvent) error {
    _, err := ref.Publish(ctx, event)
    return err
}

Tâches Cron

package cleanup

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

// La déclaration de la tâche cron
var _ = cron.NewJob("cleanup-sessions", cron.JobConfig{
    Title:    "Clean up expired sessions",
    Schedule: "0 * * * *",  // Toutes les heures
    Endpoint: CleanupExpiredSessions,
})

//encore:api private
func CleanupExpiredSessions(ctx context.Context) error {
    // Logique de nettoyage
    return nil
}

Formats de Planification

Format Exemple Description
Expression cron "0 9 * * 1" 9h tous les lundis
Intervalle "every 1h" Toutes les heures
Intervalle "every 30m" Tous les 30 minutes

Stockage d'Objets

package uploads

import "encore.dev/storage/objects"

// Niveau du package
var Uploads = objects.NewBucket("user-uploads", objects.BucketConfig{})

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

Opérations

// Téléverser (pattern streaming)
writer := Uploads.Upload(ctx, "path/to/file.jpg")
_, err := io.Copy(writer, dataReader)
if err != nil {
    writer.Abort()
    return err
}
err = writer.Close()

// Télécharger
reader := Uploads.Download(ctx, "path/to/file.jpg")
if err := reader.Err(); err != nil {
    return err
}
defer reader.Close()
data, _ := io.ReadAll(reader)

// Vérifier l'existence
exists, err := Uploads.Exists(ctx, "path/to/file.jpg")

// Obtenir les attributs (taille, type de contenu, ETag)
attrs, err := Uploads.Attrs(ctx, "path/to/file.jpg")

// Lister les objets
for err, entry := range Uploads.List(ctx, &objects.Query{}) {
    if err != nil {
        return err
    }
    fmt.Println(entry.Key, entry.Size)
}

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

// URL publique (uniquement pour les buckets publics)
url := PublicAssets.PublicURL("image.jpg")

URLs Signées

Générez des URLs temporaires pour téléverser/télécharger sans exposer votre bucket :

import "time"

// URL de téléversement signée (expire dans 2 heures)
url, err := Uploads.SignedUploadURL(ctx, "user-uploads/avatar.jpg",
    objects.WithTTL(time.Duration(7200)*time.Second))

// URL de téléchargement signée
url, err := Uploads.SignedDownloadURL(ctx, "documents/report.pdf",
    objects.WithTTL(time.Duration(7200)*time.Second))

Références de Buckets

Transmettez l'accès au bucket avec des permissions spécifiques au code de bibliothèque :

// Créer une référence avec permission de téléchargement uniquement
ref := objects.BucketRef[objects.Downloader](Uploads)

// Créer une référence avec plusieurs permissions
type myPerms interface {
    objects.Downloader
    objects.Uploader
}
ref := objects.BucketRef[myPerms](Uploads)

// Types de permissions : Downloader, Uploader, Lister, Attrser, Remover,
// SignedDownloader, SignedUploader, ReadWriter

Secrets

package email

var secrets struct {
    SendGridAPIKey string
    SMTPPassword   string
}

func sendEmail() error {
    apiKey := secrets.SendGridAPIKey
    // Utiliser le secret...
}

Définissez les secrets via CLI :

encore secret set --type prod SendGridAPIKey

Valeurs de Configuration

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

Recommandations

  • Les déclarations d'infrastructure DOIVENT être au niveau du package
  • Utilisez des noms descriptifs pour les ressources
  • Gardez les migrations séquentielles et numérotées
  • Les handlers de souscription doivent être idempotents (livraison au moins une fois)
  • Les secrets sont accédés en les appelant comme des fonctions
  • Les endpoints cron doivent être private (internes uniquement)
  • Chaque service a généralement sa propre base de données

Skills similaires