Skip to content

Elixir SDK

The FraiseQL Elixir SDK is a schema authoring SDK: you define GraphQL types, queries, and mutations using compile-time Elixir macros, and the FraiseQL compiler generates an optimized GraphQL API backed by your SQL views.

Add to mix.exs:

mix.exs
defp deps do
[
{:fraiseql, "~> 2.0"}
]
end

Then:

Terminal window
mix deps.get

FraiseQL Elixir provides compile-time macros that map Elixir module definitions to GraphQL schema constructs:

MacroGraphQL EquivalentPurpose
fraiseql_type/2typeDefine a GraphQL output type with fields
fraiseql_query/2Query fieldWire a query to a SQL view
fraiseql_mutation/2Mutation fieldDefine a mutation backed by a SQL function
field/3field declarationDeclare a field inside a type block
argument/3argument declarationDeclare an argument inside a query/mutation block

All schema definitions live in a module that uses FraiseQL.Schema:

lib/my_app/schema.ex
defmodule MyApp.Schema do
use FraiseQL.Schema
fraiseql_type "User", sql_source: "v_user", description: "A registered user" do
field :id, :id, nullable: false
field :username, :string, nullable: false
field :email, :email, nullable: false
field :bio, :string, nullable: true
field :created_at, :datetime, nullable: false
end
fraiseql_type "Post", sql_source: "v_post" do
field :id, :id, nullable: false
field :title, :string, nullable: false
field :slug, :slug, nullable: false
field :content, :string, nullable: false
field :is_published, :boolean, nullable: false
field :created_at, :datetime, nullable: false
end
end

FraiseQL maps Elixir atoms to GraphQL scalars:

AtomGraphQL Scalar
:idID (UUID)
:stringString
:integerInt
:floatFloat
:booleanBoolean
:emailEmail
:slugSlug
:datetimeDateTime
:urlURL

Input types use input: true:

lib/my_app/schema.ex
fraiseql_type "CreatePostInput", input: true do
field :title, :string, nullable: false
field :content, :string, nullable: false
field :is_published, :boolean, nullable: false
end

lib/my_app/schema.ex
# List query — maps to v_post SQL view
fraiseql_query :posts,
return_type: "Post",
returns_list: true,
sql_source: "v_post",
description: "Fetch posts, optionally filtered by published status" do
argument :is_published, :boolean, nullable: true
argument :limit, :integer, default: 20
argument :offset, :integer, default: 0
end
# Single-item query
fraiseql_query :post,
return_type: "Post",
sql_source: "v_post",
nullable: true do
argument :id, :id, nullable: false
end

Query arguments matching columns in the backing view become SQL WHERE clauses automatically. See SQL Patterns → Automatic WHERE Clauses for details.


lib/my_app/schema.ex
# Maps to fn_create_post PostgreSQL function
fraiseql_mutation :create_post,
return_type: "Post",
sql_source: "fn_create_post",
operation: "insert",
invalidates_views: ["v_post"],
description: "Create a new blog post" do
argument :input, "CreatePostInput", nullable: false
end
# Role-restricted mutation
fraiseql_mutation :delete_post,
return_type: "Post",
sql_source: "fn_delete_post",
operation: "delete",
requires_role: "admin",
invalidates_views: ["v_post"] do
argument :id, :id, nullable: false
end

Mutations wire to PostgreSQL functions returning mutation_response. See SQL Patterns for complete function templates.


Use requires_role: on query or mutation definitions:

fraiseql_query :my_posts,
return_type: "Post",
sql_source: "v_post",
returns_list: true,
requires_role: "member",
inject_params: %{author_id: "jwt:sub"} do
argument :limit, :integer, default: 20
end

Field-level scope restrictions use scope: in field/3:

fraiseql_type "User", sql_source: "v_user" do
field :id, :id, nullable: false
field :email, :email, nullable: false, scope: "user:read:email"
end

Transport annotations are optional. Omit them to serve an operation via GraphQL only. Pass rest_path: and rest_method: as keyword options on fraiseql_query and fraiseql_mutation. gRPC endpoints are auto-generated when [grpc] is enabled — no per-operation annotation needed. See gRPC Transport.

defmodule MyApp.Schema do
use FraiseQL.SDK
query :posts,
sql_source: "v_post",
rest_path: "/posts",
rest_method: "GET" do
arg :limit, :integer, default: 10
returns list_of(:post)
end
query :post,
sql_source: "v_post",
rest_path: "/posts/{id}",
rest_method: "GET" do
arg :id, :uuid
returns :post
end
mutation :create_post,
sql_source: "create_post",
operation: :create,
rest_path: "/posts",
rest_method: "POST" do
arg :title, :string
arg :author_id, :uuid
returns :post
end
end

Path parameters in rest_path (e.g., {id}) must match argument names declared in the block exactly. A mismatch produces a compile-time error. Duplicate (method, path) pairs are also rejected at compile time.


  1. Export the schema — converts your module to schema.json:

    Terminal window
    mix fraiseql.export --module MyApp.Schema --output schema.json

    Or from an IEx session:

    MyApp.Schema.export_to_file!("schema.json")
  2. Compile with the FraiseQL CLI:

    Terminal window
    fraiseql compile --schema schema.json
  3. Serve the API:

    Terminal window
    fraiseql run