About encore-go-service
A guide to organizing Encore.go applications into services and packages for Go developers. It explains how API-bearing packages become services, how services call one another, and how shared helper packages fit into an application.
Key features
- 01Packages with API endpoints become services
- 02Single- and multi-service layouts
- 03System-based service grouping
- 04Direct service-to-service calls
- 05Shared non-service packages
Use cases
- 01Choosing service boundaries in an application
- 02Structuring a multi-service Go project
- 03Organizing reusable helper code
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ encoredev/ skills --skill encore-go-serviceAfter installing, type/encore-go-servicein Claude Code to confirm it loaded.
Details
From the project READMEEncore.go Service Structure
Instructions
In Encore.go, each package with an API endpoint is automatically a service. No special configuration needed.
Creating a Service
Simply create a package with at least one //encore:api endpoint:
// user/user.go
package user
import "context"
type User struct {
ID string `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
}
//encore:api public method=GET path=/users/:id
func GetUser(ctx context.Context, params *GetUserParams) (*User, error) {
// This makes "user" a service
}
Minimal Service Structure
user/
├── user.go # API endpoints
├── db.go # Database (if needed)
└── migrations/ # SQL migrations
└── 1_create_users.up.sql
Application Patterns
Single Service
An application can define its APIs in one root service package:
my-app/
├── encore.app
├── go.mod
├── api.go # All endpoints
├── db.go # Database
└── migrations/
└── 1_initial.up.sql
Multi-Service
Each service lives in its own package:
my-app/
├── encore.app
├── go.mod
├── user/
│ ├── user.go
│ ├── db.go
│ └── migrations/
├── order/
│ ├── order.go
│ ├── db.go
│ └── migrations/
└── notification/
└── notification.go
Large Application (System-based)
Group related services into systems:
my-app/
├── encore.app
├── go.mod
├── commerce/
│ ├── order/
│ │ └── order.go
│ ├── cart/
│ │ └── cart.go
│ └── payment/
│ └── payment.go
├── identity/
│ ├── user/
│ │ └── user.go
│ └── auth/
│ └── auth.go
└── comms/
├── email/
│ └── email.go
└── push/
└── push.go
Service-to-Service Calls
Just import and call the function directly - Encore handles the RPC:
package order
import (
"context"
"encore.app/user" // Import the user service
)
//encore:api auth method=GET path=/orders/:id
func GetOrderWithUser(ctx context.Context, params *GetOrderParams) (*OrderWithUser, error) {
order, err := getOrder(ctx, params.ID)
if err != nil {
return nil, err
}
// This becomes an RPC call - Encore handles it
orderUser, err := user.GetUser(ctx, &user.GetUserParams{ID: order.UserID})
if err != nil {
return nil, err
}
return &OrderWithUser{Order: order, User: orderUser}, nil
}
Internal Helpers (Non-Service Packages)
Create packages without //encore:api endpoints for shared code:
my-app/
├── user/
│ └── user.go # Service (has API)
├── order/
│ └── order.go # Service (has API)
└── internal/
├── util/
│ └── util.go # Not a service (no API)
└── validation/
└── validate.go
Guidelines
- A package becomes a service when it has
//encore:apiendpoints - Services cannot be nested within other services
- Cross-service calls look like regular function calls
- Each service can have its own database
- Package names should be lowercase, descriptive
- Don't create services just for code organization - use sub-packages instead
- Use
encore-architecturewhen the service boundaries have not been decided