Skip to content

Java SDK

The FraiseQL Java SDK is a schema authoring SDK: you define GraphQL types using annotations and register queries and mutations using a fluent builder API, and the FraiseQL compiler generates an optimized GraphQL API backed by your SQL views.

<dependency>
<groupId>com.fraiseql</groupId>
<artifactId>fraiseql-java</artifactId>
<version>2.1.0</version>
</dependency>
dependencies {
implementation 'com.fraiseql:fraiseql-java:2.1.0'
}

Requirements: Java 17+

FraiseQL Java uses annotations for type definitions and a fluent builder API for queries and mutations:

ConstructGraphQL EquivalentPurpose
@GraphQLTypetypeMark a class as a GraphQL output type
@GraphQLInputinputMark a class as a GraphQL input type
@GraphQLFieldfield metadataConfigure field names, nullability, scopes
FraiseQL.query(name)...Query fieldWire a query to a SQL view
FraiseQL.mutation(name)...Mutation fieldDefine a mutation

Annotate classes with @GraphQLType and fields with @GraphQLField. The type attribute on @GraphQLField maps to FraiseQL scalar types:

schema/User.java
package schema;
import com.fraiseql.core.*;
@GraphQLType
public class User {
@GraphQLField(type = "ID", nullable = false)
public String id;
@GraphQLField(nullable = false)
public String username;
@GraphQLField(type = "Email", nullable = false)
public String email;
@GraphQLField(nullable = true)
public String bio;
@GraphQLField(type = "DateTime", nullable = false)
public String createdAt;
}
schema/Post.java
package schema;
import com.fraiseql.core.*;
@GraphQLType
public class Post {
@GraphQLField(type = "ID", nullable = false)
public String id;
@GraphQLField(nullable = false)
public String title;
@GraphQLField(type = "Slug", nullable = false)
public String slug;
@GraphQLField(nullable = false)
public String content;
@GraphQLField(nullable = false)
public boolean isPublished;
@GraphQLField(type = "DateTime", nullable = false)
public String createdAt;
@GraphQLField(type = "DateTime", nullable = false)
public String updatedAt;
}

Scalar type strings are provided as constants in com.fraiseql.core.Scalars:

import com.fraiseql.core.Scalars;
// Use as string literals in @GraphQLField(type = ...)
Scalars.ID // "ID"
Scalars.EMAIL // "Email"
Scalars.SLUG // "Slug"
Scalars.DATE_TIME // "DateTime"
Scalars.URL // "URL"
Scalars.DATE // "Date"
Scalars.UUID // "UUID"
Scalars.DECIMAL // "Decimal"
Scalars.JSON // "Json"

Use requiresScope or requiresScopes on @GraphQLField for field-level access control:

@GraphQLType
public class User {
@GraphQLField(type = "ID", nullable = false)
public String id;
@GraphQLField(
type = "Email",
nullable = false,
requiresScope = "read:user.email",
description = "Email address — requires read:user.email scope"
)
public String email;
@GraphQLField(
type = Scalars.DECIMAL,
nullable = true,
requiresScopes = {"read:user.salary", "admin"},
description = "Salary — requires both read:user.salary and admin scopes"
)
public String salary;
}

Use @RequiresRole on a type or field to restrict access by role:

import com.fraiseql.core.RequiresRole;
@GraphQLType
@RequiresRole("admin")
public class AdminDashboard {
@GraphQLField(nullable = false)
public int totalUsers;
@GraphQLField(nullable = false)
public String systemStatus;
}

Annotate input classes with @GraphQLInput and fields with @GraphQLField:

schema/CreatePostInput.java
package schema;
import com.fraiseql.core.*;
@GraphQLInput
public class CreatePostInput {
@GraphQLField(nullable = false)
public String title;
@GraphQLField(nullable = false)
public String content;
@GraphQLField(nullable = false)
public boolean isPublished;
}
schema/UpdatePostInput.java
package schema;
import com.fraiseql.core.*;
@GraphQLInput
public class UpdatePostInput {
@GraphQLField(type = "ID", nullable = false)
public String id;
@GraphQLField(nullable = true)
public String title;
@GraphQLField(nullable = true)
public String content;
@GraphQLField(nullable = true)
public Boolean isPublished;
}

Use FraiseQL.query(name) to create a QueryBuilder, chain configuration methods, and call .register() to add the query to the schema:

schema/SchemaSetup.java
package schema;
import com.fraiseql.core.FraiseQL;
public class SchemaSetup {
public static void register() {
// Register types
FraiseQL.registerTypes(User.class, Post.class);
FraiseQL.registerTypes(CreatePostInput.class, UpdatePostInput.class);
// List query — maps to v_post view
FraiseQL.query("posts")
.returnType(Post.class)
.returnsArray(true)
.sqlSource("v_post")
.arg("isPublished", "Boolean")
.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.query("post")
.returnType(Post.class)
.sqlSource("v_post")
.arg("id", "ID")
.description("Fetch a single post by ID.")
.register();
}
}

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


Use FraiseQL.mutation(name) to create a MutationBuilder. Each mutation maps to a PostgreSQL function using the fn_ naming convention:

schema/SchemaSetup.java
package schema;
import com.fraiseql.core.FraiseQL;
import java.util.List;
public class SchemaSetup {
public static void register() {
// ... type and query registration above ...
// Maps to fn_create_post PostgreSQL function
FraiseQL.mutation("createPost")
.returnType(Post.class)
.sqlSource("fn_create_post")
.arg("input", "CreatePostInput")
.operation("insert")
.invalidatesViews(List.of("v_post"))
.description("Create a new blog post.")
.register();
// Maps to fn_publish_post PostgreSQL function
FraiseQL.mutation("publishPost")
.returnType(Post.class)
.sqlSource("fn_publish_post")
.arg("id", "ID")
.operation("update")
.invalidatesViews(List.of("v_post"))
.description("Publish a draft post.")
.register();
// Maps to fn_delete_post PostgreSQL function
FraiseQL.mutation("deletePost")
.returnType(Post.class)
.sqlSource("fn_delete_post")
.arg("id", "ID")
.operation("delete")
.invalidatesViews(List.of("v_post"))
.description("Soft-delete a post.")
.register();
}
}

Mutations wire to PostgreSQL functions returning mutation_response. Views return (id UUID, data JSONB). See SQL Patterns for complete table, view, and function templates.


Transport annotations are optional. Omit them to serve an operation via GraphQL only. Use @FraiseQLQuery and @FraiseQLMutation method-level annotations with restPath and restMethod attributes. gRPC endpoints are auto-generated when [grpc] is enabled — no per-operation annotation needed. See gRPC Transport.

import io.fraiseql.sdk.*;
@FraiseQLQuery(
sqlSource = "v_post",
restPath = "/posts",
restMethod = "GET"
)
public List<Post> posts(@Default("10") int limit) { return null; }
@FraiseQLQuery(
sqlSource = "v_post",
restPath = "/posts/{id}",
restMethod = "GET"
)
public Post post(UUID id) { return null; }
@FraiseQLMutation(
sqlSource = "create_post",
operation = "CREATE",
restPath = "/posts",
restMethod = "POST"
)
public Post createPost(String title, UUID authorId) { return 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.


Use .inject(map) on a query or mutation builder to inject server-side values from the JWT without exposing them as client-controlled arguments:

import java.util.Map;
// Automatically inject the authenticated user's ID from the JWT sub claim
FraiseQL.query("myPosts")
.returnType(Post.class)
.returnsArray(true)
.sqlSource("v_post")
.arg("limit", "Int", 20)
.inject(Map.of("author_id", "jwt:sub"))
.description("Fetch the current user's posts.")
.register();

  1. Register all types and operations in your application startup:

    src/main/java/Main.java
    public class Main {
    public static void main(String[] args) throws Exception {
    SchemaSetup.register();
    FraiseQL.exportSchema("schema.json");
    System.out.println("Schema exported to schema.json");
    }
    }
  2. Compile with the CLI:

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

    Terminal window
    fraiseql run