Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Add Docs
Encore
https://github.com/encoredev/encore
Admin
Open Source Development Platform for building robust type-safe distributed systems with declarative
...
Tokens:
335,314
Snippets:
2,701
Trust Score:
8.6
Update:
2 months ago
Context
Skills
Chat
Benchmark
91.8
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Encore Framework Encore is an open-source backend framework for building type-safe distributed systems with declarative infrastructure. Available for both TypeScript and Go, it lets developers define APIs, services, and infrastructure resources (databases, Pub/Sub, caching, object storage, cron jobs) as type-safe objects directly in application code. The framework handles all infrastructure provisioning automatically—locally via Docker during development and on AWS/GCP in production—eliminating the need for Terraform, CloudFormation, or manual DevOps configuration. The Encore CLI provides powerful local development tools including distributed tracing, API documentation, service catalogs, architecture diagrams, and a database explorer. When deployed via Encore Cloud, it automatically provisions cloud-native infrastructure (RDS, Cloud SQL, SQS, GCP Pub/Sub, S3, etc.) in your AWS or GCP account according to best practices. Alternatively, you can self-host by exporting Docker images and providing your own infrastructure configuration. ## Defining API Endpoints Create type-safe HTTP APIs by annotating Go functions with `//encore:api`. Encore automatically handles routing, request/response serialization, and generates OpenAPI documentation. ```go package hello import ( "context" "fmt" ) // PingParams defines the request structure type PingParams struct { Name string `json:"name"` } // PingResponse defines the response structure type PingResponse struct { Message string `json:"message"` } // Ping is a public API endpoint accessible at POST /hello.Ping //encore:api public func Ping(ctx context.Context, params *PingParams) (*PingResponse, error) { msg := fmt.Sprintf("Hello, %s!", params.Name) return &PingResponse{Message: msg}, nil } ``` ## REST API with Path Parameters Define RESTful endpoints with HTTP methods and path parameters using the `method` and `path` annotations. ```go package blog import "context" type BlogPost struct { ID int64 `json:"id"` Title string `json:"title"` Content string `json:"content"` } // GetBlogPost retrieves a blog post by ID // GET /blog/:id //encore:api public method=GET path=/blog/:id func GetBlogPost(ctx context.Context, id int64) (*BlogPost, error) { // Query database for blog post... return &BlogPost{ID: id, Title: "My Post", Content: "Content here"}, nil } // UpdateBlogPost updates an existing blog post // PUT /blog/:id with JSON body //encore:api public method=PUT path=/blog/:id func UpdateBlogPost(ctx context.Context, id int64, post *BlogPost) error { // Update blog post in database... return nil } // ListBlogPosts returns paginated blog posts // GET /blog?limit=10&offset=0 //encore:api public method=GET path=/blog func ListBlogPosts(ctx context.Context, params *ListParams) (*ListResponse, error) { return &ListResponse{Posts: []*BlogPost{}}, nil } type ListParams struct { Limit uint `query:"limit"` Offset uint `query:"offset"` } type ListResponse struct { Posts []*BlogPost `json:"posts"` } ``` ## API Access Levels Control API visibility with three access levels: `public` (internet-accessible), `private` (internal only), and `auth` (requires authentication). ```go package user import "context" // Public API - accessible by anyone //encore:api public method=GET path=/health func Health(ctx context.Context) error { return nil } // Private API - only callable from other services or cron jobs //encore:api private func InternalSync(ctx context.Context) error { // Internal synchronization logic... return nil } // Auth API - requires valid authentication token //encore:api auth method=GET path=/profile func GetProfile(ctx context.Context) (*Profile, error) { // auth.UserID() is guaranteed to be set here return &Profile{}, nil } type Profile struct { Name string `json:"name"` Email string `json:"email"` } ``` ## Service-to-Service API Calls Call APIs in other services like regular function calls. Encore handles service discovery, networking, and serialization automatically. ```go package orders import ( "context" "encore.app/users" // Import the users service "encore.app/inventory" ) //encore:api public method=POST path=/orders func CreateOrder(ctx context.Context, req *CreateOrderRequest) (*Order, error) { // Call the users service to verify the user user, err := users.GetUser(ctx, req.UserID) if err != nil { return nil, err } // Call inventory service to check stock stock, err := inventory.CheckStock(ctx, &inventory.StockRequest{ ProductID: req.ProductID, Quantity: req.Quantity, }) if err != nil { return nil, err } // Create order... return &Order{ID: 123, UserID: user.ID}, nil } type CreateOrderRequest struct { UserID string `json:"user_id"` ProductID string `json:"product_id"` Quantity int `json:"quantity"` } type Order struct { ID int64 `json:"id"` UserID string `json:"user_id"` } ``` ## SQL Database Create PostgreSQL databases with automatic migrations. Encore provisions local databases via Docker and managed databases in the cloud. ```go package todo import ( "context" "encore.dev/storage/sqldb" ) // Create database with migrations directory var tododb = sqldb.NewDatabase("todo", sqldb.DatabaseConfig{ Migrations: "./migrations", }) // migrations/1_create_table.up.sql: // CREATE TABLE todo_item ( // id BIGSERIAL PRIMARY KEY, // title TEXT NOT NULL, // done BOOLEAN NOT NULL DEFAULT false // ); type TodoItem struct { ID int64 `json:"id"` Title string `json:"title"` Done bool `json:"done"` } // Insert a new todo item func insertTodo(ctx context.Context, title string) (int64, error) { var id int64 err := tododb.QueryRow(ctx, ` INSERT INTO todo_item (title, done) VALUES ($1, false) RETURNING id `, title).Scan(&id) return id, err } // Query todo items //encore:api public method=GET path=/todos func ListTodos(ctx context.Context) (*ListResponse, error) { rows, err := tododb.Query(ctx, ` SELECT id, title, done FROM todo_item ORDER BY id `) if err != nil { return nil, err } defer rows.Close() var items []TodoItem for rows.Next() { var item TodoItem if err := rows.Scan(&item.ID, &item.Title, &item.Done); err != nil { return nil, err } items = append(items, item) } return &ListResponse{Items: items}, nil } type ListResponse struct { Items []TodoItem `json:"items"` } ``` ## Pub/Sub Messaging Create topics and subscriptions for asynchronous event-driven communication. Encore automatically provisions NSQ locally, GCP Pub/Sub, or AWS SNS/SQS in the cloud. ```go package user import ( "context" "encore.dev/pubsub" ) // Define event type type SignupEvent struct { UserID string `json:"user_id"` Email string `json:"email"` } // Create a topic (package-level variable) var Signups = pubsub.NewTopic[*SignupEvent]("signups", pubsub.TopicConfig{ DeliveryGuarantee: pubsub.AtLeastOnce, }) // Publish events when users sign up //encore:api public method=POST path=/signup func Signup(ctx context.Context, req *SignupRequest) (*SignupResponse, error) { userID := generateUserID() // Save user to database... // Publish signup event msgID, err := Signups.Publish(ctx, &SignupEvent{ UserID: userID, Email: req.Email, }) if err != nil { return nil, err } return &SignupResponse{UserID: userID, MessageID: msgID}, nil } type SignupRequest struct { Email string `json:"email"` Password string `json:"password"` } type SignupResponse struct { UserID string `json:"user_id"` MessageID string `json:"message_id"` } func generateUserID() string { return "user_123" } ``` ```go package email import ( "context" "encore.dev/pubsub" "encore.app/user" ) // Subscribe to signup events var _ = pubsub.NewSubscription( user.Signups, "send-welcome-email", pubsub.SubscriptionConfig[*user.SignupEvent]{ Handler: SendWelcomeEmail, RetryPolicy: &pubsub.RetryPolicy{ MaxRetries: 5, }, }, ) // Handler function - called for each event func SendWelcomeEmail(ctx context.Context, event *user.SignupEvent) error { // Send welcome email to event.Email return nil } ``` ## Caching with Redis Create Redis cache clusters with type-safe keyspaces. Encore provisions local in-memory caches and managed Redis (ElastiCache/Memorystore) in the cloud. ```go package api import ( "context" "time" "encore.dev/storage/cache" ) // Create cache cluster var MyCacheCluster = cache.NewCluster("my-cache", cache.ClusterConfig{ EvictionPolicy: cache.AllKeysLRU, }) // Define typed keyspace for user sessions type SessionData struct { UserID string `json:"user_id"` CreatedAt time.Time `json:"created_at"` } var Sessions = cache.NewStructKeyspace[string, SessionData]( MyCacheCluster, cache.KeyspaceConfig{ KeyPattern: "session/:key", DefaultExpiry: cache.ExpireIn(24 * time.Hour), }, ) // Store session in cache func CreateSession(ctx context.Context, sessionID string, userID string) error { return Sessions.Set(ctx, sessionID, SessionData{ UserID: userID, CreatedAt: time.Now(), }) } // Retrieve session from cache func GetSession(ctx context.Context, sessionID string) (*SessionData, error) { session, err := Sessions.Get(ctx, sessionID) if err != nil { return nil, err } return &session, nil } // Integer keyspace for rate limiting var RequestCounts = cache.NewIntKeyspace[string](MyCacheCluster, cache.KeyspaceConfig{ KeyPattern: "requests/:key", DefaultExpiry: cache.ExpireIn(10 * time.Second), }) func IncrementRequestCount(ctx context.Context, userID string) (int64, error) { return RequestCounts.Increment(ctx, userID, 1) } ``` ## Cron Jobs Schedule recurring tasks with cron expressions or intervals. Encore automatically manages job scheduling and execution. ```go package reports import ( "context" "encore.dev/cron" ) // Run every 2 hours var _ = cron.NewJob("daily-report", cron.JobConfig{ Title: "Generate Daily Reports", Every: 2 * cron.Hour, Endpoint: GenerateDailyReport, }) // The endpoint must be a private API with no parameters //encore:api private func GenerateDailyReport(ctx context.Context) error { // Generate and send reports... return nil } // Run at specific time using cron expression (4 AM UTC on 15th of each month) var _ = cron.NewJob("monthly-billing", cron.JobConfig{ Title: "Process Monthly Billing", Schedule: "0 4 15 * *", Endpoint: ProcessMonthlyBilling, }) //encore:api private func ProcessMonthlyBilling(ctx context.Context) error { // Process billing... return nil } ``` ## Object Storage Store and retrieve files using S3-compatible object storage. Encore provisions local filesystem storage and managed buckets (S3/GCS) in the cloud. ```go package files import ( "context" "io" "net/http" "encore.dev/storage/objects" "encore.dev/beta/errs" ) // Create bucket for profile pictures var ProfilePictures = objects.NewBucket("profile-pictures", objects.BucketConfig{ Versioned: false, }) // Upload file to bucket //encore:api auth raw method=POST path=/upload/:key func Upload(w http.ResponseWriter, req *http.Request) { key := req.PathValue("key") writer := ProfilePictures.Upload(req.Context(), key) _, err := io.Copy(writer, req.Body) if err != nil { writer.Abort() errs.HTTPError(w, err) return } if err := writer.Close(); err != nil { errs.HTTPError(w, err) return } w.WriteHeader(http.StatusOK) } // Download file from bucket //encore:api public raw method=GET path=/download/:key func Download(w http.ResponseWriter, req *http.Request) { key := req.PathValue("key") reader := ProfilePictures.Download(req.Context(), key) if err := reader.Err(); err != nil { errs.HTTPError(w, err) return } w.Header().Set("Content-Type", "application/octet-stream") io.Copy(w, reader) } // List objects in bucket func ListFiles(ctx context.Context) ([]string, error) { var keys []string for err, entry := range ProfilePictures.List(ctx, &objects.Query{}) { if err != nil { return nil, err } keys = append(keys, entry.Key) } return keys, nil } // Delete object func DeleteFile(ctx context.Context, key string) error { return ProfilePictures.Remove(ctx, key) } ``` ## Secrets Management Store and use secrets securely. Secrets are encrypted at rest and injected at runtime. ```go package payment import ( "context" "net/http" ) // Define secrets as unexported struct var secrets struct { StripeAPIKey string WebhookSecret string } // Use secrets in code //encore:api private func ProcessPayment(ctx context.Context, req *PaymentRequest) error { client := &http.Client{} httpReq, _ := http.NewRequestWithContext(ctx, "POST", "https://api.stripe.com/v1/charges", nil) // Use secret like a regular variable httpReq.Header.Set("Authorization", "Bearer "+secrets.StripeAPIKey) resp, err := client.Do(httpReq) if err != nil { return err } defer resp.Body.Close() return nil } type PaymentRequest struct { Amount int64 `json:"amount"` Currency string `json:"currency"` } ``` Set secrets via CLI: ```bash # Set for production encore secret set --type prod StripeAPIKey # Set for development and local encore secret set --type dev,local StripeAPIKey ``` ## Authentication Handler Define custom authentication logic with an auth handler. Encore automatically validates tokens and propagates auth data across service calls. ```go package auth import ( "context" "encore.dev/beta/auth" "encore.dev/beta/errs" ) // Custom auth data available to all handlers type AuthData struct { UserID string Email string IsAdmin bool } // Auth handler validates tokens and returns user info //encore:authhandler func AuthHandler(ctx context.Context, token string) (auth.UID, *AuthData, error) { // Validate token (e.g., JWT verification, database lookup) userID, email, isAdmin, err := validateToken(token) if err != nil { return "", nil, &errs.Error{ Code: errs.Unauthenticated, Message: "invalid or expired token", } } return auth.UID(userID), &AuthData{ UserID: userID, Email: email, IsAdmin: isAdmin, }, nil } func validateToken(token string) (string, string, bool, error) { // Token validation logic... return "user_123", "user@example.com", false, nil } ``` ```go package profile import ( "context" "encore.dev/beta/auth" myauth "encore.app/auth" ) // This endpoint requires authentication //encore:api auth method=GET path=/me func GetMyProfile(ctx context.Context) (*Profile, error) { // Get authenticated user ID (guaranteed to be set) userID, _ := auth.UserID() // Get custom auth data data := auth.Data().(*myauth.AuthData) return &Profile{ ID: string(userID), Email: data.Email, IsAdmin: data.IsAdmin, }, nil } type Profile struct { ID string `json:"id"` Email string `json:"email"` IsAdmin bool `json:"is_admin"` } ``` ## Middleware Create reusable middleware for cross-cutting concerns like validation, logging, or rate limiting. ```go package middleware import ( "encore.dev/beta/errs" "encore.dev/middleware" ) // Global middleware applied to all endpoints //encore:middleware global target=all func ValidationMiddleware(req middleware.Request, next middleware.Next) middleware.Response { // Validate request payload if it has a Validate method if validator, ok := req.Data().Payload.(interface{ Validate() error }); ok { if err := validator.Validate(); err != nil { return middleware.Response{ Err: &errs.Error{ Code: errs.InvalidArgument, Message: err.Error(), }, } } } return next(req) } // Targeted middleware using tags //encore:middleware target=tag:logged func LoggingMiddleware(req middleware.Request, next middleware.Next) middleware.Response { data := req.Data() // Log request... resp := next(req) // Log response... return resp } ``` ```go package api import "context" // Apply middleware via tags //encore:api public method=POST path=/action tag:logged func SomeAction(ctx context.Context) error { return nil } ``` ## Structured API Errors Return structured errors with codes, messages, and metadata using the `errs` package. ```go package orders import ( "context" "errors" "encore.dev/beta/errs" "encore.dev/storage/sqldb" ) //encore:api public method=GET path=/orders/:id func GetOrder(ctx context.Context, id int64) (*Order, error) { // Create error builder with metadata eb := errs.B().Meta("order_id", id) var order Order err := db.QueryRow(ctx, `SELECT id, status FROM orders WHERE id = $1`, id). Scan(&order.ID, &order.Status) if errors.Is(err, sqldb.ErrNoRows) { return nil, eb.Code(errs.NotFound).Msg("order not found").Err() } if err != nil { return nil, eb.Cause(err).Msg("failed to fetch order").Err() } return &order, nil } // Direct error construction //encore:api auth method=DELETE path=/orders/:id func DeleteOrder(ctx context.Context, id int64) error { if !userCanDelete(ctx, id) { return &errs.Error{ Code: errs.PermissionDenied, Message: "you don't have permission to delete this order", } } // Delete order... return nil } type Order struct { ID int64 `json:"id"` Status string `json:"status"` } func userCanDelete(ctx context.Context, id int64) bool { return false } var db *sqldb.Database ``` ## Configuration Define environment-specific configuration using CUE files with type-safe Go bindings. ```go package myservice import "encore.dev/config" type Config struct { MaxConnections config.Int DebugMode config.Bool APIEndpoint config.String } var cfg = config.Load[*Config]() //encore:api private func DoSomething(ctx context.Context) error { if cfg.DebugMode() { // Debug logging... } maxConn := cfg.MaxConnections() endpoint := cfg.APIEndpoint() // Use config values... _ = maxConn _ = endpoint return nil } ``` ```cue // myservice/config.cue MaxConnections: 100 DebugMode: false APIEndpoint: "https://api.example.com" // Override for specific environments if #Meta.Environment.Type == "development" { DebugMode: true MaxConnections: 10 } ``` ## Testing Write tests using Go's standard testing package with Encore's test infrastructure. ```go package todo import ( "context" "testing" "encore.dev/et" ) func TestCreateTodo(t *testing.T) { ctx := context.Background() // Call API directly in tests resp, err := Create(ctx, &CreateRequest{Title: "Test Todo"}) if err != nil { t.Fatal(err) } if resp.Title != "Test Todo" { t.Errorf("expected title 'Test Todo', got '%s'", resp.Title) } } func TestWithIsolatedDatabase(t *testing.T) { // Create temporary database for this test only testDB := et.NewTestDatabase(ctx, tododb) _ = testDB // Use testDB for isolated testing // Test with clean database state... } func TestPubSubPublishing(t *testing.T) { ctx := context.Background() // Trigger action that publishes events _, _ = Signup(ctx, &SignupRequest{Email: "test@example.com"}) // Verify published messages msgs := et.Topic(Signups).PublishedMessages() if len(msgs) != 1 { t.Errorf("expected 1 message, got %d", len(msgs)) } } ``` Run tests: ```bash encore test ./... encore test -v ./myservice/... ``` ## Client Generation Generate type-safe API clients for TypeScript, Go, or OpenAPI specifications. ```bash # Generate TypeScript client encore gen client myapp --output=./client.ts --env=staging # Generate Go client encore gen client myapp --output=./client.go --env=local # Generate OpenAPI spec encore gen client myapp --lang=openapi --output=./openapi.json # Generate for specific services only encore gen client myapp --services=users,orders --output=./client.ts ``` ```typescript // Using generated TypeScript client import Client from './client'; const client = new Client({ baseURL: 'https://myapp-abc123.encr.app', auth: 'your-auth-token' }); // Call API with full type safety const response = await client.users.GetProfile(); console.log(response.email); ``` ## Summary Encore is designed for teams building production backends who want to eliminate infrastructure complexity while maintaining full control over their systems. It excels at microservices architectures where type-safe service-to-service communication, event-driven patterns with Pub/Sub, and automatic infrastructure provisioning significantly reduce development time. The framework is particularly well-suited for startups needing to move fast without dedicated DevOps, and for larger teams wanting to standardize backend development patterns across services. Integration patterns include deploying to your own AWS/GCP account via Encore Cloud, self-hosting with Docker images and custom infrastructure configuration, or using the built-in development cloud for rapid prototyping. The generated API clients enable seamless frontend integration, while the CLI tools provide local development workflows with distributed tracing and real-time architecture diagrams. Whether building a new greenfield application or incrementally adopting Encore in an existing system through service-by-service migration, the framework provides escape hatches at every level to ensure you're never locked in.