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.
Installation
Section titled “Installation”Add to mix.exs:
defp deps do [ {:fraiseql, "~> 2.0"} ]endThen:
mix deps.getCore Concepts
Section titled “Core Concepts”FraiseQL Elixir provides compile-time macros that map Elixir module definitions to GraphQL schema constructs:
| Macro | GraphQL Equivalent | Purpose |
|---|---|---|
fraiseql_type/2 | type | Define a GraphQL output type with fields |
fraiseql_query/2 | Query field | Wire a query to a SQL view |
fraiseql_mutation/2 | Mutation field | Define a mutation backed by a SQL function |
field/3 | field declaration | Declare a field inside a type block |
argument/3 | argument declaration | Declare an argument inside a query/mutation block |
Defining a Schema Module
Section titled “Defining a Schema Module”All schema definitions live in a module that uses FraiseQL.Schema:
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 endendBuilt-in Scalar Atoms
Section titled “Built-in Scalar Atoms”FraiseQL maps Elixir atoms to GraphQL scalars:
| Atom | GraphQL Scalar |
|---|---|
:id | ID (UUID) |
:string | String |
:integer | Int |
:float | Float |
:boolean | Boolean |
:email | Email |
:slug | Slug |
:datetime | DateTime |
:url | URL |
Defining Inputs
Section titled “Defining Inputs”Input types use input: true:
fraiseql_type "CreatePostInput", input: true do field :title, :string, nullable: false field :content, :string, nullable: false field :is_published, :boolean, nullable: falseendDefining Queries
Section titled “Defining Queries”# List query — maps to v_post SQL viewfraiseql_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: 0end
# Single-item queryfraiseql_query :post, return_type: "Post", sql_source: "v_post", nullable: true do argument :id, :id, nullable: falseendHow 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”# Maps to fn_create_post PostgreSQL functionfraiseql_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: falseend
# Role-restricted mutationfraiseql_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: falseendThe SQL Side
Section titled “The SQL Side”Mutations wire to PostgreSQL functions returning mutation_response. See SQL Patterns for complete function templates.
Authorization
Section titled “Authorization”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: 20endField-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"endTransport Annotations
Section titled “Transport Annotations”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 endendPath 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.
Build and Export
Section titled “Build and Export”-
Export the schema — converts your module to
schema.json:Terminal window mix fraiseql.export --module MyApp.Schema --output schema.jsonOr from an IEx session:
MyApp.Schema.export_to_file!("schema.json") -
Compile with the FraiseQL 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