Back to Encore

Code snippets

docs/go/primitives/code-snippets.md

1.56.96.1 KB
Original Source

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

go
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

go
// 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

go
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

go
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.go --
package 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.
-- todo/migrations/1_create_table.up.sql --
CREATE TABLE todo_item (
  id BIGSERIAL PRIMARY KEY,
  title TEXT NOT NULL,
  done BOOLEAN NOT NULL DEFAULT false
  -- etc...
);

Inserting data into a database

One way of inserting data is with a helper function that uses the package function sqldb.Exec:

go
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:

go
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

go
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

go
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)

go
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.

go
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

go
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

go
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

shell
$ 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

go
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.