encore-go-database — Claude Code skill: what it does, how to install
Guides SQL queries, schema migrations, and PostgreSQL setup in Encore Go services using the sqldb package.
npx skills add encoredev/skills --skill encore-go-database -a claude-code
npx skills add encoredev/skills --skill go-database -a claude-codeWhat encore-go-database does
This skill teaches the coding agent how to manage PostgreSQL databases inside Encore Go services. It configures the database instance using sqldb.NewDatabase from encore.dev/storage/sqldb and manages schema migrations. Migration scripts reside in the migrations/ directory with sequential numeric prefixes such as 1_create_users.up.sql. Encore executes them on service launch.
Database operations follow Go's standard library conventions. The agent uses QueryRow and Query for reads, Exec for mutations, and atomic transactions via Begin and Commit. Method .Scan() maps columns by index position rather than name. If the argument sequence diverges from the SELECT query, values map to incorrect fields.
All database queries require parameterized arguments like $1 and $2 to prevent SQL injection. Single-row queries must explicitly handle sqldb.ErrNoRows. For existing ORM libraries, the skill exposes db.Stdlib().
What it helps with
- configure PostgreSQL connections via
sqldb.NewDatabase - structure ordered SQL migrations inside the
migrations/folder - execute parameterized queries using
QueryRow,Query, orExec - run atomic operations inside database transactions
Example prompts for the agent
- Create a
user_sessionstable and preparemigrations/1_sessions.up.sql - Add a Go function using
QueryRowandsqldb.ErrNoRowsto fetch a user byid - Implement an atomic balance transfer using
db.Beginand transaction rollback
How to install encore-go-database
1Skills CLI (Vercel)
npx skills add encoredev/skills --skill encore-go-database -a claude-codenpx skills add encoredev/skills --skill go-database -a claude-code2Manually: put the files in place
~/.claude/skills/go-database/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/go-database/
1Skills CLI (Vercel)
npx skills add encoredev/skills --skill go-database -a codex2Manually: put the files in place
.agents/skills/go-database/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add encoredev/skills --skill go-database -a cursor2Manually: put the files in place
.cursor/skills/go-database/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install encoredev/skills go-database2Manually: put the files in place
.github/skills/go-database/SKILL.md1Skills CLI (Vercel)
npx skills add encoredev/skills --skill go-database -a gemini-cli2Platform CLI
gemini skills install https://github.com/encoredev/skills --path encore/go-databaseManually: put the files in place
.devin/skills/go-database/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add encoredev/skills --skill go-database -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about encore-go-database
How to install encore-go-database in Claude Code?
Run npx skills add encoredev/skills --skill encore-go-database -a claude-code in your terminal.
What does the encore-go-database skill do?
It provides conventions and code patterns for PostgreSQL in Encore Go, including schema migrations, parameterized queries, and transaction management.
How does Encore Go handle schema migrations?
Migration files live in the migrations/ directory as sequentially numbered .up.sql files and run automatically when the service starts up.
Source text
SKILL.md · Apache-2.0
---
name: encore-go-database
description: Work with PostgreSQL in Encore Go using `sqldb.NewDatabase` from `encore.dev/storage/sqldb` — schema migrations and SQL queries.
when_to_use: >-
User wants to add a database table, write a migration, run a SQL query, insert/update/delete rows, or design a relational schema in an Encore Go service. Covers `sqldb.NewDatabase`, `db.QueryRow(ctx, ...)`, `db.Query(ctx, ...)`, `db.Exec(ctx, ...)`, `db.Stdlib()` for ORM integration, the `migrations/` directory, `*.up.sql` files, and sequential migration numbering. Trigger phrases: "Postgres table", "user_sessions table", "SQL", "migration", "QueryRow", "INSERT", "SELECT", "schema", "sqldb".
---
# Encore Go Database Operations
## Instructions
### Database Setup
```go
package user
import "encore.dev/storage/sqldb"
var db = sqldb.NewDatabase("userdb", sqldb.DatabaseConfig{
Migrations: "./migrations",
})
```
## Query Methods
Encore's database API mirrors Go's standard `database/sql` package. Use `.Scan()` to read query results into variables.
### `Query` - Multiple Rows
```go
type User struct {
ID string
Email string
Name string
}
func listActiveUsers(ctx context.Context) ([]*User, error) {
rows, err := db.Query(ctx, `
SELECT id, email, name FROM users WHERE active = true
`)
if err != nil {
return nil, err
}
defer rows.Close()
var users []*User
for rows.Next() {
var u User
if err := rows.Scan(&u.ID, &u.Email, &u.Name); err != nil {
return nil, err
}
users = append(users, &u)
}
return users, rows.Err()
}
```
### `QueryRow` - Single Row
```go
func getUser(ctx context.Context, id string) (*User, error) {
var u User
err := db.QueryRow(ctx, `
SELECT id, email, name FROM users WHERE id = $1
`, id).Scan(&u.ID, &u.Email, &u.Name)
if errors.Is(err, sqldb.ErrNoRows) {
return nil, &errs.Error{
Code: errs.NotFound,
Message: "user not found",
}
}
if err != nil {
return nil, err
}
return &u, nil
}
```
### `Exec` - No Return Value
For INSERT, UPDATE, DELETE operations:
```go
func createUser(ctx context.Context, email, name string) error {
_, err := db.Exec(ctx, `
INSERT INTO users (id, email, name)
VALUES ($1, $2, $3)
`, generateID(), email, name)
return err
}
func updateUser(ctx context.Context, id, name string) error {
_, err := db.Exec(ctx, `
UPDATE users SET name = $1 WHERE id = $2
`, name, id)
return err
}
func deleteUser(ctx context.Context, id string) error {
_, err := db.Exec(ctx, `
DELETE FROM users WHERE id = $1
`, id)
return err
}
```
## Migrations
### File Structure
```
user/
└── migrations/
├── 1_create_users.up.sql
├── 2_add_posts.up.sql
└── 3_add_indexes.up.sql
```
### Naming Convention
- Start with a number (1, 2, etc.)
- Followed by underscore and description
- End with `.up.sql`
- Numbers must be sequential
### Example Migration
```sql
-- migrations/1_create_users.up.sql
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_users_email ON users(email);
```
## Transactions
```go
func transferFunds(ctx context.Context, fromID, toID string, amount int) error {
tx, err := db.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback() // No-op if committed
_, err = tx.Exec(ctx, `
UPDATE accounts SET balance = balance - $1 WHERE id = $2
`, amount, fromID)
if err != nil {
return err
}
_, err = tx.Exec(ctx, `
UPDATE accounts SET balance = balance + $1 WHERE id = $2
`, amount, toID)
if err != nil {
return err
}
return tx.Commit()
}
```
## Using Scan
The `Scan` method reads columns from query results into variables. Columns are mapped by position, not by name - the order of arguments to `Scan` must match the order of columns in your SELECT statement.
```go
type User struct {
ID string
Email string
Name string
CreatedAt time.Time
}
// Single row with QueryRow
func getUser(ctx context.Context, id string) (*User, error) {
var u User
err := db.QueryRow(ctx, `
SELECT id, email, name, created_at FROM users WHERE id = $1
`, id).Scan(&u.ID, &u.Email, &u.Name, &u.CreatedAt)
if err != nil {
return nil, err
}
return &u, nil
}
// You can also scan into an inline struct
func getItem(ctx context.Context, id int64) error {
var item struct {
ID int64
Title string
Done bool
}
err := db.QueryRow(ctx, `
SELECT id, title, done FROM items WHERE id = $1
`, id).Scan(&item.ID, &item.Title, &item.Done)
return err
}
```
## SQL Injection Protection
Always use parameterized queries:
```go
// SAFE - values are parameterized
var u User
err := db.QueryRow(ctx, `
SELECT id, email, name FROM users WHERE email = $1
`, email).Scan(&u.ID, &u.Email, &u.Name)
// WRONG - SQL injection risk
query := fmt.Sprintf("SELECT * FROM users WHERE email = '%s'", email)
```
## Error Handling
```go
import (
"errors"
"encore.dev/storage/sqldb"
"encore.dev/beta/errs"
)
func getUser(ctx context.Context, id string) (*User, error) {
var u User
err := db.QueryRow(ctx, `
SELECT id, email, name FROM users WHERE id = $1
`, id).Scan(&u.ID, &u.Email, &u.Name)
if errors.Is(err, sqldb.ErrNoRows) {
return nil, &errs.Error{
Code: errs.NotFound,
Message: "user not found",
}
}
if err != nil {
return nil, err
}
return &u, nil
}
```
## Guidelines
- Always use parameterized queries (`$1`, `$2`, etc.)
- Use `Scan` to read query results - columns are mapped by position
- Check for `sqldb.ErrNoRows` when expecting a single row
- Migrations are applied automatically on startup
- Database names should be lowercase, descriptive
- Each service typically has its own database
- Use transactions for operations that must be atomic
© encoredev · Apache-2.0 · encoredev/skills