Developing the Camunda GraphQL extension


Just tried it out and I must say it is fun to use :slight_smile:

For anyone interested in this project and using Windows, I used graphql-ide as the client and followed the build instructions in the repository. I wasn’t able to build skevy/graphiql-app locally.

One minor remark on the code: I think you can also use the Task interface where you currently use TaskEntity. That way you would stay with the Camunda public API for now.


Hi Thorben, thanks for your feedback. Appreciate that.

Regarding Task vs. TaskEntity:

  1. Java code: I switched from TaskEntity to Task for the setAssignee Mutation. It’s now the same as for the GraphQL tasks queries. Thanks for your hint.
  2. GraphQL: TaskQuery. .list() returns a List of TaskEntity objects, right?. That is why I decided to use a type called TaskEntity in GraphQL as return type for e.g. the GraphQL tasks(..) query. With that I can make use of the GraphQL-Java-classes-mapping without defining new arbitrary GraphQL types. So the GraphQL types really map to the Java API classes with same name! :slight_smile:
    Let me know if that doesn’t make sense to you.

Let me know If you find some more improvements especially in the usage of the API.

So, what’s next?



I see, makes sense. There is at least one occasion where there are multiple implementations of one API interface, see ProcessInstance and its implementations. The default mapping might not work there.

One thing: I would not call the entry point to access process instances processes. processes rather refers to process models aka process definitions instead of instances. In the REST API we use the terms process-instance and process-definition to distinguish the two concepts.

That’s up to you. You could go for a quick release and blog post to gain traction or keep building things. Interesting topics to explore could be:

  • APIs that take or return variables (e.g. TaskQuery#processVariableValueEquals and TaskService#getVariables)
  • authentication and authorization
  • error handling; e.g. what happens if I assign a task that does not exist
  • testing


List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery().list();

returns a List of ProcessInstance, actually implemented by a List of ExecutionEntity

…which is why I defined in GraphQL the type named exactly ExecutionEntity:

type Query { processes: [ExecutionEntity] }

The query processes returns a List of ExecutionEntity matching the implemented return type of the Java API call from above.

I agree to rename processes to processInstances in GraphQL, actually had it named that before - its Colloquialism, but its not accurate. So yes, makes sense because we will also expose processDefintions and process(Models) to GraphQL, so we need to distinguish them. Thanks for pointing me to this.


Correct, for process instance queries, you will get instances of ExecutionEntity in return. However, when you implement a mutation that starts a new process instance, then you will get an instance of ProcessInstanceWithVariablesImpl in return (which also implements ProcessInstance but does not subclass ExecutionEntity). I just wanted to point out that you cannot map it to ExecutionEntity in the GraphQL schema then.


Right, that’s a valid point. I did not realize it.

So let’s implement this in GraphQL now…

The Camunda API here is:

These are the adapted GraphQL type definitions:

You can immediately see how they resemble the Camunda API.

Now having this above Type Definitions in place, we can rewrite our query nicely like this:

(see how we are getting closer to the Java API. Cherry-picking from the Camunda API…)

The query in GraphiQL looks now like this:

(I added the internal GraphQL field __typename to the query selector to show the GraphQL type: As you can see the query processes returns correctly a list of ExecutionEntity as an implementation of the interface ProcessInstance)

At least for the process instance queries we generalized the Type Schema using GraphQL interfaces to resemble the Camunda API.

The same must now be applied to the mutation createProcessInstance, which I implement next…
(Maybe the same Pattern applies to tasks and other things…)

Again, thanks for your valid input here :slight_smile:




(returns the processInstanceId after creation of processInstance)


Currently this project is maintained in my GitHub repo Loydl/camunda-graphql.
I think it should move soon to the camunda GitHub repo as discussed previously. That is the natural home for Community Extensions.
We can put it in/name it something like

  • camunda/camunda-bpm-graphql-server
  • camunda/camunda-graphql-server
  • camunda/camunda-bpm-graphql

So, what is the best way to move the current project from Loydl to camunda and continue developing it there?
How can I support or start this process now?


Hi Harald,

There are two ways:

  • I can create the repository in the Camunda organization and give you push rights. This would lose the two issues in your current repository and would not create redirects from the current repository URL to the new URL.
  • To make a proper transfer including issues and redirects, the transferring user must have admin rights in the Camunda organization. Going down this route, you could transfer the repository to my personal user first (named ThorbenLindhauer) and I would then transfer it to the Camunda organization. Some details on repository transfer can be found here:

Regarding naming: I think we can still rename the repository after transfer, but if you already have a favourite name, then we could set it up straight away. I would prefer camunda-bpm-graphql or camunda-bpm-graphql-api.


New Extension camunda-bpm-migration

OK, I requested the transfer to GitHub repo ThorbenLindhauer :slight_smile:


Done, repository is here:
I also created a dedicated forum category:

Next, I’ll see that we set up a Jenkins build on including the possibility to release to Camunda Nexus and Maven central.


In a next step we want to enhance GraphQL mutations for user task form submits.
I.e. we want to be able to submit a user task form (i.e. send data to the server) of a specific process instance (waiting for this user input) via a GraphQL mutation.
To prepare this a little bit I want to backup my assumptions here. I am correct with this:
When we submit a form in Camunda BPM tasklist, regardless of the kind of the form (embedded, generated, external, generic), it will create or update process variables. We probably also have to look into a mapping between GraphQL and Java types.
For this kind of mutation we need to find the right Camunda API call to “upsert” process variables and tell the process engine to continue with the process.
Please let me know if my understanding is correct and where the possible pitfalls are here.


Hi Harald,

The corresponding Java APIs to submit forms are org.camunda.bpm.engine.FormService.submitTaskForm(String, Map<String, Object>) and org.camunda.bpm.engine.FormService.submitStartForm(String, String, Map<String, Object>). I’m not 100% sure, but I think that is what is used for all types of forms in tasklist.

For conversion of GraphQL types to process variables, have a look at the typed value API for process variables. That should give you an idea what kind of values can be submitted and how they are created.

I don’t really see a pitfall, but I expect implementing the variable conversion to be a little time-consuming.



One possible pitfall with variables of type object, and in particular instances of classes provided along with a process application: I’d advise against deserializing GraphlQL input into a custom object in the context of the camunda-graphql application. That is because this requires the classes to be available to its classloader, which does not hold in many deployment scenarios. Instead, use the APIs to create a Camunda object value from its serialized value. Relevant documentation would be


We have added JWT (JSON Web Token) Authentication to Camunda GraphQL. Now there are two Authentication options available for Camunda GraphQL:

  • Basic
  • JWT
    (If you count “No Authentication” then there are three options ;-))

JWT makes totally sense in a lot of scenarios (e.g. SSO).
To make JWT work you need a “JWT Provider”, i.e. a system who creates or issues JWTs. Camunda GraphQL will not do that for you - this functionality does not belong to GraphQL.
For that reason we created another Open Source project: Camunda JWT Provider to let Camunda BPM create JWTs.
It is an early draft but basically adds an endpoint to accomplish login for Camunda BPM using your IdentityProvider (internal, LDAP, …).
This it how it works: your client software POSTs a JSON with name and password to the JWT Provider endpoint (e.g. localhost:8080/auth/jwt, and here we let Camunda BPM play that role) and if your name and password gets authenticated the server returns a new JWT. This JWT will be included in subsequent client requests to the server or other systems.
So if you present this JWT to Camunda GraphQL and the parameters are set properly (issuer and secret match) you get authorized by Camunda GraphQL without Camunda having to know name and password (because JWT is self contained).
Single Sign On (SSO) is a feature that uses JWT, because of its small overhead and its ability to be easily used across different domains.


I started to follow the instruction. (link:
First of all, is paragraph “Release 0.3.0” is somekind of action or just for information? (P.S I skipped it)
Then we have header “Build the GraphQL server” which has 3 steps

  1. I cloned Git repository
  2. how I should “Adapt src/main/resources/”?
  3. without changing anything from second step i tried to make “mvn clean package” and have error shown below
    Could you, please, give some advise how to solve this problem?

[ERROR] [ERROR] Some problems were encountered while processing the POMs:
[FATAL] Non-resolvable parent POM for org.camunda.bpm.extension.graphql:camunda-bpm-graphql:0.4.0-SNAPSHOT: Could not transfer artifact org.camunda:camunda-release-parent:pom:2.5 from/to ( Software caused connection abort: recv failed and ‘parent.relativePath’ points at no local POM @ line 5, column 13