Code snippets
Shortcuts for building with Encore
When you're familiar with how Encore works, you can simplify your development workflow by copy-pasting these examples. If you're looking for details on how Encore works, please refer to the relevant docs section.
APIs
Defining APIs
package hello // service name
//encore:api public
func Ping(ctx context.Context, params *PingParams) (*PingResponse, error) {
msg := fmt.Sprintf("Hello, %s!", params.Name)
return &PingResponse{Message: msg}, nil
}
Defining Request and Response schemas
// PingParams is the request data for the Ping endpoint.
type PingParams struct {
Name string
}
// PingResponse is the response data for the Ping endpoint.
type PingResponse struct {
Message string
}
Calling APIs
import "encore.app/hello" // import service
//encore:api public
func MyOtherAPI(ctx context.Context) error {
resp, err := hello.Ping(ctx, &hello.PingParams{Name: "World"})
if err == nil {
log.Println(resp.Message) // "Hello, World!"
}
return err
}
Hint: Import the service package and call the API endpoint using a regular function call.
Receive Webhooks
import "net/http"
// Webhook receives incoming webhooks from Some Service That Sends Webhooks.
//encore:api public raw
func Webhook(w http.ResponseWriter, req *http.Request) {
// ... operate on the raw HTTP request ...
}
Hint: Like any other API endpoint, this will be exposed at:
https://<env>-<app-id>.encr.app/service.Webhook
Databases
Creating a SQL database
To create a database, import encore.dev/storage/sqldb
and call sqldb.NewDatabase
, assigning the result to a package-level variable.
sqldb.DatabaseConfig
specifies the directory containing the database migration files, which is how you define the database schema.
todo/db.gotodo/migrations/1_create_table.up.sqlpackage todo
// Create the todo database and assign it to the "tododb" variable
var tododb = sqldb.NewDatabase("todo", sqldb.DatabaseConfig{
Migrations: "./migrations",
})
// Then, query the database using db.QueryRow, db.Exec, etc.
Inserting data into a database
One way of inserting data is with a helper function that uses the package function sqldb.Exec
:
import "encore.dev/storage/sqldb"
// insert inserts a todo item into the database.
func insert(ctx context.Context, id, title string, done bool) error {
_, err := tododb.Exec(ctx, `
INSERT INTO todo_item (id, title, done)
VALUES ($1, $2, $3)
`, id, title, done)
return err
}
Querying a database
To read a single todo item in the example schema above, we can use sqldb.QueryRow
:
import "encore.dev/storage/sqldb"
var item struct {
ID int64
Title string
Done bool
}
err := tododb.QueryRow(ctx, `
SELECT id, title, done
FROM todo_item
LIMIT 1
`).Scan(&item.ID, &item.Title, &item.Done)
Hint: If sqldb.QueryRow
does not find a matching row, it reports an error that can be checked against
by importing the standard library errors
package and calling errors.Is(err, sqldb.ErrNoRows)
.
Defining a Cron Job
import "encore.dev/cron"
var _ = cron.NewJob("welcome-email", cron.JobConfig{
Title: "Send welcome emails",
Every: 2 * cron.Hour,
Endpoint: SendWelcomeEmail,
})
//encore:api private
func SendWelcomeEmail(ctx context.Context) error {
// ...
return nil
}
Hint: Cron Jobs do not run in your local development environment.
PubSub
Creating a PubSub topic
import "encore.dev/pubsub"
type SignupEvent struct { UserID int }
var Signups = pubsub.NewTopic[*SignupEvent]("signups", pubsub.TopicConfig {
DeliveryGuarantee: pubsub.AtLeastOnce,
})
Hint: Topics are declared as package level variables and cannot be created inside functions. Regardless of where you create a topic, it can be published and subscribed to from any service.
Publishing an Event (Pub)
if _, err := Signups.Publish(ctx, &SignupEvent{UserID: id}); err != nil {
return err
}
if err := tx.Commit(); err != nil {
return err
}
Hint: If you want to publish to the topic from another service, import the topic package variable (Signups
in this example) and call publish on it from there.
Subscribing to Events (Sub)
Create a Subscription as a package level variable by calling pubsub.NewSubscription
.
var _ = pubsub.NewSubscription(
user.Signups, "send-welcome-email",
pubsub.SubscriptionConfig[*SignupEvent] {
Handler: SendWelcomeEmail,
},
)
func SendWelcomeEmail(ctx context.Context, event *SignupEvent) error {
... send email ...
return nil
}
Defining a Cache cluster
import "encore.dev/storage/cache"
var MyCacheCluster = cache.NewCluster("my-cache-cluster", cache.ClusterConfig{
// EvictionPolicy tells Redis how to evict keys when the cache reaches
// its memory limit. For typical cache use cases, cache.AllKeysLRU is a good default.
EvictionPolicy: cache.AllKeysLRU,
})
Secrets
Defining Secrets
var secrets struct {
GitHubAPIToken string // personal access token for deployments
SomeOtherSecret string // some other secret
}
Hint: The variable must be an unexported struct named secrets
, and all the fields must be of type string
.
Setting secret values
$ encore secret set --type <types...> <secret-name>
Hint: <types>
defines which environment types the secret value applies to. Use a comma-separated list of production
, development
, preview
, and local
. For each Secret, there can only be one secret value for each environment type.
Using secrets
func callGitHub(ctx context.Context) {
req, _ := http.NewRequestWithContext(ctx, "GET", "https:///api.github.com/user", nil)
req.Header.Add("Authorization", "token " + secrets.GitHubAPIToken)
resp, err := http.DefaultClient.Do(req)
// ... handle err and resp
}
Hint: Secret keys are globally unique for your whole application; if multiple services use the same secret name they both receive the same secret value at runtime.