### Implement LoggingResolverInterceptor Source: https://context7.com/leangen/graphql-spqr/llms.txt Add logging to resolvers by implementing the ResolverInterceptor interface. This example logs resolution time and errors. ```java import io.leangen.graphql.execution.ResolverInterceptor; import io.leangen.graphql.execution.InvocationContext; public class LoggingInterceptor implements ResolverInterceptor { @Override public Object aroundInvoke(InvocationContext context, Continuation continuation) throws Exception { long start = System.currentTimeMillis(); try { Object result = continuation.proceed(context); log.info("Resolved {} in {}ms", context.getResolver().getOperationName(), System.currentTimeMillis() - start); return result; } catch (Exception e) { log.error("Error resolving {}: {}", context.getResolver().getOperationName(), e.getMessage()); throw e; } } } ``` -------------------------------- ### Basic Schema Generation and Execution Source: https://context7.com/leangen/graphql-spqr/llms.txt Demonstrates the minimal setup for generating a GraphQL schema from service classes and executing a query. Ensure `com.example` is the root package for type scanning. ```java import io.leangen.graphql.GraphQLSchemaGenerator; import graphql.GraphQL; import graphql.schema.GraphQLSchema; // Basic schema generation UserService userService = new UserService(); ProductService productService = new ProductService(); GraphQLSchema schema = new GraphQLSchemaGenerator() .withBasePackages("com.example") // Recommended: set root packages for type scanning .withOperationsFromSingleton(userService) // Register a service instance .withOperationsFromSingleton(productService) // Register multiple services .generate(); // Create GraphQL executor GraphQL graphQL = new GraphQL.Builder(schema).build(); // Execute queries ExecutionResult result = graphQL.execute("{ user(id: 123) { name, email } }"); Map data = result.getData(); ``` -------------------------------- ### Java Link Type Example Source: https://github.com/leangen/graphql-spqr/wiki/1.-Home Corresponding Java class for the Link type, demonstrating the structure that GraphQL SPQR can generate a schema from. ```java public class Link { private final String id; private final String url; private final String description; //constructors, getters and setters //... } ``` -------------------------------- ### GraphQL Schema Definition Source: https://github.com/leangen/graphql-spqr/wiki/1.-Home Example of a GraphQL schema definition for a Link type. ```graphql type Link { id: ID! url: String! description: String } ``` -------------------------------- ### Map Ambiguous Member Type with Explicit Type - Original Example Source: https://github.com/leangen/graphql-spqr/wiki/5.-Common-errors-and-solutions Illustrates a scenario where a TypeMappingException occurs due to missing generic type parameters in a class definition. ```java public class ItemHolder { public T[] getItems() {...} public List getItemNames() {...} } GraphQLSchema schema = new GraphQLSchemaGenerator() .withOperationsFromSingleton(new ItemHolder()) //no explicit singleton type provided .generate(); ``` -------------------------------- ### Generate GraphQL Schema and Execute Query Source: https://github.com/leangen/graphql-spqr/wiki/2.-Getting-Started Instantiate a service, generate a GraphQL schema using GraphQLSchemaGenerator, build a GraphQL instance, and execute a query. ```java UserService userService = new UserService(); //instantiate the service (or inject by Spring or another framework) GraphQLSchema schema = new GraphQLSchemaGenerator() .withBasePackages("io.leangen") //not mandatory but strongly recommended to set your "root" packages .withOperationsFromSingleton(userService) //register the service .generate(); //done ;) GraphQL graphQL = new GraphQL.Builder(schema) .build(); //keep the reference to GraphQL instance and execute queries against it. //this operation selects a user by ID and requests name, regDate and twitterProfile fields only ExecutionResult result = graphQL.execute( "{ user (id: 123) { name, regDate }}"); ``` -------------------------------- ### Registering Singleton Services Source: https://context7.com/leangen/graphql-spqr/llms.txt Shows how to register stateless service instances for GraphQL exposure. Type inference is used for basic registration, while explicit types are needed for generic or Spring-proxied beans. ```java import io.leangen.geantyref.TypeToken; // Basic registration - type inferred from instance GraphQLSchema schema = new GraphQLSchemaGenerator() .withOperationsFromSingleton(new UserService()) .generate(); // Explicit type for generic services (required for proper type mapping) GraphQLSchema schema = new GraphQLSchemaGenerator() .withOperationsFromSingleton( new GenericService(), new TypeToken>(){}.getAnnotatedType() ) .generate(); // For Spring-proxied beans, provide the actual type explicitly @Autowired UserService userServiceBean; GraphQLSchema schema = new GraphQLSchemaGenerator() .withOperationsFromSingleton(userServiceBean, UserService.class) .generate(); ``` -------------------------------- ### Registering Services with Suppliers Source: https://context7.com/leangen/graphql-spqr/llms.txt Illustrates registering services using supplier functions, suitable for beans with non-singleton scopes like request-scoped beans in Spring. Explicit runtime types can be provided for proxied beans. ```java // Using supplier for request-scoped beans GraphQLSchema schema = new GraphQLSchemaGenerator() .withOperationsFromBean( () -> applicationContext.getBean(SessionService.class), SessionService.class ) .generate(); // With explicit runtime type for proxied beans GraphQLSchema schema = new GraphQLSchemaGenerator() .withOperationsFromBean( () -> springContext.getBean("userService"), UserService.class, UserServiceImpl.class // Actual runtime class ) .generate(); ``` -------------------------------- ### Implement Relay-Style Pagination Source: https://context7.com/leangen/graphql-spqr/llms.txt Use PageFactory to create offset-based pages for Relay-style cursor-based pagination. Requires `first` and `after` arguments. ```java import io.leangen.graphql.execution.relay.Page; import io.leangen.graphql.execution.relay.generic.PageFactory; public class ProductService { @GraphQLQuery(name = "products") public Page getProducts( @GraphQLArgument(name = "first") int first, @GraphQLArgument(name = "after") String after, @GraphQLArgument(name = "category") String category ) { long offset = after != null ? Long.parseLong(after) : 0; List products = productRepository.findByCategory(category, first, offset); long totalCount = productRepository.countByCategory(category); return PageFactory.createOffsetBasedPage(products, totalCount, offset); } } ``` ```graphql { products(first: 10, after: "20", category: "electronics") { pageInfo { hasNextPage hasPreviousPage startCursor endCursor } edges { cursor node { id name price } } } } ``` -------------------------------- ### Register Generic Beans with TypeToken or TypeFactory Source: https://github.com/leangen/graphql-spqr/wiki/5.-Common-errors-and-solutions For generic types (e.g., GenericService), use TypeToken or TypeFactory with withOperationsFromSingleton to provide the full type information explicitly, as generic type information cannot always be extracted via reflection. ```java schemaGenerator.withOperationsFromSingleton(genericServiceBean, new TypeToken>(){}.getType()) ``` -------------------------------- ### Enable Complexity Analysis Instrumentation Source: https://context7.com/leangen/graphql-spqr/llms.txt Instrument the GraphQL instance with ComplexityAnalysisInstrumentation to enforce query complexity limits. Set the maximum allowed complexity. ```java // Enable complexity analysis with max allowed complexity GraphQL graphQL = GraphQL.newGraphQL(schema) .instrumentation(new ComplexityAnalysisInstrumentation(1000)) .build(); ``` -------------------------------- ### Configure BeanResolverBuilder Source: https://context7.com/leangen/graphql-spqr/llms.txt Expose all public getter methods as GraphQL queries. Specify the base package to scan for beans. ```java import io.leangen.graphql.metadata.strategy.query.BeanResolverBuilder; // Expose all public getter methods as queries GraphQLSchema schema = new GraphQLSchemaGenerator() .withResolverBuilders(new BeanResolverBuilder("com.example")) .withOperationsFromSingleton(service) .generate(); ``` -------------------------------- ### User POJO with GraphQL Queries Source: https://github.com/leangen/graphql-spqr/wiki/2.-Getting-Started Define a User POJO with methods annotated with @GraphQLQuery to expose fields like name, id, and registrationDate to the GraphQL schema. ```java public class User { private String name; private Integer id; private Date registrationDate; @GraphQLQuery(name = "name", description = "A person's name") public String getName() { return name; } @GraphQLQuery public Integer getId() { return id; } @GraphQLQuery(name = "regDate", description = "Date of registration") public Date getRegistrationDate() { return registrationDate; } } ``` -------------------------------- ### GraphQL Argument Definition Source: https://context7.com/leangen/graphql-spqr/llms.txt Defines GraphQL arguments with optional name, description, and default value using the @GraphQLArgument annotation. ```APIDOC ## @GraphQLArgument Defines a GraphQL argument with optional name, description, and default value. ### Method N/A (Annotation) ### Endpoint N/A (Annotation) ### Parameters #### Annotation Parameters - **name** (String) - Optional - The name of the argument. - **description** (String) - Optional - A description for the argument. - **defaultValue** (String) - Optional - The default value for the argument. ### Request Example ```java @GraphQLArgument(name = "query", description = "Search query string") String query, @GraphQLArgument(name = "limit", defaultValue = "20") int limit ``` ### Response N/A (Annotation) ``` -------------------------------- ### Configure DefaultTypeTransformer Source: https://context7.com/leangen/graphql-spqr/llms.txt Handle raw types and missing generic parameters by using the DefaultTypeTransformer. Set flags to treat them as Object. ```java import io.leangen.graphql.metadata.strategy.type.DefaultTypeTransformer; // Treat raw types and unresolved type variables as Object GraphQLSchema schema = new GraphQLSchemaGenerator() .withOperationsFromSingleton(legacyService) .withTypeTransformer(new DefaultTypeTransformer(true, true)) .generate(); ``` -------------------------------- ### Define GraphQL Query with Annotations Source: https://github.com/leangen/graphql-spqr/wiki/2.-Getting-Started Use @GraphQLQuery and @GraphQLArgument annotations to define a query operation and its arguments in a Java service. ```java class UserService { @GraphQLQuery(name = "user") public User getById(@GraphQLArgument(name = "id") Integer id) { ... } } ``` -------------------------------- ### GraphQL Subscription API Source: https://context7.com/leangen/graphql-spqr/llms.txt Defines real-time data streams for clients. ```APIDOC ## GraphQL Subscription Operations ### Description Methods annotated with `@GraphQLSubscription` are exposed as subscription operations, enabling real-time data updates to clients. These methods typically return a `Publisher`. ### Method POST (Typically for subscriptions, though WebSocket is common) ### Endpoint / ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **query** (string) - The GraphQL subscription string. - **variables** (object) - Optional variables for the subscription. ### Request Example ```json { "query": "subscription { userNotifications(userId: 123) { id, message, timestamp } }" } ``` ### Response #### Success Response (200) - **data** (object) - Contains the initial data or events from the subscription stream. - **errors** (array) - Contains any errors encountered. #### Response Example ```json { "data": { "userNotifications": { "id": "notif-1", "message": "Your order has shipped.", "timestamp": "2023-10-27T10:00:00Z" } } } ``` ### Example Usage (Java) ```java @GraphQLSubscription(name = "userNotifications", description = "Stream of user notifications") public Publisher subscribeToNotifications( @GraphQLArgument(name = "userId") Integer userId ) { // ... implementation returning a Publisher ... return Flowable.create(emitter -> { NotificationListener listener = notification -> { if (notification.getUserId().equals(userId)) { emitter.onNext(notification); } }; notificationHub.addListener(listener); emitter.setCancellable(() -> notificationHub.removeListener(listener)); }, BackpressureStrategy.BUFFER); } ``` ``` -------------------------------- ### Configure AnnotatedResolverBuilder Source: https://context7.com/leangen/graphql-spqr/llms.txt Expose only methods annotated with @GraphQLQuery or @GraphQLMutation. This is the default behavior. ```java import io.leangen.graphql.metadata.strategy.query.AnnotatedResolverBuilder; // Default: only @GraphQLQuery/@GraphQLMutation annotated methods GraphQLSchema schema = new GraphQLSchemaGenerator() .withResolverBuilders(new AnnotatedResolverBuilder()) .withOperationsFromSingleton(service) .generate(); ``` -------------------------------- ### Define GraphQL Interface with @GraphQLInterface Source: https://context7.com/leangen/graphql-spqr/llms.txt Define a GraphQL interface using @GraphQLInterface. Set implementationAutoDiscovery to true to automatically discover implementing classes within specified packages. ```java import io.leangen.graphql.annotations.types.GraphQLInterface; @GraphQLInterface( name = "Vehicle", description = "A vehicle that can transport passengers", implementationAutoDiscovery = true, scanPackages = {"com.example.vehicles"} ) public interface Vehicle { @GraphQLQuery String getRegistration(); @GraphQLQuery int getPassengerCapacity(); } public class Car implements Vehicle { @Override public String getRegistration() { return registration; } @Override public int getPassengerCapacity() { return seats; } @GraphQLQuery public int getWheelCount() { return 4; } } public class Motorcycle implements Vehicle { @Override public String getRegistration() { return registration; } @Override public int getPassengerCapacity() { return 2; } @GraphQLQuery public boolean hasSidecar() { return sidecar; } } ``` -------------------------------- ### Handle Multiple Resolver Methods with Different Return Types Source: https://github.com/leangen/graphql-spqr/wiki/5.-Common-errors-and-solutions Use TypeInference.LIMITED or TypeInference.UNLIMITED to configure schema generation when an operation has multiple resolver methods with different return types. This avoids TypeMappingException by inferring a common super type. ```java @GraphQLQuery(name = "numbers") public ArrayList getLongs(String paramOne) {...} @GraphQLQuery(name = "numbers") public LinkedList getDoubles(String paramTwo) {...} ``` ```java GraphQLSchemaGenerator#withOperationBuilder(new DefaultOperationBuilder(DefaultOperationBuilder.TypeInference.LIMITED)); ``` -------------------------------- ### Enable Relay-Compliant Mutations Source: https://context7.com/leangen/graphql-spqr/llms.txt Configure GraphQL SPQR to automatically wrap mutations with Relay's input and clientMutationId fields. This simplifies mutation handling for Relay clients. ```java GraphQLSchema schema = new GraphQLSchemaGenerator() .withOperationsFromSingleton(new UserService()) .withRelayCompliantMutations() // Enable Relay mutation format .generate(); ``` ```graphql mutation { createUser(input: { clientMutationId: "abc123", name: "John", email: "john@example.com" }) { clientMutationId result { id name } } } ``` -------------------------------- ### Gradle Dependency for GraphQL SPQR Source: https://github.com/leangen/graphql-spqr/wiki/2.-Getting-Started Add this dependency to your Gradle project to include GraphQL SPQR. ```groovy compile 'io.leangen.graphql:spqr:0.12.3' ``` -------------------------------- ### Configure Resolver Interceptors Source: https://context7.com/leangen/graphql-spqr/llms.txt Apply custom interceptors for cross-cutting concerns like logging or authentication. Multiple interceptors can be chained. ```java GraphQLSchema schema = new GraphQLSchemaGenerator() .withOperationsFromSingleton(service) .withResolverInterceptors(new LoggingInterceptor(), new AuthInterceptor()) .generate(); ``` -------------------------------- ### Register Proxied Beans with Explicit Type Source: https://github.com/leangen/graphql-spqr/wiki/5.-Common-errors-and-solutions When registering proxied beans (e.g., from Spring) with the schema generator, explicitly provide the correct type using the two or three argument version of withOperationsFromSingleton to avoid misleading reflection information. ```java schemaGenerator.withOperationsFromSingleton(bookServiceBean, BookService.class) ``` -------------------------------- ### Configure BeanResolverBuilder for Schema Generation Source: https://github.com/leangen/graphql-spqr/wiki/2.-Getting-Started Configure GraphQL SPQR to use BeanResolverBuilder to register all public methods within a service to the GraphQL schema. ```java GraphQLSchema schema = new GraphQLSchemaGenerator() ... .withResolverBuilders(new BeanResolverBuilder()) ... .generate(); //done ;) ``` -------------------------------- ### Maven Dependency for GraphQL SPQR Source: https://github.com/leangen/graphql-spqr/wiki/2.-Getting-Started Add this dependency to your Maven project to include GraphQL SPQR. ```xml io.leangen.graphql spqr 0.12.4 ``` -------------------------------- ### Implement Custom TypeMapper for User Class Source: https://github.com/leangen/graphql-spqr/wiki/3.-Advanced-Configuration Extend ObjectTypeMapper to add custom fields like 'twitterHandle' to the User GraphQL type. Override supports() to target specific classes and getFields() to define new fields and their DataFetchers. ```java public class CustomTypeMapper extends ObjectTypeMapper { @Override protected List getFields(String typeName, AnnotatedType javaType, TypeMappingEnvironment env) { //Retrieve the fields from the annotated methods by calling the super#getFields() method. List fields = super.getFields(typeName, javaType, env); //Now add a custom field fields.add(GraphQLFieldDefinition.newFieldDefinition() .name("twitterHandle") .type(GraphQLString) .build()); // Append a custom DataFetcher (also known as a Resolver) DataFetcher dataFetcher = e -> { User user = (User) e.getSource(); return getTwitterHandleForUser(user); }; // And register the DataFetcher to the code registry env.buildContext.codeRegistry.dataFetcher(coordinates(typeName, "twitterHandle"), dataFetcher); return fields; } @Override public boolean supports(AnnotatedElement element, AnnotatedType type) { return ClassUtils.getRawType(type).equals(User.class); } } ``` -------------------------------- ### Define GraphQL Queries with @GraphQLQuery Source: https://context7.com/leangen/graphql-spqr/llms.txt Use @GraphQLQuery to expose methods and fields as query operations in your GraphQL schema. Specify names and descriptions for clarity. Default names are derived from method names. ```java import io.leangen.graphql.annotations.GraphQLQuery; import io.leangen.graphql.annotations.GraphQLArgument; public class UserService { @GraphQLQuery(name = "user", description = "Fetch a user by ID") public User getUserById(@GraphQLArgument(name = "id") Integer id) { return userRepository.findById(id); } @GraphQLQuery(name = "users", description = "List all users with pagination") public List getUsers( @GraphQLArgument(name = "limit", defaultValue = "10") int limit, @GraphQLArgument(name = "offset", defaultValue = "0") int offset ) { return userRepository.findAll(limit, offset); } @GraphQLQuery // Name defaults to "activeUsers" from method name public List getActiveUsers() { return userRepository.findByStatus("ACTIVE"); } } // Domain class with field-level queries public class User { @GraphQLQuery(name = "id", description = "User's unique identifier") public Integer id; @GraphQLQuery(name = "fullName", description = "User's full name") public String getName() { return firstName + " " + lastName; } @Deprecated @GraphQLQuery(name = "legacyId", deprecationReason = "Use 'id' field instead") public String getLegacyId() { return "legacy-" + id; } } ``` -------------------------------- ### Define GraphQL Mutations with @GraphQLMutation Source: https://context7.com/leangen/graphql-spqr/llms.txt Annotate methods with @GraphQLMutation to define operations that modify data. You can specify names, descriptions, and arguments for these mutations. ```java import io.leangen.graphql.annotations.GraphQLMutation; public class UserService { @GraphQLMutation(name = "createUser", description = "Create a new user") public User createUser( @GraphQLArgument(name = "name") String name, @GraphQLArgument(name = "email") String email ) { User user = new User(name, email); return userRepository.save(user); } @GraphQLMutation(name = "updateUser") public User updateUser( @GraphQLArgument(name = "id") Integer id, @GraphQLArgument(name = "input") UserUpdateInput input ) { User user = userRepository.findById(id); if (input.getName() != null) user.setName(input.getName()); if (input.getEmail() != null) user.setEmail(input.getEmail()); return userRepository.save(user); } @GraphQLMutation(name = "deleteUser") public boolean deleteUser(@GraphQLArgument(name = "id") Integer id) { return userRepository.deleteById(id); } } ``` ```graphql mutation { createUser(name: "John", email: "john@example.com") { id, name } } ``` -------------------------------- ### Inject Execution Environment into Resolvers Source: https://context7.com/leangen/graphql-spqr/llms.txt Access execution environment details like requested fields, AST field, or the full resolution context within resolver methods using @GraphQLEnvironment. ```java import io.leangen.graphql.annotations.GraphQLEnvironment; import io.leangen.graphql.execution.ResolutionEnvironment; import graphql.language.Field; public class UserService { @GraphQLQuery(name = "user") public User getUser( @GraphQLArgument(name = "id") Integer id, @GraphQLEnvironment Set requestedFields, // Names of requested sub-fields @GraphQLEnvironment Field field, // Current AST field @GraphQLEnvironment ResolutionEnvironment env // Full resolution context ) { // Optimize query based on requested fields if (requestedFields.contains("orders")) { return userRepository.findByIdWithOrders(id); } return userRepository.findById(id); } } ``` -------------------------------- ### GraphQL Query API Source: https://context7.com/leangen/graphql-spqr/llms.txt Defines methods and fields that can be queried in a GraphQL schema. ```APIDOC ## GraphQL Query Operations ### Description Methods annotated with `@GraphQLQuery` are exposed as query operations in the GraphQL schema. Fields within domain classes can also be exposed as query fields. ### Method GET (Implicit for queries) ### Endpoint / ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None (GraphQL queries are typically sent as a JSON payload with a 'query' field). ### Request Example ```json { "query": "{ user(id: 1) { id fullName } }" } ``` ### Response #### Success Response (200) - **data** (object) - Contains the result of the query. - **errors** (array) - Contains any errors encountered during query execution. #### Response Example ```json { "data": { "user": { "id": 1, "fullName": "John Doe" } } } ``` ### Example Usage (Java) ```java // Exposing a method as a query @GraphQLQuery(name = "user", description = "Fetch a user by ID") public User getUserById(@GraphQLArgument(name = "id") Integer id) { return userRepository.findById(id); } // Exposing a field as a query public class User { @GraphQLQuery(name = "id", description = "User's unique identifier") public Integer id; } ``` ``` -------------------------------- ### Configure Gson Value Mapper Factory Source: https://github.com/leangen/graphql-spqr/wiki/3.-Advanced-Configuration Use Gson as the value mapper when both Jackson and Gson are available but Gson is preferred. This requires explicitly setting the `GsonValueMapperFactory`. ```java GraphQLSchema schema = new GraphQLSchemaGenerator() .withBasePackages("io.leangen") .withOperationsFromSingleton(userService) .withValueMapperFactory(new GsonValueMapperFactory()) .generate(); ``` -------------------------------- ### GraphQL Context Annotation Source: https://context7.com/leangen/graphql-spqr/llms.txt Attaches additional fields to an existing GraphQL type without modifying the original class. The annotated parameter receives the parent object. ```APIDOC ## @GraphQLContext Attaches additional fields to an existing GraphQL type without modifying the original class. The annotated parameter receives the parent object. ### Method N/A (Annotation) ### Endpoint N/A (Annotation) ### Parameters #### Annotation Parameters - **N/A** ### Request Example ```java @GraphQLContext User user ``` ### Response N/A (Annotation) ``` -------------------------------- ### GraphQLInterface Annotation Source: https://context7.com/leangen/graphql-spqr/llms.txt The @GraphQLInterface annotation defines a GraphQL interface type, with options for automatic implementation discovery. ```APIDOC ## @GraphQLInterface ### Description Defines a GraphQL interface type with optional automatic implementation discovery. ### Usage Annotate an interface or abstract class with `@GraphQLInterface`. ### Parameters - **name** (String) - The custom name for the GraphQL interface. - **description** (String) - A description for the GraphQL interface. - **implementationAutoDiscovery** (boolean) - If true, automatically discovers implementations. - **scanPackages** (String[]) - Packages to scan for implementations if auto-discovery is enabled. ### Example ```java import io.leangen.graphql.annotations.types.GraphQLInterface; @GraphQLInterface( name = "Vehicle", description = "A vehicle that can transport passengers", implementationAutoDiscovery = true, scanPackages = {"com.example.vehicles"} ) public interface Vehicle { // ... methods ... } ``` ``` -------------------------------- ### Query Extended GraphQL Type Source: https://github.com/leangen/graphql-spqr/wiki/3.-Advanced-Configuration Demonstrates how to query the newly added field (`twitterProfile`) and its nested fields (`handle`, `numberOfTweets`) from a `user` object. ```graphql { user (id: 123) { name, regDate, twitterProfile { handle numberOfTweets } } } ``` -------------------------------- ### Configure GsonValueMapperFactory Source: https://context7.com/leangen/graphql-spqr/llms.txt Use Gson for JSON processing by setting the GsonValueMapperFactory. Ensure Gson is available in your classpath. ```java import io.leangen.graphql.metadata.strategy.value.gson.GsonValueMapperFactory; GraphQLSchema schema = new GraphQLSchemaGenerator() .withOperationsFromSingleton(userService) .withValueMapperFactory(new GsonValueMapperFactory()) .generate(); ``` -------------------------------- ### Define GraphQL Subscriptions with @GraphQLSubscription Source: https://context7.com/leangen/graphql-spqr/llms.txt Use @GraphQLSubscription to mark methods that provide real-time data updates via reactive streams (Publishers). These are suitable for event-driven notifications. ```java import io.leangen.graphql.annotations.GraphQLSubscription; import org.reactivestreams.Publisher; import io.reactivex.rxjava3.core.Flowable; public class NotificationService { @GraphQLSubscription(name = "userNotifications", description = "Stream of user notifications") public Publisher subscribeToNotifications( @GraphQLArgument(name = "userId") Integer userId ) { return Flowable.create(emitter -> { NotificationListener listener = notification -> { if (notification.getUserId().equals(userId)) { emitter.onNext(notification); } }; notificationHub.addListener(listener); emitter.setCancellable(() -> notificationHub.removeListener(listener)); }, BackpressureStrategy.BUFFER); } @GraphQLSubscription(name = "messageStream") public Publisher subscribeToMessages( @GraphQLArgument(name = "channelId") String channelId ) { return messageService.getMessageStream(channelId); } } ``` ```graphql subscription { userNotifications(userId: 123) { id, message, timestamp } } ``` -------------------------------- ### GraphQLType Annotation Source: https://context7.com/leangen/graphql-spqr/llms.txt The @GraphQLType annotation allows customization of GraphQL type names, descriptions, and field order for Java classes. ```APIDOC ## @GraphQLType ### Description Customizes the GraphQL type name and description for a class. ### Usage Annotate a Java class with `@GraphQLType` to control its GraphQL representation. ### Parameters - **name** (String) - The custom name for the GraphQL type. - **description** (String) - A description for the GraphQL type. - **fieldOrder** (String[]) - An array of field names to specify their order in the schema. ### Example ```java import io.leangen.graphql.annotations.types.GraphQLType; @GraphQLType( name = "Customer", description = "A customer in the system", fieldOrder = {"id", "name", "email", "createdAt"} ) public class User { // ... fields and methods ... } ``` ``` -------------------------------- ### Create Extended Page with Total Count Source: https://context7.com/leangen/graphql-spqr/llms.txt Implement a custom Page that includes a `totalCount` field. This is useful for displaying total item counts alongside paginated results. ```java import io.leangen.graphql.execution.relay.Page; import graphql.relay.Edge; import graphql.relay.PageInfo; public class ExtendedPage implements Page { private final List> edges; private final PageInfo pageInfo; private final long totalCount; public ExtendedPage(List> edges, PageInfo pageInfo, long totalCount) { this.edges = edges; this.pageInfo = pageInfo; this.totalCount = totalCount; } @Override public List> getEdges() { return edges; } @Override public PageInfo getPageInfo() { return pageInfo; } @GraphQLQuery(name = "totalCount", description = "Total number of items") public long getTotalCount() { return totalCount; } } ``` ```java // Service using extended page public class UserService { @GraphQLQuery(name = "users") public ExtendedPage getUsers( @GraphQLArgument(name = "first") int first, @GraphQLArgument(name = "after") String after ) { long offset = after != null ? Long.parseLong(after) : 0; List users = userRepository.findAll(first, offset); long total = userRepository.count(); return PageFactory.createOffsetBasedPage( users, total, offset, (edges, info) -> new ExtendedPage<>(edges, info, total) ); } } ``` -------------------------------- ### Define GraphQL Query Complexity Source: https://context7.com/leangen/graphql-spqr/llms.txt Use the @GraphQLComplexity annotation to define complexity expressions for queries. These expressions can reference query arguments. ```java import io.leangen.graphql.annotations.GraphQLComplexity; public class UserService { @GraphQLQuery(name = "users") @GraphQLComplexity("2 * childScore") // Complexity expression public List getUsers(@GraphQLArgument(name = "limit") int limit) { return userRepository.findAll(limit); } @GraphQLQuery(name = "expensiveOperation") @GraphQLComplexity("100 + 5 * first") // Can reference arguments public List generateReports(@GraphQLArgument(name = "first") int first) { return reportService.generate(first); } } ``` -------------------------------- ### Define GraphQL Arguments with @GraphQLArgument Source: https://context7.com/leangen/graphql-spqr/llms.txt Use @GraphQLArgument to define argument names, descriptions, and default values for GraphQL queries and mutations. Default values can be strings, numbers, or JSON objects. ```java import io.leangen.graphql.annotations.GraphQLArgument; public class SearchService { @GraphQLQuery(name = "search") public List search( @GraphQLArgument(name = "query", description = "Search query string") String query, @GraphQLArgument(name = "limit", defaultValue = "20") int limit, @GraphQLArgument(name = "offset", defaultValue = "0") int offset, @GraphQLArgument(name = "sortBy", defaultValue = "\"relevance\"") String sortBy, @GraphQLArgument(name = "filters", defaultValue = "{}") SearchFilters filters ) { return searchEngine.search(query, limit, offset, sortBy, filters); } // Complex default value as JSON @GraphQLQuery(name = "findProducts") public List findProducts( @GraphQLArgument( name = "options", defaultValue = "{\"includeOutOfStock\": false, \"minRating\": 3.0}" ) ProductSearchOptions options ) { return productService.find(options); } } ``` -------------------------------- ### Extend Existing GraphQL Type with @GraphQLContext Source: https://github.com/leangen/graphql-spqr/wiki/3.-Advanced-Configuration Use the `@GraphQLContext` annotation to add fields from another type (e.g., `TwitterProfile`) to an existing GraphQL type (e.g., `User`). The method annotated with `@GraphQLContext` receives the parent object as an argument. ```java class UserService { //existing queries public TwitterProfile getTwitterProfile(@GraphQLContext User user) { ... } } ``` -------------------------------- ### Define a New GraphQL Type Source: https://github.com/leangen/graphql-spqr/wiki/3.-Advanced-Configuration Define a new type `TwitterProfile` with its own queries. This type can later be associated with other domain types. ```java class TwitterProfile { @GraphQLQuery public String getHandle() { ... } @GraphQLQuery public int getNumberOfTweets() { ... } } ``` -------------------------------- ### GraphQL Mutation API Source: https://context7.com/leangen/graphql-spqr/llms.txt Defines operations that modify data in the GraphQL schema. ```APIDOC ## GraphQL Mutation Operations ### Description Methods annotated with `@GraphQLMutation` are exposed as mutation operations in the GraphQL schema, allowing clients to modify data. ### Method POST (Typically for mutations) ### Endpoint / ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **query** (string) - The GraphQL mutation string. - **variables** (object) - Optional variables for the mutation. ### Request Example ```json { "query": "mutation { createUser(name: \"Jane Doe\", email: \"jane@example.com\") { id, name } }" } ``` ### Response #### Success Response (200) - **data** (object) - Contains the result of the mutation. - **errors** (array) - Contains any errors encountered during mutation execution. #### Response Example ```json { "data": { "createUser": { "id": 2, "name": "Jane Doe" } } } ``` ### Example Usage (Java) ```java @GraphQLMutation(name = "createUser", description = "Create a new user") public User createUser( @GraphQLArgument(name = "name") String name, @GraphQLArgument(name = "email") String email ) { User user = new User(name, email); return userRepository.save(user); } ``` ``` -------------------------------- ### Customize GraphQL Type Name and Description with @GraphQLType Source: https://context7.com/leangen/graphql-spqr/llms.txt Use @GraphQLType to specify the GraphQL type name, description, and field order for a Java class. This annotation is applied directly to the class definition. ```java import io.leangen.graphql.annotations.types.GraphQLType; @GraphQLType( name = "Customer", description = "A customer in the system", fieldOrder = {"id", "name", "email", "createdAt"} ) public class User { private Integer id; private String name; private String email; private Date createdAt; @GraphQLQuery public Integer getId() { return id; } @GraphQLQuery public String getName() { return name; } @GraphQLQuery public String getEmail() { return email; } @GraphQLQuery public Date getCreatedAt() { return createdAt; } } ``` -------------------------------- ### GraphQLInputField Annotation Source: https://context7.com/leangen/graphql-spqr/llms.txt The @GraphQLInputField annotation customizes input field mapping for GraphQL input types. ```APIDOC ## @GraphQLInputField ### Description Customizes input field mapping for input types. ### Usage Annotate fields within an input class with `@GraphQLInputField`. ### Parameters - **name** (String) - The custom name for the input field. - **description** (String) - A description for the input field. - **defaultValue** (String) - The default value for the input field (as a JSON string). ### Example ```java import io.leangen.graphql.annotations.GraphQLInputField; public class CreateUserInput { @GraphQLInputField(name = "username", description = "Unique username") private String username; @GraphQLInputField(name = "email", description = "Email address") private String email; @GraphQLInputField(name = "role", defaultValue = "\"USER\"") private String role; @GraphQLInputField(name = "settings", defaultValue = "{\"notifications\": true}") private UserSettings settings; // Getters and setters } // Usage in mutation public class UserService { @GraphQLMutation(name = "createUser") public User createUser(@GraphQLArgument(name = "input") CreateUserInput input) { // ... implementation ... } } ``` ``` -------------------------------- ### Integrate Custom TypeMapper into Schema Generation Source: https://github.com/leangen/graphql-spqr/wiki/3.-Advanced-Configuration Register your custom TypeMapper with the GraphQLSchemaGenerator using the withTypeMappers() method to apply your customizations during schema creation. ```java GraphQLSchema schema = new GraphQLSchemaGenerator() .withBasePackages("io.leangen") //not mandatory but strongly recommended to set your "root" packages .withOperationsFromSingleton(userService) //register the service .withTypeMappers(new CustomTypeMapper()) .generate(); //done ;) ``` -------------------------------- ### Create Custom Type Mapper Source: https://context7.com/leangen/graphql-spqr/llms.txt Define a custom type mapper to control how Java types are mapped to GraphQL types. This allows adding computed fields and custom data fetchers. ```java import io.leangen.graphql.generator.mapping.common.ObjectTypeMapper; import graphql.schema.GraphQLFieldDefinition; public class CustomTypeMapper extends ObjectTypeMapper { @Override protected List getFields( String typeName, AnnotatedType javaType, TypeMappingEnvironment env ) { List fields = super.getFields(typeName, javaType, env); // Add custom computed field fields.add(GraphQLFieldDefinition.newFieldDefinition() .name("computedField") .type(GraphQLString) .build()); // Register data fetcher for custom field DataFetcher fetcher = e -> computeValue(e.getSource()); env.buildContext.codeRegistry.dataFetcher( FieldCoordinates.coordinates(typeName, "computedField"), fetcher ); return fields; } @Override public boolean supports(AnnotatedElement element, AnnotatedType type) { return ClassUtils.getRawType(type.getType()).equals(MySpecialClass.class); } } ``` ```java // Register custom mapper GraphQLSchema schema = new GraphQLSchemaGenerator() .withOperationsFromSingleton(service) .withTypeMappers(new CustomTypeMapper()) .generate(); ``` -------------------------------- ### Define GraphQL Union Type with @GraphQLUnion Source: https://context7.com/leangen/graphql-spqr/llms.txt Define a GraphQL union type using @GraphQLUnion. This can be applied to a method return type to specify possible types, or to an interface with auto-discovery enabled. ```java import io.leangen.graphql.annotations.types.GraphQLUnion; // Union defined on method return type public class SearchService { @GraphQLQuery(name = "search") public @GraphQLUnion(name = "SearchResult", possibleTypes = {User.class, Product.class, Article.class}) Object search(@GraphQLArgument(name = "query") String query) { return searchEngine.search(query); } } // Union with auto-discovery @GraphQLUnion( name = "Pet", possibleTypeAutoDiscovery = true, scanPackages = {"com.example.animals"} ) public interface Pet { String getName(); } public class Cat implements Pet { public String getName() { return name; } public int getLives() { return 9; } } public class Dog implements Pet { public String getName() { return name; } public String getBreed() { return breed; } } ``` -------------------------------- ### GraphQL Non-Null Type Source: https://context7.com/leangen/graphql-spqr/llms.txt Marks a type as non-nullable in the GraphQL schema. ```APIDOC ## @GraphQLNonNull Marks a type as non-nullable in the GraphQL schema. ### Method N/A (Annotation) ### Endpoint N/A (Annotation) ### Parameters #### Annotation Parameters - **N/A** ### Request Example ```java @GraphQLNonNull Product getProduct(@GraphQLArgument(name = "id") String id) ``` ### Response N/A (Annotation) ``` -------------------------------- ### GraphQL ID Type Source: https://context7.com/leangen/graphql-spqr/llms.txt Marks a field as a GraphQL ID type. Optionally enables Relay global ID support. ```APIDOC ## @GraphQLId Marks a field as a GraphQL ID type. Optionally enables Relay global ID support. ### Method N/A (Annotation) ### Endpoint N/A (Annotation) ### Parameters #### Annotation Parameters - **relayId** (Boolean) - Optional - If true, enables Relay global ID support. ### Request Example ```java @GraphQLId public String getId() { return id; } @GraphQLId(relayId = true) String getIsbn() { return isbn; } ``` ### Response N/A (Annotation) ``` -------------------------------- ### Transform Missing Type Arguments with DefaultTypeTransformer Source: https://github.com/leangen/graphql-spqr/wiki/5.-Common-errors-and-solutions To handle missing generic type arguments for fields or methods, use a TypeTransformer to replace them with Object or apply custom logic. ```java schemaGenerator.withTypeTransformer(new DefaultTypeTransformer(true, true)) ``` -------------------------------- ### Customize Input Field Mapping with @GraphQLInputField Source: https://context7.com/leangen/graphql-spqr/llms.txt Use @GraphQLInputField to customize input field names, descriptions, and default values for input types. This annotation is applied to class fields. ```java import io.leangen.graphql.annotations.GraphQLInputField; public class CreateUserInput { @GraphQLInputField(name = "username", description = "Unique username") private String username; @GraphQLInputField(name = "email", description = "Email address") private String email; @GraphQLInputField(name = "role", defaultValue = "\"USER\"") private String role; @GraphQLInputField(name = "settings", defaultValue = "{\"notifications\": true}") private UserSettings settings; // Getters and setters } // Usage in mutation public class UserService { @GraphQLMutation(name = "createUser") public User createUser(@GraphQLArgument(name = "input") CreateUserInput input) { return userService.create(input); } } ``` -------------------------------- ### GraphQLUnion Annotation Source: https://context7.com/leangen/graphql-spqr/llms.txt The @GraphQLUnion annotation defines a GraphQL union type, allowing explicit specification of possible types or auto-discovery. ```APIDOC ## @GraphQLUnion ### Description Defines a GraphQL union type with explicit possible types or auto-discovery. ### Usage Annotate a method return type or an interface/class with `@GraphQLUnion`. ### Parameters - **name** (String) - The custom name for the GraphQL union. - **possibleTypes** (Class[]) - An array of classes that are possible types of the union. - **possibleTypeAutoDiscovery** (boolean) - If true, automatically discovers possible types. - **scanPackages** (String[]) - Packages to scan for possible types if auto-discovery is enabled. ### Example (on method return type) ```java import io.leangen.graphql.annotations.types.GraphQLUnion; public class SearchService { @GraphQLQuery(name = "search") public @GraphQLUnion(name = "SearchResult", possibleTypes = {User.class, Product.class, Article.class}) Object search(@GraphQLArgument(name = "query") String query) { // ... implementation ... } } ``` ### Example (with auto-discovery) ```java import io.leangen.graphql.annotations.types.GraphQLUnion; @GraphQLUnion( name = "Pet", possibleTypeAutoDiscovery = true, scanPackages = {"com.example.animals"} ) public interface Pet { // ... methods ... } ``` ``` -------------------------------- ### Attach Additional Field to GraphQL Type Source: https://github.com/leangen/graphql-spqr/blob/master/README.md Attach a new field 'twitterProfile' to the User GraphQL type by defining a query in the service class that uses `@GraphQLContext` to access the User object. ```java class UserService { ... //regular queries, as above // Attach a new field called twitterProfile to the User GraphQL type @GraphQLQuery public TwitterProfile twitterProfile(@GraphQLContext User user) { ... } } ``` -------------------------------- ### Add Fields to Existing Types with @GraphQLContext Source: https://context7.com/leangen/graphql-spqr/llms.txt Use @GraphQLContext to add fields to existing GraphQL types by annotating parameters that receive the parent object. This allows extending types without modifying their original classes. ```java public class UserService { @GraphQLQuery(name = "user") public User getUser(@GraphQLArgument(name = "id") Integer id) { return userRepository.findById(id); } // Adds 'twitterProfile' field to User type @GraphQLQuery(name = "twitterProfile") public TwitterProfile getTwitterProfile(@GraphQLContext User user) { return twitterService.getProfileForUser(user.getId()); } // Adds 'friends' field to User type with arguments @GraphQLQuery(name = "friends") public List getFriends( @GraphQLContext User user, @GraphQLArgument(name = "limit", defaultValue = "10") int limit ) { return friendshipService.getFriends(user.getId(), limit); } // Adds 'orderCount' field to User type @GraphQLQuery(name = "orderCount") public int getOrderCount(@GraphQLContext User user) { return orderService.countByUserId(user.getId()); } } ``` ```graphql String query = """ { user(id: 123) { name email twitterProfile { handle, followers } friends(limit: 5) { name } orderCount } } """ ``` -------------------------------- ### Handle Ambiguous Member Type with Explicit Type - TypeToken Source: https://github.com/leangen/graphql-spqr/wiki/5.-Common-errors-and-solutions When a generic type's type parameters are lost due to type erasure, provide the type explicitly using TypeToken. ```java schemaGenerator.withOperationsFromSingleton(new ItemHolder(), new TypeToken>(){}.getType()) ``` -------------------------------- ### GraphQL Ignore Annotation Source: https://context7.com/leangen/graphql-spqr/llms.txt Excludes a method or field from the GraphQL schema. ```APIDOC ## @GraphQLIgnore Excludes a method or field from the GraphQL schema. ### Method N/A (Annotation) ### Endpoint N/A (Annotation) ### Parameters #### Annotation Parameters - **reason** (String) - Optional - A reason for ignoring the field/method. ### Request Example ```java @GraphQLIgnore public String getPasswordHash() { return passwordHash; } ``` ### Response N/A (Annotation) ``` -------------------------------- ### Mark Fields as GraphQL ID with @GraphQLId Source: https://context7.com/leangen/graphql-spqr/llms.txt Use @GraphQLId to map a field to the GraphQL ID scalar type. Optionally, set relayId = true to enable Relay global ID support, which encodes the type and ID. ```java import io.leangen.graphql.annotations.GraphQLId; public class User { @GraphQLId // Maps to GraphQL ID scalar @GraphQLQuery(name = "id") public String getId() { return id; } } // With Relay global ID (base64 encoded type:id) public class Book { @GraphQLQuery(name = "id") public @GraphQLId(relayId = true) String getIsbn() { return isbn; } } // Resolver accepting Relay ID public class BookService { @GraphQLQuery(name = "book") public Book getBook(@GraphQLId(relayId = true) String isbn) { return bookRepository.findByIsbn(isbn); } } // Relay global ID in queries: "Qm9vazoxMjM=" decodes to "Book:123" ``` -------------------------------- ### Handle Ambiguous Member Type with Explicit Type - TypeFactory Source: https://github.com/leangen/graphql-spqr/wiki/5.-Common-errors-and-solutions Dynamically construct the parameterized type for generic classes when explicit type provision is needed. ```java schemaGenerator.withOperationsFromSingleton(new ItemHolder(), TypeFactory.parameterizedClass(ItemHolder.class, Book.class)) ``` -------------------------------- ### Handle Ambiguous Member Type with AnnotatedType and TypeToken Source: https://github.com/leangen/graphql-spqr/wiki/5.-Common-errors-and-solutions When generic type arguments require annotations, use AnnotatedType constructed with TypeToken. Ensure declaration at the top level due to a JDK8 bug. ```java private static final AnnotatedType beanType = new TypeToken>(){}.getAnnotatedType(); schemaGenerator.withOperationsFromSingleton(new ItemHolder(), beanType) ```