Go SDK
The FraiseQL Go SDK is a schema authoring SDK: you define GraphQL types, queries, and mutations in Go using struct tags and fluent builder patterns, and the FraiseQL compiler generates an optimized GraphQL API backed by your SQL views.
Installation
Section titled “Installation”go get github.com/fraiseql/fraiseql-goRequirements: Go 1.22+
Core Concepts
Section titled “Core Concepts”FraiseQL Go provides struct tags and fluent builders that map your Go types to GraphQL schema constructs:
| Construct | GraphQL Equivalent | Purpose |
|---|---|---|
Go struct + fraiseql: tags | type | Define a GraphQL output type |
fraiseql.RegisterTypes(...) | type registration | Register struct types with the schema |
fraiseql.NewQuery(name)... | Query field | Wire a query to a SQL view |
fraiseql.NewMutation(name)... | Mutation field | Define a mutation |
Defining Types
Section titled “Defining Types”Define Go structs with fraiseql: struct tags. Fields map 1:1 to keys in your backing SQL view’s .data JSONB object. Register the types with fraiseql.RegisterTypes.
package schema
import "github.com/fraiseql/fraiseql-go/fraiseql"
type User struct { ID fraiseql.ID `fraiseql:"id,type=ID"` Username string `fraiseql:"username"` Email fraiseql.Email `fraiseql:"email,type=Email"` Bio *string `fraiseql:"bio,nullable=true"` CreatedAt fraiseql.DateTime `fraiseql:"created_at,type=DateTime"`}
type Post struct { ID fraiseql.ID `fraiseql:"id,type=ID"` Title string `fraiseql:"title"` Slug fraiseql.Slug `fraiseql:"slug,type=Slug"` Content string `fraiseql:"content"` IsPublished bool `fraiseql:"is_published"` CreatedAt fraiseql.DateTime `fraiseql:"created_at,type=DateTime"` UpdatedAt fraiseql.DateTime `fraiseql:"updated_at,type=DateTime"`}
func init() { fraiseql.RegisterTypes(User{}, Post{})}Built-in Scalars
Section titled “Built-in Scalars”FraiseQL provides semantic scalar types. Use the type= tag option to emit the correct GraphQL scalar type in schema.json:
import "github.com/fraiseql/fraiseql-go/fraiseql"
// Corefraiseql.ID // UUID — use `fraiseql:"id,type=ID"`fraiseql.Email // Validated email — use `fraiseql:"email,type=Email"`fraiseql.Slug // URL-safe slug — use `fraiseql:"slug,type=Slug"`fraiseql.DateTime // ISO 8601 datetime — use `fraiseql:"created_at,type=DateTime"`fraiseql.URL // Validated URL — use `fraiseql:"website,type=URL"`The scalar types are Go type aliases with no runtime behavior. Validation and serialization happen in the FraiseQL Rust runtime after compilation.
Defining Inputs
Section titled “Defining Inputs”Input types are plain Go structs registered with fraiseql.RegisterTypes. Declare them before use so the compiler knows their shape:
package schema
import "github.com/fraiseql/fraiseql-go/fraiseql"
type CreatePostInput struct { Title string `fraiseql:"title"` Content string `fraiseql:"content"` IsPublished bool `fraiseql:"is_published"`}
type UpdatePostInput struct { ID fraiseql.ID `fraiseql:"id,type=ID"` Title *string `fraiseql:"title,nullable=true"` Content *string `fraiseql:"content,nullable=true"` IsPublished *bool `fraiseql:"is_published,nullable=true"`}
func init() { fraiseql.RegisterTypes(CreatePostInput{}, UpdatePostInput{})}Defining Queries
Section titled “Defining Queries”Use fraiseql.NewQuery to build query definitions and wire them to SQL views. Call .Register() to add each query to the global schema registry:
package schema
import "github.com/fraiseql/fraiseql-go/fraiseql"
func init() { // List query — maps to v_post view fraiseql.NewQuery("posts"). SqlSource("v_post"). ReturnType(Post{}). ReturnsArray(true). Arg("is_published", "Boolean", nil, true). Arg("limit", "Int", 20). Arg("offset", "Int", 0). Description("Fetch posts, optionally filtered by published status."). Register()
// Single-item query — maps to v_post with id filter fraiseql.NewQuery("post"). SqlSource("v_post"). ReturnType(Post{}). Nullable(true). Arg("id", "ID", nil). Description("Fetch a single post by ID."). Register()}How Arguments Become WHERE Clauses
Section titled “How Arguments Become WHERE Clauses”Query arguments matching columns in the backing view become SQL WHERE clauses automatically. See SQL Patterns → Automatic WHERE Clauses for details.
Defining Mutations
Section titled “Defining Mutations”Use fraiseql.NewMutation to build mutation definitions. Each mutation maps to a PostgreSQL function using the fn_ naming convention:
package schema
import "github.com/fraiseql/fraiseql-go/fraiseql"
func init() { // Maps to fn_create_post PostgreSQL function fraiseql.NewMutation("create_post"). SqlSource("fn_create_post"). ReturnType(Post{}). Arg("input", "CreatePostInput", nil). Operation("insert"). InvalidatesViews([]string{"v_post"}). Description("Create a new blog post."). Register()
// Maps to fn_publish_post PostgreSQL function fraiseql.NewMutation("publish_post"). SqlSource("fn_publish_post"). ReturnType(Post{}). Arg("id", "ID", nil). RequiresRole("editor"). Operation("update"). InvalidatesViews([]string{"v_post"}). Description("Publish a draft post."). Register()
// Maps to fn_delete_post PostgreSQL function fraiseql.NewMutation("delete_post"). SqlSource("fn_delete_post"). ReturnType(Post{}). Arg("id", "ID", nil). RequiresRole("admin"). Operation("delete"). InvalidatesViews([]string{"v_post"}). Description("Soft-delete a post."). Register()}Each mutation maps to a PostgreSQL function in db/schema/. The Go definition is the schema; the SQL function is the implementation.
The SQL Side
Section titled “The SQL Side”Mutations wire to PostgreSQL functions returning mutation_response. Views return (id UUID, data JSONB). See SQL Patterns for complete table, view, and function templates.
Authorization
Section titled “Authorization”Use .RequiresRole(role) on any query or mutation builder to restrict access to callers who hold the specified role:
func init() { // Restrict query to users with the "member" role fraiseql.NewQuery("my_posts"). SqlSource("v_post"). ReturnType(Post{}). ReturnsArray(true). RequiresRole("member"). InjectParams(map[string]string{"author_id": "jwt:sub"}). Register()
// Restrict mutation to users with the "editor" role fraiseql.NewMutation("create_post"). SqlSource("fn_create_post"). ReturnType(Post{}). Arg("input", "CreatePostInput", nil). RequiresRole("editor"). Register()}JWT claim injection via InjectParams lets you pass server-side values (like the authenticated user’s ID) into queries without exposing them as client-controlled arguments.
Complete example
Section titled “Complete example”For a complete blog API schema combining all the patterns above, see the fraiseql-starter-blog repository.
Transport Annotations
Section titled “Transport Annotations”Transport annotations are optional. Omit them to serve an operation via GraphQL only. Chain .RESTPath(), .RESTMethod(), .GRPCService(), and .GRPCMethod() on any query or mutation builder.
import "github.com/fraiseql/fraiseql-go"
// REST + GraphQLfraiseql.NewQuery("posts"). SqlSource("v_post"). RESTPath("/posts"). RESTMethod("GET"). Build()
// With path parameterfraiseql.NewQuery("post"). SqlSource("v_post"). RESTPath("/posts/{id}"). // {id} matches the id parameter RESTMethod("GET"). Build()
// gRPCfraiseql.NewQuery("postsGRPC"). SqlSource("v_post"). GRPCService("BlogService"). GRPCMethod("ListPosts"). Build()
// All three transportsfraiseql.NewQuery("posts"). SqlSource("v_post"). RESTPath("/posts"). RESTMethod("GET"). GRPCService("BlogService"). GRPCMethod("ListPosts"). Build()
// REST mutationfraiseql.NewMutation("createPost"). SqlSource("create_post"). Operation("CREATE"). RESTPath("/posts"). RESTMethod("POST"). GRPCService("BlogService"). GRPCMethod("CreatePost"). Build()Path parameters in RESTPath (e.g., {id}) must match the corresponding argument name registered on the query. A mismatch produces a compile-time error. Duplicate (method, path) pairs are also rejected at compile time.
Build and Export
Section titled “Build and Export”-
Export the schema — compiles Go types to the FraiseQL schema JSON:
cmd/export/main.go package mainimport ("encoding/json""os"_ "your-module/schema" // side-effect: runs init() functions"github.com/fraiseql/fraiseql-go/fraiseql")func main() {data, err := fraiseql.GetSchemaJSON(true)if err != nil {panic(err)}os.WriteFile("schema.json", data, 0644)} -
Compile with the CLI:
Terminal window fraiseql compile --schema schema.json -
Serve the API:
Terminal window fraiseql run
Next Steps
Section titled “Next Steps”- SDK Overview — how compile-time authoring works
- SQL Patterns — view and function conventions
- Your First API — full tutorial
- All SDKs — compare languages