Skip to main content
Technology & EngineeringBaas252 lines

Encore

Encore is a backend development platform that automatically provisions, configures, and manages cloud infrastructure based on your Go code. It simplifies building and deploying cloud-native applications by allowing you to focus purely on business logic.

Quick Summary23 lines
You are an expert in Encore, mastering its unique infra-from-code paradigm for building scalable, cloud-native Go applications. You adeptly leverage its static analysis capabilities to provision and manage cloud infrastructure automatically, streamline API development, integrate with managed services, and orchestrate complex microservice architectures with unparalleled developer experience.

## Key Points

1.  **Install the Encore CLI:**
2.  **Initialize a new Encore application:**
3.  **Define your first service and API:**
4.  **Run your application locally:**
*   **Define Clear API Contracts:** Use Go structs for API request and response bodies. This ensures type safety and allows Encore to generate robust clients and documentation.
*   **Write Comprehensive Tests:** Encore services are standard Go code, making them easy to test with Go's built-in testing framework. Focus on unit and integration tests for your business logic.

## Quick Example

```bash
go install encore.dev/cli/encore@latest
```

```bash
encore app init my-encore-app
    cd my-encore-app
```
skilldb get baas-skills/EncoreFull skill: 252 lines
Paste into your CLAUDE.md or agent config

Encore — Infra from Code Backend Development

You are an expert in Encore, mastering its unique infra-from-code paradigm for building scalable, cloud-native Go applications. You adeptly leverage its static analysis capabilities to provision and manage cloud infrastructure automatically, streamline API development, integrate with managed services, and orchestrate complex microservice architectures with unparalleled developer experience.

Core Philosophy

Encore's core philosophy centers on eliminating the "undifferentiated heavy lifting" of cloud infrastructure management. By analyzing your Go code, Encore automatically understands your application's architecture—identifying APIs, database schemas, secrets, and inter-service dependencies—and provisions the necessary cloud resources (compute, databases, queues, caches, etc.) on AWS, GCP, or Azure. This "infra-from-code" approach means you define your infrastructure implicitly through your application code, freeing you from writing YAML, configuring Terraform, or managing Kubernetes.

You choose Encore when you want to accelerate backend development, reduce operational overhead, and maintain a high level of code quality and consistency across your services. It's particularly well-suited for teams building full-stack applications, microservices, or APIs that require robust, scalable cloud infrastructure without the steep learning curve and complexity traditionally associated with cloud-native development. Encore promotes a Go-first approach, leveraging Go's strong typing and tooling to provide a seamless development and deployment experience from local machine to production.

Setup

Encore requires the Encore CLI to be installed, which is used to manage your applications, run them locally, and deploy them to the cloud.

  1. Install the Encore CLI:

    go install encore.dev/cli/encore@latest
    
  2. Initialize a new Encore application:

    encore app init my-encore-app
    cd my-encore-app
    

    This creates a basic project structure with a main.go file and an encore.app configuration file.

  3. Define your first service and API:

    Create a new directory for your service, e.g., pkg/greeting, and a file pkg/greeting/service.go.

    // pkg/greeting/service.go
    package greeting
    
    import (
    	"context"
    	"encore.dev/beta/encore"
    )
    
    //encore:api public path=/hello/:name
    func SayHello(ctx context.Context, name string) (*GreetingResponse, error) {
    	return &GreetingResponse{Message: "Hello, " + name + "!"}, nil
    }
    
    type GreetingResponse struct {
    	Message string
    }
    
  4. Run your application locally:

    encore run
    

    Encore will compile your application, start local services, and expose your API on localhost:4000. You can then access it: curl http://localhost:4000/hello/World.

Key Techniques

1. Exposing an HTTP API Endpoint

Define an API endpoint using the //encore:api comment directive. Encore automatically generates a strongly typed client for inter-service communication and provisions a public HTTP endpoint.

// pkg/user/service.go
package user

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

type User struct {
	ID    string `json:"id"`
	Name  string `json:"name"`
	Email string `json:"email"`
}

//encore:api public method=GET path=/users/:id
func GetUser(ctx context.Context, id string) (*User, error) {
	// In a real app, you'd fetch this from a database or another service.
	// For demonstration, return a mock user.
	if id == "123" {
		return &User{ID: "123", Name: "Alice Smith", Email: "alice@example.com"}, nil
	}
	return nil, encore.Errorf("user not found")
}

//encore:api public method=POST path=/users
func CreateUser(ctx context.Context, params *CreateUserParams) (*User, error) {
	// In a real app, generate ID, save to DB, etc.
	newUser := &User{
		ID:    "new-user-id", // Mock ID
		Name:  params.Name,
		Email: params.Email,
	}
	return newUser, nil
}

type CreateUserParams struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

2. Integrating with an Encore SQL Database

Encore allows you to define managed SQL databases (Postgres) directly in your code. It handles provisioning, migrations, and connection management.

// pkg/product/service.go
package product

import (
	"context"
	"database/sql"
	"encore.dev/beta/encore"
	_ "github.com/lib/pq" // PostgreSQL driver
)

//encore:service
type Service struct {
	db *encore.SQLDatabase
}

//encore:startup
func (s *Service) Startup(ctx context.Context) error {
	// Encore automatically creates and manages a PostgreSQL database.
	// The `migrations` directory contains SQL files for schema management.
	s.db = encore.NewSQLDatabase("productdb", encore.SQLDatabaseConfig{
		Migrations: "./migrations",
	})
	return nil
}

type Product struct {
	ID    string  `json:"id"`
	Name  string  `json:"name"`
	Price float64 `json:"price"`
}

//encore:api public method=GET path=/products/:id
func (s *Service) GetProduct(ctx context.Context, id string) (*Product, error) {
	var p Product
	err := s.db.QueryRow(ctx, "SELECT id, name, price FROM products WHERE id = $1", id).Scan(&p.ID, &p.Name, &p.Price)
	if err == sql.ErrNoRows {
		return nil, encore.Errorf("product not found")
	} else if err != nil {
		return nil, encore.Wrap(err, "failed to get product")
	}
	return &p, nil
}

And your migration file pkg/product/migrations/1_initial_schema.up.sql:

CREATE TABLE products (
    id TEXT PRIMARY KEY,
    name TEXT NOT NULL,
    price NUMERIC(10, 2) NOT NULL
);

3. Inter-Service Communication

Encore automatically generates strongly typed clients for your services, enabling seamless and type-safe communication between them.

// pkg/order/service.go
package order

import (
	"context"
	"encore.dev/beta/encore"
	"my-encore-app/pkg/product" // Import the product service
	"my-encore-app/pkg/user"    // Import the user service
)

//encore:service
type Service struct {
	products product.Service // Encore injects the client for the product service
	users    user.Service    // Encore injects the client for the user service
}

type CreateOrderParams struct {
	UserID    string `json:"userId"`
	ProductID string `json:"productId"`
	Quantity  int    `json:"quantity"`
}

type Order struct {
	ID       string `json:"id"`
	UserID   string `json:"userId"`
	Product  *product.Product `json:"product"`
	Quantity int    `json:"quantity"`
	Status   string `json:"status"`
}

//encore:api public method=POST path=/orders
func (s *Service) CreateOrder(ctx context.Context, params *CreateOrderParams) (*Order, error) {
	// Call the user service to verify the user
	u, err := s.users.GetUser(ctx, params.UserID)
	if err != nil {
		return nil, encore.Wrap(err, "failed to get user")
	}

	// Call the product service to get product details
	p, err := s.products.GetProduct(ctx, params.ProductID)
	if err != nil {
		return nil, encore.Wrap(err, "failed to get product")
	}

	// Logic to create an order...
	newOrder := &Order{
		ID:       "order-id-123", // Mock ID
		UserID:   u.ID,
		Product:  p,
		Quantity: params.Quantity,
		Status:   "Pending",
	}

	return newOrder, nil
}

Best Practices

  • Leverage Encore's Static Analysis: Treat warnings from encore run or encore deploy as errors. Encore's analyzer guides you to correctly define services, APIs, and infrastructure, preventing common cloud configuration mistakes.
  • Use Built-in Services: Whenever possible, use encore.SQLDatabase, encore.Cache, encore.Queue, encore.Secret, etc., instead of manually integrating with external cloud services. This simplifies provisioning, scaling, and operational management.
  • Define Clear API Contracts: Use Go structs for API request and response bodies. This ensures type safety and allows Encore to generate robust clients and documentation.
  • Implement Robust Error Handling: Return error from your API functions. Use encore.Errorf for user-facing errors and encore.Wrap to add context to underlying errors. Encore automatically logs unhandled errors.
  • Structure Your Project Logically: Organize services into pkg/servicename directories. Keep related types, API definitions, and database migrations within their respective service directories.
  • Write Comprehensive Tests: Encore services are standard Go code, making them easy to test with Go's built-in testing framework. Focus on unit and integration tests for your business logic.
  • Monitor and Observe: Utilize the Encore Cloud Dashboard for logs, metrics, traces, and operational insights into your deployed applications. Integrate with external tools as needed via Encore's observability features.

Anti-Patterns

  • Manual Infrastructure Configuration. Don't attempt to manually configure cloud resources (e.g., via Terraform or CloudFormation) that Encore is designed to manage. This bypasses Encore's guarantees and introduces unnecessary complexity and potential conflicts. Let Encore handle the infrastructure from your code.
  • Ignoring Encore's Static Analysis. Treating static analysis warnings as non-critical. These warnings often indicate issues that will prevent deployment or lead to runtime errors. Address all warnings and errors proactively.
  • Directly Managing Cloud SDKs. Avoid using cloud provider SDKs (e.g., aws-sdk-go) directly within your Encore services for common resources like databases or queues. Instead, leverage Encore's managed primitives (encore.SQLDatabase, encore.Queue, etc.) for better integration, observability, and portability.
  • Over-reliance on Global State. Favor explicit dependencies and clean service boundaries. Global variables or singleton patterns can make services harder to test and reason about, especially in a distributed microservices environment.
  • Complex Monolithic Services. While Encore handles services well, avoid creating one massive //encore:service that encompasses too much unrelated logic. Break down functionality into smaller, focused Encore services to improve maintainability, scalability, and independent deployment.

Install this skill directly: skilldb add baas-skills

Get CLI access →