Adapters
Adapters
Section titled “Adapters”Adapters bridge the gap between scaf’s code generation and specific database drivers. Each adapter knows how to generate code that executes queries using a particular database library.
Architecture
Section titled “Architecture”flowchart LR
subgraph CodeGen["Code Generation"]
scaf[".scaf file"]
lang["Language (Go)"]
adapter["Adapter (neogo)"]
end
subgraph Generated["Generated Code"]
code["scaf.go"]
driver["Database Driver"]
end
scaf --> lang
lang --> adapter
adapter --> code
code --> driver
The flow:
- Language (e.g., Go) handles the overall code structure
- Adapter (e.g., neogo) generates database-specific function bodies
- Generated code uses the database driver directly—no scaf runtime dependency
Supported Adapters
Section titled “Supported Adapters”| Adapter | Database | Driver | Language | Status |
|---|---|---|---|---|
neogo | Neo4j | neogo | Go | ✅ Available |
pgx | PostgreSQL | pgx | Go | 🚧 Planned |
mysql | MySQL | go-sql-driver | Go | 🚧 Planned |
sqlite | SQLite | modernc.org/sqlite | Go | 🚧 Planned |
neogo Adapter
Section titled “neogo Adapter”The neogo adapter generates Go code that uses the neogo driver for Neo4j.
Configuration
Section titled “Configuration”neo4j: uri: bolt://localhost:7687 username: neo4j password: password
generate: lang: go adapter: neogo # Inferred from neo4j + go if not specified out: ./queries package: queriesGenerated Code Pattern
Section titled “Generated Code Pattern”Given this scaf query:
fn GetUser `MATCH (u:User {id: $userId})RETURN u.name AS name, u.email AS email`The neogo adapter generates:
func GetUser(ctx context.Context, db neogo.Driver, userId string) (name string, email string, err error) { err = db.Exec(). Cypher(`MATCH (u:User {id: $userId}) RETURN u.name AS name, u.email AS email`). RunWithParams(ctx, map[string]any{"userId": userId}, "name", &name, "email", &email) if err != nil { return "", "", err } return name, email, nil}Features
Section titled “Features”- Standalone functions — No receiver type, functions are package-level
- Context-aware — First parameter is always
context.Context - Driver injection — Second parameter is
neogo.Driverfor flexibility - Error handling — All functions return
erroras the last value - Parameter mapping — Query
$paramsbecome function parameters - Return binding — Query
RETURNfields become return values
package main
import ( "context" "log"
"github.com/rlch/neogo" "myproject/queries")
func main() { ctx := context.Background()
db, err := neogo.New(ctx, "bolt://localhost:7687", neogo.WithAuth("neo4j", "password")) if err != nil { log.Fatal(err) } defer db.Close(ctx)
name, email, err := queries.GetUser(ctx, db, "user-123") if err != nil { log.Fatal(err) }
log.Printf("User: %s <%s>", name, email)}pgx Adapter (Planned)
Section titled “pgx Adapter (Planned)”The pgx adapter will generate Go code using pgx for PostgreSQL.
Planned Generated Code
Section titled “Planned Generated Code”func GetUser(ctx context.Context, pool *pgxpool.Pool, userId int) (name string, email string, err error) { row := pool.QueryRow(ctx, `SELECT name, email FROM users WHERE id = $1`, userId) err = row.Scan(&name, &email) if err != nil { return "", "", err } return name, email, nil}Adapter Selection
Section titled “Adapter Selection”Automatic Inference
Section titled “Automatic Inference”If adapter is not specified in config, scaf infers it from the database and language:
| Database | Language | Default Adapter |
|---|---|---|
neo4j | go | neogo |
postgres | go | pgx |
mysql | go | mysql |
sqlite | go | sqlite |
Manual Override
Section titled “Manual Override”generate: adapter: neogo # Explicitly set adapterOr via CLI:
scaf generate --adapter=neogoCreating Custom Adapters
Section titled “Creating Custom Adapters”Adapters implement the golang.Binding interface:
type Binding interface { // Name returns the adapter identifier Name() string
// Imports returns required import paths Imports() []string
// ReceiverType returns method receiver type (empty for functions) ReceiverType() string
// PrependParams returns params added before query params PrependParams() []BindingParam
// ReturnsError returns true if functions return error ReturnsError() bool
// GenerateBody generates the function body GenerateBody(ctx *BodyContext) (string, error)}Register your adapter:
package myadapter
import "github.com/rlch/scaf/language/go"
func init() { golang.RegisterBinding(&MyBinding{})}Import in your generate command:
import _ "myproject/adapters/myadapter"