C# SDK
The FraiseQL C# SDK is a schema authoring SDK: you define GraphQL types using .NET attributes and the FraiseQL compiler generates an optimized GraphQL API backed by your SQL views.
Installation
Section titled “Installation”dotnet add package FraiseQLRequirements: .NET 8+
Core Concepts
Section titled “Core Concepts”FraiseQL C# uses .NET attributes for type definitions and a fluent builder API for queries and mutations:
| Construct | GraphQL Equivalent | Purpose |
|---|---|---|
[GraphQLType] | type | Mark a class as a GraphQL output type |
[GraphQLType(IsInput = true)] | input | Mark a class as a GraphQL input type |
[GraphQLField] | field metadata | Configure field types, nullability, scopes |
SchemaRegistry.Query(name)... | Query field | Wire a query to a SQL view |
SchemaRegistry.Mutation(name)... | Mutation field | Define a mutation |
Defining Types
Section titled “Defining Types”Annotate classes with [GraphQLType] and properties with [GraphQLField]. The Type parameter accepts GraphQL scalar names; when omitted the .NET type is auto-detected:
using FraiseQL.Attributes;
[GraphQLType(SqlSource = "v_user")]public class User{ [GraphQLField(Type = "ID", Nullable = false)] public string Id { get; set; } = "";
[GraphQLField(Nullable = false)] public string Username { get; set; } = "";
[GraphQLField(Type = "Email", Nullable = false)] public string Email { get; set; } = "";
[GraphQLField(Nullable = true)] public string? Bio { get; set; }
[GraphQLField(Type = "DateTime", Nullable = false)] public DateTimeOffset CreatedAt { get; set; }}using FraiseQL.Attributes;
[GraphQLType(SqlSource = "v_post")]public class Post{ [GraphQLField(Type = "ID", Nullable = false)] public string Id { get; set; } = "";
[GraphQLField(Nullable = false)] public string Title { get; set; } = "";
[GraphQLField(Type = "Slug", Nullable = false)] public string Slug { get; set; } = "";
[GraphQLField(Nullable = false)] public string Content { get; set; } = "";
[GraphQLField(Nullable = false)] public bool IsPublished { get; set; }
[GraphQLField(Type = "DateTime", Nullable = false)] public DateTimeOffset CreatedAt { get; set; }}Defining Inputs
Section titled “Defining Inputs”Input types use [GraphQLType(IsInput = true)]:
using FraiseQL.Attributes;
[GraphQLType(IsInput = true)]public class CreatePostInput{ [GraphQLField(Nullable = false)] public string Title { get; set; } = "";
[GraphQLField(Nullable = false)] public string Content { get; set; } = "";
[GraphQLField(Nullable = false)] public bool IsPublished { get; set; }}Defining Queries
Section titled “Defining Queries”Use SchemaRegistry.RegisterQuery in your application startup to wire queries to SQL views:
using FraiseQL;using FraiseQL.Registry;
public static class SchemaSetup{ public static void Register() { // Register types first SchemaRegistry.Register(typeof(User), typeof(Post), typeof(CreatePostInput));
// List query — maps to v_post view SchemaRegistry.RegisterQuery("posts") .SqlSource("v_post") .ReturnType<Post>() .ReturnsList(true) .Arg("isPublished", "Boolean", nullable: true) .Arg("limit", "Int", defaultValue: 20) .Arg("offset", "Int", defaultValue: 0) .Description("Fetch posts, optionally filtered by published status.") .Register();
// Single-item query SchemaRegistry.RegisterQuery("post") .SqlSource("v_post") .ReturnType<Post>() .Nullable(true) .Arg("id", "ID") .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”// Maps to fn_create_post PostgreSQL functionSchemaRegistry.RegisterMutation("createPost") .SqlSource("fn_create_post") .ReturnType<Post>() .Arg("input", "CreatePostInput") .Operation("insert") .InvalidatesViews(["v_post"]) .Description("Create a new blog post.") .Register();
// Maps to fn_delete_post PostgreSQL functionSchemaRegistry.RegisterMutation("deletePost") .SqlSource("fn_delete_post") .ReturnType<Post>() .Arg("id", "ID") .RequiresRole("admin") .Operation("delete") .InvalidatesViews(["v_post"]) .Description("Soft-delete a post.") .Register();The 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 .RequiresRole(role) on query or mutation builders to restrict access:
SchemaRegistry.RegisterQuery("myPosts") .SqlSource("v_post") .ReturnType<Post>() .ReturnsList(true) .RequiresRole("member") .InjectParams(new Dictionary<string, string> { ["authorId"] = "jwt:sub" }) .Register();Field-level scope restrictions use [GraphQLField(Scope = "...")]:
[GraphQLField(Type = "Email", Nullable = false, Scope = "user:read:email")]public string Email { get; set; } = "";Transport Annotations
Section titled “Transport Annotations”Transport annotations are optional. Omit them to serve an operation via GraphQL only. Extend [FraiseQLQuery] and [FraiseQLMutation] with RestPath and RestMethod named parameters. gRPC endpoints are auto-generated when [grpc] is enabled — no per-operation annotation needed. See gRPC Transport.
using FraiseQL.SDK;
// REST + GraphQL[FraiseQLQuery(SqlSource = "v_post", RestPath = "/posts", RestMethod = "GET")]public List<Post> Posts(int limit = 10) => null;
// With path parameter[FraiseQLQuery(SqlSource = "v_post", RestPath = "/posts/{id}", RestMethod = "GET")]public Post Post(Guid id) => null;
// REST mutation[FraiseQLMutation(SqlSource = "create_post", Operation = "CREATE", RestPath = "/posts", RestMethod = "POST")]public Post CreatePost(string title, Guid authorId) => null;Path parameters in RestPath (e.g., {id}) must match method parameter names 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”-
Call
SchemaSetup.Register()in your export entry point:ExportSchema/Program.cs using FraiseQL.Registry;SchemaSetup.Register();await SchemaRegistry.ExportAsync("schema.json"); -
Run the export tool:
Terminal window dotnet run --project ExportSchema -
Compile with the FraiseQL CLI:
Terminal window fraiseql compile --schema schema.json -
Serve the API:
Terminal window fraiseql run
HTTP Client
Section titled “HTTP Client”FraiseQLClient is a sealed, IDisposable async HTTP client for consuming FraiseQL (or any GraphQL) APIs from .NET applications:
using FraiseQL;
var options = new FraiseQLClientOptions{ Endpoint = new Uri("https://api.example.com/graphql"), Authorization = "Bearer your-token"};
using var client = new FraiseQLClient(options);
// Queryvar users = await client.QueryAsync<List<User>>(""" query { users { id username email } } """);
// Mutationvar post = await client.MutateAsync<Post>(""" mutation($input: CreatePostInput!) { createPost(input: $input) { id title } } """, new { input = new { title = "Hello", content = "World", isPublished = true } });Retry Configuration
Section titled “Retry Configuration”var options = new FraiseQLClientOptions{ Endpoint = new Uri("https://api.example.com/graphql"), Retry = new RetryOptions { MaxAttempts = 3, BaseDelay = TimeSpan.FromMilliseconds(200), MaxDelay = TimeSpan.FromSeconds(5), Jitter = true }};Exception Hierarchy
Section titled “Exception Hierarchy”| Exception | Trigger |
|---|---|
FraiseQLException | Base class for all SDK exceptions |
GraphQLException | One or more GraphQL errors in the response (exposes Errors list) |
AuthenticationException | HTTP 401 or 403 |
RateLimitException | HTTP 429 |
FraiseQLTimeoutException | Request exceeded timeout |
NetworkException | Connection or DNS failure |
Factory-Based Authorization
Section titled “Factory-Based Authorization”For dynamic tokens (e.g. refresh flows), pass a factory instead of a static string:
var options = new FraiseQLClientOptions{ Endpoint = new Uri("https://api.example.com/graphql"), AuthorizationFactory = async () => $"Bearer {await GetFreshTokenAsync()}"};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