{"id":18336544,"url":"https://github.com/kumuluz/kumuluzee-graphql","last_synced_at":"2025-04-06T04:35:38.037Z","repository":{"id":54739798,"uuid":"128820020","full_name":"kumuluz/kumuluzee-graphql","owner":"kumuluz","description":"KumuluzEE GraphQL project for enabling GraphQL support for KumuluzEE microservices.","archived":false,"fork":false,"pushed_at":"2024-06-21T15:06:55.000Z","size":598,"stargazers_count":3,"open_issues_count":1,"forks_count":2,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-03-21T17:23:57.656Z","etag":null,"topics":["cloud-native","ee4j","graphql","java","javaee","kumuluzee","microprofile","microservices"],"latest_commit_sha":null,"homepage":"https://ee.kumuluz.com/","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kumuluz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-04-09T18:58:47.000Z","updated_at":"2021-02-09T14:14:12.000Z","dependencies_parsed_at":"2024-01-15T06:07:30.318Z","dependency_job_id":null,"html_url":"https://github.com/kumuluz/kumuluzee-graphql","commit_stats":{"total_commits":67,"total_committers":6,"mean_commits":"11.166666666666666","dds":0.5223880597014925,"last_synced_commit":"fa97a41f7c6043ddf1bac74b9d96055d09a1ef4f"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kumuluz%2Fkumuluzee-graphql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kumuluz%2Fkumuluzee-graphql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kumuluz%2Fkumuluzee-graphql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kumuluz%2Fkumuluzee-graphql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kumuluz","download_url":"https://codeload.github.com/kumuluz/kumuluzee-graphql/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247435041,"owners_count":20938530,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloud-native","ee4j","graphql","java","javaee","kumuluzee","microprofile","microservices"],"created_at":"2024-11-05T20:08:11.834Z","updated_at":"2025-04-06T04:35:36.956Z","avatar_url":"https://github.com/kumuluz.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# KumuluzEE GraphQL\n\n![KumuluzEE CI](https://github.com/kumuluz/kumuluzee-graphql/workflows/KumuluzEE%20CI/badge.svg)\n\n\u003e Kick-start your GraphQL server development.\n\nKumuluzEE GraphQL project enables you to easily create your own GraphQL server with a few simple annotations and is\nfully compliant with [MicroProfile GraphQL Sepcification](https://github.com/eclipse/microprofile-graphql). Using \nthis extension requires understanding of the basic GraphQL concepts.\n\nRead about GraphQL: [GraphQL](http://graphql.org/learn/).\n\nThis project is built upon the [SmallRye GraphQL implementation](https://github.com/smallrye/smallrye-graphql). \n\n\u003e **For 1.0.x users, see the following README: [kumuluzee-graphql](https://github.com/kumuluz/kumuluzee-graphql/tree/master/core)**\n\u003e\n\u003e For new users, using MicroProfile based implementation (this README) is recommended.\n\n## Usage\n\nYou can enable KumuluzEE GraphQL by adding the following dependency to the project:\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.kumuluz.ee.graphql\u003c/groupId\u003e\n    \u003cartifactId\u003ekumuluzee-graphql-mp\u003c/artifactId\u003e\n    \u003cversion\u003e${kumuluzee-graphql.version}\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nWhen KumuluzEE GraphQL is included in the project, you can start developing your GraphQL services.\n\n### Registering GraphQL Resource \n\nThe `@GraphQLApi` annotation must be used on the classes that define GraphQL related functions (queries, mutations, etc.).\nAll GraphQL annotated functions in annotated classes will be added to your GraphQL schema.\n\n```java\n@GraphQLApi\npublic class CustomerResource {...}\n```\n\n### Defining GraphQL queries \nThe `@Query` annotation will register your Java function as a Query function in GraphQL. All types and \nparameters will be automatically converted to GraphQL types and added to the schema. You can override the query name\n(which defaults to the function name without `get` or `set` prefix) or add a description to the query.\n\n```java\n@GraphQLApi\npublic class HelloWorld {\n\n    @Query(\"helloWorld\")\n    public String hello() {\n        return \"Hello world!\";\n    }\n    \n    @Query\n    @Name(\"greet\")\n    @Description(\"Greets person.\")\n    public String sayHello(String person) {\n        return \"Hello \" + person + \"!\";\n    }\n}\n```\n\n### Defining GraphQL mutations\nThe `@Mutation` annotation is used for defining mutations. It is used the same way as `@Query` annotation. \nThe only difference is, that mutations are used for changing persistent state, while queries only retrieve data. \n\nMore information on this can be found in GraphQL documentation: [Queries and mutations](http://graphql.org/learn/queries/).\n\n```java\n@GraphQLApi\npublic class CustomerResource {\n\n    // ...\n\n    @Mutation\n    public Customer saveCustomer(Customer customer) {\n        return customerService.save(customer);\n    }\n    \n    @Mutation\n    @Name(\"saveOrder\")\n    @Description(\"Saves the order to the database.\")\n    public String newOrder(Order order) {\n        return orderService.save(order);\n    }\n}\n```\n\n### Annotating GraphQL arguments\n\nThe name of GraphQL argument (on query or mutation) can be overridden with `@Name` annotation. It can also be marked as\nnon-nullable with `@NonNull` annotation or assigned a default value with `@DefaultValue` annotation.\n\n```java\n@Query\npublic Integer getCustomerCount(@Name(\"onlyRegistered\") Boolean registered) {\n    return customerService.getCustomerCount(registered);\n}\n```\n\n\u003e Avoid using primitive types as parameters (int, double...), because they cannot be `null`. If you use them, please provide their default values with `@DefaultValue` annotation.\n\n### Annotation `@Ignore`\n\nThis annotation can be used to ignore a certain field.\n```java\npublic class Customer {\n    @Ignore\n    private String address;\n}\n```\n\n### Annotation `@NonNull`\n\nIf you want to mark a parameter as required, you can annotate the type with `@NonNull` annotation.\nIt can be also used on lists: \n\n```java\n// non null list of non null students\n@NonNull List\u003c@NonNull Student\u003e\n\n@Mutation\npublic String someMutation(@NonNull String field) {\n  return field;\n} \n```\n\n### Annotation `@Source`\n\nThe `@Source` annotation can be used to define a resolver function for additional fields. The example below adds a\nnew field `referrer` (of type `String`) on the `Customer` type:\n\n```java\n@GraphQLApi\npublic class CustomerResource {\n    \n    @Name(\"referrer\")\n    public String getReferrerForCustomer(@Source Customer customer) {\n        return refererApi.getReferer(customer);\n    }   \n}\n```\n\nThe `@Source` annotation can also be used to resolve fields in batches. This is commonly referred to as the dataloader\npattern and is used to solve the N+1 problem. The following example would generate exactly the same schema as the example\nabove. The only difference is that in the example above the method is called once for every customer returned and in the\nfollowing example the method is called once for all customers that are returned.\n\n```java\n@GraphQLApi\npublic class CustomerResource {\n    \n    @Name(\"referrer\")\n    public List\u003cString\u003e getReferrerForCustomer(@Source List\u003cCustomer\u003e customers) {\n        return refererApi.getReferersForMultipleCustomers(customers);\n    }   \n}\n```\n\nAnother use of the `@Source` annotation is defining nested queries on types. For example:\n\n```java\n@GraphQLApi\npublic class CustomerResource {\n    \n    @Name(\"paidOrders\")\n    public List\u003cOrder\u003e getPaidOrders(@Source Customer customer) {\n        return customer.getOrders().stream()\n                    .filter(o -\u003e o.isPaid()).collect(Collectors.toList());\n    }   \n}\n```\n\nNested queries can also be batched (dataloader pattern). This will generate the same schema (and functionality) as the\nexample above:\n\n```java\n@GraphQLApi\npublic class CustomerResource {\n    \n    @Name(\"paidOrders\")\n    public List\u003cList\u003cOrder\u003e\u003e getPaidOrders(@Source List\u003cCustomer\u003e customers) {\n        return customers.stream.map(c -\u003e c.getOrders().stream()\n                        .filter(o -\u003e o.isPaid()).collect(Collectors.toList()))\n                    .collect(Collectors.toList());\n    }   \n}\n```\n\n### Error handling\n\nExceptions can be thrown during query/mutation execution. The response will have the structure of the GraphQL error as\ndefined in the GraphQL specification.\n\nBy default, all messages from unchecked exceptions (except some defaults, see below) will be hidden for security reasons. You can override this behavior\nwith the configuration key `kumuluzee.graphql.exceptions.show-error-message`. The message will be replaced with\n`Server Error` and can be set using the configuration key `kumuluzee.graphql.exceptions.default-error-message`.\nBy default, all messages from checked exceptions will be shown. You can hide messages from exceptions with the\nconfiguration key `kumuluzee.graphql.exceptions.hide-error-message`. Example configuration:\n\n```yaml\nkumuluzee:\n  graphql:\n    exceptions:\n      hide-error-message:\n        - com.example.exceptions.HiddenCheckedException\n      show-error-message:\n        - com.example.exceptions.ShownRuntimeException\n      default-error-message: Server error, for more information contact ustomer service.\n```\n\n#### `show-error-message` defaults\n\nTo provide a more seamless integration with __kumuluzee-rest__, some exceptions are added to `show-error-message`\nlist by default, namely:\n\n- com.kumuluz.ee.rest.exceptions.InvalidEntityFieldException\n- com.kumuluz.ee.rest.exceptions.InvalidFieldValueException\n- com.kumuluz.ee.rest.exceptions.NoGenericTypeException\n- com.kumuluz.ee.rest.exceptions.NoSuchEntityFieldException\n- com.kumuluz.ee.rest.exceptions.QueryFormatException\n\nTo disable these defaults and handle everything manually use the following configuration:\n\n```yaml\nkumuluzee:\n  graphql:\n    exceptions:\n      include-show-error-defaults: false\n```\n\n## Querying GraphQL endpoint\n\nGraphQL endpoint (`/graphql`) should be queried using a POST request. Request body should be a JSON object containing\nfield `query` with the query that should be excecuted and optionally a field `variables` containing a map of GraphQL\nvariables. For example:\n```json\nHTTP POST localhost:8080/graphql\nHeader: Content-Type: application/json\nPost data: \n{\n\t\"query\": \"{customers {id, name, orders {id, total}}}\",\n\t\"variables\": {\"myVariable\": \"someValue\"}\n}\n```\n\n### Querying GraphQL schema\n\nGraphQL schema generated from annotations can be accessed by sending a GET request on `/graphql/schema.graphql`\nendpoint. By default, some elements from the schema are omitted for readability. Additional information can be added to\nschema by setting the following configuration keys to `true`:\n\n```yaml\nkumuluzee:\n  graphql:\n    schema:\n      include-scalars: true\n      include-schema-definition: true\n      include-directives: true\n      include-introspection-types: true\n```\n\n### GraphQL endpoint mapping\n\nGraphQL server and schema will be served on `/graphql/` by default. You can change this with the KumuluzEE configuration\nframework by setting the following key:\n\n```yaml\nkumuluzee:\n  graphql:\n    mapping: customers-api\n```\n\n## Annotation scanning for GraphQL schema generation\n\nBy default KumuluzEE GraphQL uses optimized scanning in order to reduce startup times. This means that only the main\napplication JAR will be scanned (main artifact). In order to scan additional artifacts you need to specify them using\nthe [scan-libraries mechanism](https://github.com/kumuluz/kumuluzee/pull/123). You need to include all dependencies\nwhich contain GraphQL resources (annotated with `@GraphQLApi`) as well as dependencies containing models returned from\nGraphQL resources.\nIf all your models and resources are in the main artifact you don't need to include anything. For example to include\n_my-models_ artifact use the following configuration:\n\n```yaml\nkumuluzee:\n  dev:\n    scan-libraries:\n      - my-models\n```\n\nIf you are not sure if your configuration is correct you can try disabling scanning optimization. This will scan all\ndependencies but will drastically increase application startup time. Having this optimization disabled in production is not\nrecommended. Disable optimized scanning by using the following configuration:\n\n```yaml\nkumuluzee:\n  graphql:\n    scanning:\n      optimize: false\n```\n\nYou can also enable scan debugging by setting the following key to `true`: `kumuluzee.graphql.scanning.debug`. This\nwill output a verbose log of scanning configuration and progress.\n\n## Adding Graph*i*QL (a GraphQL UI)\n\nGraph*i*QL is a querying tool for GraphQL application. \nIt is the Postman equivalent for GraphQL.\nYou write your query, parameters and Graph*i*QL will send the request. \nIt also checks your query syntax and allows you to explore your schema graphically.\nMore information can be found [here](https://github.com/graphql/graphiql).\n\nIf you want to include GraphiQL to your project, include the following dependency:\n  \n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.kumuluz.ee.graphql\u003c/groupId\u003e\n    \u003cartifactId\u003ekumuluzee-graphql-ui\u003c/artifactId\u003e\n    \u003cversion\u003e${kumuluzee-graphql.version}\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nBy default, Graph*i*QL will be accessible on `/graphiql` endpoint. You can configure the mapping or disable Graph*i*QL\nwith KumuluzEE Configuration framework. Example configuration:\n\n```yaml\nkumuluzee:\n  graphql:\n    ui:\n      mapping: /api-ui\n      enabled: false\n```\n\n## Using kumuluzee-security on GraphQL queries\n\nYou can use [kumuluzee-security](https://github.com/kumuluz/kumuluzee-security) extension to secure GraphQL queries and\nmutations with familiar annotations `@RolesAllowed`, `@PermitAll`, etc. In order to start using kumuluzee-security first\ncreate a class that extends `GraphQLApplication` class and annotate it with `@GraphQLApplication` and `@DeclareRoles`.\nFor example:\n\n```java\n@GraphQLApplicationClass\n@DeclareRoles({\"user\", \"admin\"})\npublic class CustomerApp extends GraphQLApplication {\n}\n```\n\nThen secure a class annotated with `@GraphQLApi` by adding `@Secure` annotation. You can then proceed to use the\nstandard `@DenyAll`, `@PermitAll` and `@RolesAllowed` annotations. For example:\n\n```java\n@RequestScoped\n@GraphQLApi\n@Secure\npublic class CustomerResource {\n\n    @Inject\n    private CustomerService customerBean;\n\n    @Query\n    @PermitAll\n    public List\u003cCustomer\u003e getAllCustomers() {\n       return customerBean.getCustomers();\n    }\n\n    @Query\n    @RolesAllowed({\"user\", \"admin\"})\n    public Customer getCustomer(@Name(\"customerId\") String customerId) {\n        return customerBean.getCustomer(customerId);\n    }\n}\n```\n\nFor a more detailed example of kumuluzee-security integration check out the\n[kumuluzee-graphql-jpa-security](https://github.com/kumuluz/kumuluzee-samples/tree/master/kumuluzee-graphql-jpa-security)\nsample.\n\n## Integration with kumuluzee-metrics\n\nYou can enable automatic metrics integration by setting the following configuration key (note that\n`kumuluzee-metrics-core` dependency must be present):\n\n```yaml\nkumuluzee:\n  graphql:\n    metrics:\n      enabled: true\n```\n\nThis will add a counter and a timer to every query and mutation in the application. For a more fine-grained control\nover metrics you can always use metrics annotations on your query/mutation methods. For example:\n\n```java\n@Query\n@Counted(name = \"get_customer_counter\")\npublic Customer getCustomer(@Name(\"customerId\") String customerId) {\n    return customerBean.getCustomer(customerId);\n}\n```\n\n## Integration with kumuluzee-bean-validation\n\nYou can validate arguments to queries and mutations by enabling Bean Validation integration with the following\nconfiguration key (note that `kumuluzee-bean-validation-hibernate-validator` dependency must be present):\n\n```yaml\nkumuluzee:\n  graphql:\n    bean-validation:\n      enabled: true\n```\n\nArguments in query and mutation methods will then be verified by bean validation implementation. For example:\n\n```java\n@Query\npublic Customer getCustomer(@Name(\"customerId\") @Pattern(regexp = \"\\\\d+\") String customerId) {\n    return customerBean.getCustomer(customerId);\n}\n```\n\nAnother example:\n\n```java\n@Mutation\npublic Customer addNewCustomer(@Name(\"customer\") Customer customer) {\n    customerBean.saveCustomer(customer);\n    return customer;\n}\n\n// Customer.java:\npublic class Customer {\n\n    // ...\n\n    @Size(min = 3, max = 15)\n    private String firstName;\n\n    // ...\n}\n```\n\n## Integration with kumuluzee-rest\n\nYou can use the standard [kumuluzee-rest](https://github.com/kumuluz/kumuluzee-rest) parameters (pagination/sort/filter)\nin GraphQL queries by using the `GraphQLUtils.queryParametersBuilder()` to construct `QueryParameters`\nwhich can then be used by _kumuluzee-rest_.\n\nFor example:\n\n```java\n@Query\npublic StudentConnection getStudentsConnection(Long limit, Long offset, String sort, String filter) {\n\n    QueryParameters qp = GraphQLUtils.queryParametersBuilder()\n            .withQueryStringDefaults(qsd)\n            .withLimit(limit)\n            .withOffset(offset)\n            .withOrder(sort)\n            .withFilter(filter)\n            .build();\n\n    return new StudentConnection(JPAUtils.queryEntities(em, Student.class, qp),\n        JPAUtils.queryEntitiesCount(em, Student.class, qp));\n}\n```\n\nQuery:\n\n```graphql\nquery StudentsStartingWithJ {\n  studentsConnection(\n    offset: \"0\"\n    limit: \"10\"\n    sort: \"studentNumber\"\n    filter: \"name:LIKE:J%\"\n  ) {\n    totalCount\n    edges {\n      studentNumber\n      name\n      surname\n    }\n  }\n}\n```\n\n## Changelog\n\nRecent changes can be viewed on Github on the [Releases Page](https://github.com/kumuluz/kumuluzee-graphql/releases).\n\n\u003e **For 1.0.x users, see the following README: [kumuluzee-graphql](https://github.com/kumuluz/kumuluzee-graphql/tree/master/core)**\n\n## Contribute\n\nSee the [contributing docs](https://github.com/kumuluz/kumuluzee-graphql/blob/master/CONTRIBUTING.md).\n\nWhen submitting an issue, please follow the \n[guidelines](https://github.com/kumuluz/kumuluzee-graphql/blob/master/CONTRIBUTING.md#bugs).\n\nWhen submitting a bugfix, write a test that exposes the bug and fails before applying your fix. Submit the test \nalongside the fix.\n\nWhen submitting a new feature, add tests that cover the feature.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkumuluz%2Fkumuluzee-graphql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkumuluz%2Fkumuluzee-graphql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkumuluz%2Fkumuluzee-graphql/lists"}