We are an e-Commerce consulting company and have been working on different e-Commerce projects for a long time. It started with huge monolithic Java applications using Spring and Hibernate and evolved to smaller micro-services that use various technologies. Our Java dev team does the database connectivities, business logic, etc. (we call it the Backend) using Spring, SpringBoot, and Hibernate frameworks. With the help of Rest APIs, we transfer the data to the FrontEnd (done in Angular, React, etc.), and it is displayed elegantly as per the customer requirements by our frontend developers. There was no change in this until we started building the Subscriber app on Shopify.
Shopify Admin uses GraphQL APIs for transferring the data and not the Rest APIs we have always worked with. Rest APIs were simple; we just used a GET request to get the data required. It will have all the data related to that object, even if we need only a few properties from the response.
To update, delete or create an object, we had to use corresponding requests, and the response either had more than the data we needed or none of the data we needed. We used the Rest APIs to build custom Shopify apps for our clients. Armed with this experience of custom app development, we started our journey of building a subscription app with the help of GraphQL.
As in the case of all developers, we started googling for answers. We knew for sure that there would be libraries for us to use, and all we had to do was add the dependencies in our pom.xml file (we are building using Maven).
Disclaimer: This article describes how we achieved things; we believe that it will work for others as well; but since technology keeps changing, there will be tweaks that may have to be done for others.
GraphQL-Java
We soon discovered those first hits in our ‘java graphql’ searches were building server-side graphql programs and not on how to query and retrieve the GraphQL responses using the schema. We found another great article on dev.to on building with SpringBoot and Maven, and it was on building graphQL service rather than retrieving and processing the response.
Apollo-Android
We discovered Apollo-Android during our searches, and it seemed that was the solution we were looking for. Apollo android supported Android, but we found some Java examples too. Most of the examples were, as usual, server-sided, and we were not able to make use of it much. There were some client-side coding examples too, but a major drawback was the absence of Maven plugins. We either had to change to Gradle or find another solution, and we chose the latter.
JMustache
We added one more word to the search query - Shopify. We aimed to check if anyone else has done a SpringBoot-Maven App using GraphQL before us. We came across one blog, and they used the JMustache Library to send the queries and had examples on how to send queries and get the response. We should create POJO classes and map the response to them. But unfortunately, the example did not work in our case, and there were some conflicts with the SpringBoot libraries we used.
Should we discard Java?
At this stage, we were doubtful about using SpringBoot-Maven to develop an app on Shopify with GraphQL APIs. We wondered if we should change it and use only Node.js and React for the entire app.
We planned to distribute the load; all tasks that dealt with the user were to be done using Node.js and React, while the schedulers, webhooks, etc., will be handled using the SpringBoot application. The abilities of Java-like multi-threading, schedulers in SpringBoot, in-built security features of Spring framework, simple database connectivity with Hibernate, etc., made us explore a Java-based solution once again. We took help from senior architects and junior developers. You never know who strikes the gold!
GraphQL Java Generator
It was one of our junior developers who shared with us the solution - the graphql java generator. The application takes a GraphQL schema and generates the code/interface/POJOS to the given package - and it works like a charm! All the codes were created, and we only had to call them in our application!
To get the schema file for Shopify Admin APIs, apollo ( nodejs version ) was used:
npm install -g apollo
apollo client:download-schema \
shoify-admin.graphqls \ --header= "X-Shopify-Access-Token:<the access token here>" \ --endpoint= "https://myshopify_domain_here/admin/api/2021-01/graphql.json"
We added the schema, shopify-admin.graphqls, to our resource folder.
After compilation, the generated code was added to the maven repository and importing the code was all that we had to do.
Here is an example for retrieving the Shop information using graphql query:
public Shop getShop(String shopUrl, String access token) throws GraphQLRequestPreparationException, GraphQLRequestExecutionException, IOException {
QueryRootExecutor queryTypeExecutor = new QueryRootExecutor(<shopify admin url>, <access token>);
final String query = loadQueryFile(<path to get_shop.graphql>);
GraphQLRequest shopSimpleRequest = queryTypeExecutor.getShopGraphQLRequest(query);
Shop shop = e.shop(shopSimpleRequest);
return shop;
}
To create the query, we used the shopify graphql query explorer, which is a very helpful tool provided by Shopify. And this was the get_shop.graphql file:
{
id
name
url
myshopifyDomain
features {
eligibleForSubscriptions
sellsSubscriptions
eligibleForSubscriptionMigration
}
}
We could retrieve the properties with the getters and setters inside the POJO classes generated while running the application:
Shop shop = getShop( url, access token);
System.out.println(“Shop Name = “+ shop.getName());
Output: Shop Name = shop_name
Errors
Although we found the solution, everything did not work as expected. We had issues, but we were able to fix it.
Missing Extensions
Our first error was that the GraphQL Java Generator did not give the mapping for the extensions field in Shopify GraphQL Response:
{
"data": {
"shop": {
"name": "shop_name",
"id": "gid:\/\/shopify\/Shop\/shop_id_number",
"url": "https:\/\/shop_name.com",
"myshopifyDomain": "shop_name.myshopify.com",
"features": {
"branding": "SHOPIFY",
"eligibleForSubscriptions": false,
"sellsSubscriptions": true,
"eligibleForSubscriptionMigration": false
}
}
},
"extensions": {
"cost": {
"requestedQueryCost": 3,
"actualQueryCost": 3,
"throttleStatus": {
"maximumAvailable": 1000.0,
"currentlyAvailable": 997,
"restoreRate": 50.0
}
}
}
}
The generated Java classes had only the data and error objects and missed the extensions. We contacted the developer and he gave us the solution, and it was added in the next release of the library, version 1.12.3. We are using this version in our project.
Dependency Conflicts
We started again and soon we got stuck with a new error - Field graphQL in graphql.spring.web.servlet.components.DefaultGraphQLInvocation required a bean of type 'graphql.GraphQL' that could not be found. But the problem was solved with a little bit of googling - we added an exclusion to the java-graphql dependency:
<!-- Dependencies for GraphQL -->
<dependency>
<groupId>com.graphql-java-generator</groupId>
<artifactId>graphql-java-runtime</artifactId>
<version>${graphql-maven-plugin.version}</version>
<exclusions>
<exclusion>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java-spring-boot-starter-webmvc</artifactId>
</exclusion>
</exclusions>
</dependency>
Scalar Conflicts
There were some scalar conflicts, like in case of a date where shopify accepts a string of a particular format but the generated POJO was accepting only java.util.Date object. We had to make some code changes there. Removing the jar file from the Maven libraries and adding the code to our application was the only option we had. The source code can be retrieved by unzipping the graphql-java-runtime-1.12.3-sources.jar file present in the com\graphql-java-generator\graphql-java-runtime{version_number} folder of your maven repository.
To stop the auto-generation of the code during the build, we commented out the com.graphql-java-generator plugin from the pom file. So the generated graphql code was added to our application, instead of adding the jar file as a library. It increased the size of the application and the compilation time. Since we couldn’t come up with another solution, we had to make this sacrifice.
SpringBoot version issue
Another error which blocked us was the NoSuchMethodError:
java.lang.NoSuchMethodError: 'reactor.core.publisher.Mono reactor.core.publisher.Mono.retryWhen(java.util.function.Function)
We changed the SpringBoot version to 2.4.0 to get rid of this error.
Let us win together
Our journey with the Shopify app has been an adventurous one, falling down and getting up again to complete the race. The help we received from our colleagues and the development community has been great and wonderful. We started the journey during March 2021 building the Subscriber App, and got approved by mid August 2021, including the production extensions released by Shopify a week before our submission for approval. We have reached a position where we are stable, and currently we are concentrating on winning the marathon rather than completing it. But we don’t want to win it alone. We want all of the passionate Shopify experts and developers to win with us. And that is why we have described our journey here, hoping it may help someone to win the race.
If anyone finds this useful or any other alternative methods to use Springboot for Shopify App development using Maven, please share in the below comments section. We would love to hear from you and start exploring more.
Top comments (1)
That is very interesting and it's encouraging to see other Java developers trying to get Shopify working. I find their docs to be terribly opaque.
About GraphQL I suggest you take a look at project Manifold and its support for it. I've written a lot about Manifold in the past month but didn't get a chance to cover the GraphQL support yet. It's pretty fantastic and very close to what you would get with JavaScript.