How to use GraphQL in Spring: An easy step-by-step tutorial
Want to get started using GraphQL in Spring? In this guide, we cover how to set up a basic GraphQL server application and test it, so you can start building.
May 2, 2024 • 6 Minute Read
In this guide, we’ll cover how to get started with GraphQL in Spring. We’ll touch on some of the motivations for choosing GraphQL, and how to get a prototype quickly up and going so you can learn and demonstrate the benefits it may hold for your use cases.
Spring tutorial series
- How to get started with the Spring Framework
- What is Spring AI, and how to use it: A beginner's guide
- Spring Security: How to get started
Why GraphQL?
Given that there are already standards like SOAP and REST for exposing APIs, you might wonder about the motivations for creating GraphQL.
To address this, let’s look at REST for a moment. REST is a way to model resources over HTTP; generally speaking, each request operates on a single resource, operates on the entire resource, and indicates no schema or type system. While REST is very popular due to its simplicity, these architectural assumptions tend to create very chatty APIs that rely on the client to interpret the types. Thinking about a single resource at a time can also make things like domain-driven design less of a natural fit for REST.
GraphQL approaches the problem of fetching data differently; it’s a query language (like SQL is a query language) that allows clients to fetch data how they need it returned. Behind the query language is a query engine that invokes the correct data fetchers based on the parsing of the query.
Where REST is a set of principles that describe how a server can expose data to a client, GraphQL is a client-side query language that clients use to describe to the server the data that it wants. This means a client can more easily query across resources (think SQL table joins) and retrieve only the data it needs (think SQL SELECT and WHERE clauses) without the server needing to anticipate that specific query in advance.
With that in mind, let’s build a prototype API that manages trains and their routes.
Using SpringInitializr
When you are building a Spring application, https://start.spring.io is a great place to begin. If you are following along, pick the latest Spring Boot and Java versions, and then select the Spring Web and Spring for GraphQL modules like in this screenshot:
Then, download the artifact and import into your favorite IDE.
Aside from building a skeleton application, it added the following two dependencies:
implementation 'org.springframework.boot:spring-boot-starter-graphql'
implementation 'org.springframework.boot:spring-boot-starter-web'
...which will give us the needed Spring GraphQL primitives for building a GraphQL-enabled service.
Modeling Resources
Let’s introduce two simple Java records, one called Train and the other called Route:
public record Train(String id, String conductor, Integer capacity) {}
public record Route(String id, String departure, String arrival, Integer time, String trainId) {}
While we are here, note that these two domain objects only refer to each other by reference, which may be different than what you are used to. This is because we’re going to allow the GraphQL engine to do that joining work for us.
Declaring the Schema
Remember that one of the advantages of GraphQL is that it is type-safe (again, that’s the idea with SQL, too). To achieve that, we need to declare a schema.
In Spring Boot, you’ll declare this in a file called schema.graphqls and place it in src/main/resources/graphql. It should look something like this:
type Query {
routeById(id: ID): Route
}
type Route {
id: ID
departure: String
arrival: String
time: Int
train: Train
}
type Train {
id: ID
conductor: String
capacity: Int
}
The first declaration is general to all GraphQL schemas. It is a listing of the query operations exposed by the application. The remaining two describe our custom data types to GraphQL. Note that `Query` and `Route` are both composed of GraphQL types and custom types.
Declaring Data Fetchers
Because there are two custom data relationships in our schema -- the query and the mapping from Route to Train -- we are going to declare two data fetchers using Spring GraphQL.
In Spring GraphQL, you declare data fetchers in the same way that you declare controllers. Instead of @GetMapping like in a @RestController that maps a GET query to a Java method, we’ll use @QueryMapping to map the routeById query to a Java method. Then, we’ll use @SchemaMapping to map the route-to-train schema relationship to a Java method.
Let’s do both now:
@Controller
public class RouteController {
@QueryMapping
public Route routeById(@Argument(“id”) String id) {
// …
}
@SchemaMapping
public Train train(Route route) {
// …
}
}
To add some data to return, let’s create a couple of maps like so:
@Controller
public class RouteController {
private final Map<String, Train> trains = Map.of(“1”, new Train(“1”, “James”, 50),
“2”, new Train(“2”, “Carla”, 75), “3”, new Train(“3”, “Peabody”, 20));
private final Map<String, Route> routes = Map.of(“4N”, new Route(“4N”, “Manhattan”, “Albany”, 180, “1”),
“4S”, new Route(“4S”, “Albany”, “Manhattan”, 180, “1”));
// …
}
Using something in-memory now is a good way to keep our focus on GraphQL principles before adding in concepts like a database.
And now let’s add the implementation details for each method. The query method will need to fetch a route by its id, which we can do by querying the routes map like so:
And for the schema method, we need to find the train, given the route:
```
@SchemaMapping
public Train train(Route route) {
return this.trains.get(route.trainId());
}
Running a Few Queries
Great! The server is ready to process a few queries. But how do we send them? cURL, HTTPie, and others are tools for sending HTTP-based queries. We need something that knows how to send GraphQL queries.
Fortunately, Spring Boot ships with one that you can activate by setting the following property in application.properties:
spring.graphql.graphiql.enabled=true
Now we’re ready to see how Spring Boot and GraphQL’s query engine interact.
Start the server by running the main method in TrainsQlApplication, and then navigate to http://localhost:8080/graphiql.
You should see a GraphQL client like this one:
In the query section, paste the following query:
query routeDetails {
routeById(id: "4S") {
departure
arrival
time
train {
capacity
}
}
}
This query says that you want the departure and arrival city for the 4S route, the time it’s leaving and how many seats remain on the train.
Run the query and you should see a result like this:
{
"data": {
"routeById": {
"departure": "Albany",
"arrival": "Manhattan",
"time": 180,
"train": {
"capacity": 50
}
}
}
}
Looks like there is plenty of space left for you and your friends to take a trip!
Testing GraphQL Fetchers
Let’s end by adding a test to make sure our fetcher continues to work as expected.
Create a new test file in src/test/java called RouteControllerTests and place the following inside:
@GraphQlTest(RouteController.class)
public class RouteControllerTests {
@Autowired
GraphQlTester graphQl;
@Test
void shouldGetRouteById() {
this.graphQl.documentName("routeDetails")
.variable("id", "4S")
.execute()
.path("routeById")
.matchesJson("""
{
"departure": "Albany",
"arrival": "Manhattan",
"time": 180,
"train": {
"capacity": 50
}
}
""");
}
}
This refers to a document called routeDetails that we also need to add. In the src/test/resources/graphql-test directory, add a file called routeDetails.graphql and place the following:
query routeDetails($id: ID) {
routeById(id: $id) {
departure
arrival
time
train {
capacity
}
}
}
You’ll recognize this as the query we wrote during the demo.
Run the test and it should pass.
Conclusion
In this article, you learned some of the motivations for GraphQL and how it differs from REST in the set of problems it is trying to solve. Then, you learned how to set up the skeleton of a basic GraphQL server application. We route the needed domain objects and controller to accept a query. And we also added testing to ensure that it continues to work down the road.
Now, think about your use cases and try building something yourself!
Learning more about Java, Spring, and GraphQL
If you’re keen to upgrade your Java skills, Pluralsight also offers a wide range of Java and Spring related courses that cater to your skill level, as well as a dedicated course on building Scalable APIs with GraphQL. You can sign up for a 10-day free trial with no commitments. You can also perform an equally free roleIQ assessment to see where your Java skills currently stack up, with advice on where to shore up the gaps in your current skill set.
Below are two Pluralsight learning paths with beginner, intermediate, and advanced Java courses — just pick the starting point that works for you. If you’re not sure where you’re at, each page has a skill assessment you can use that will recommend where you should start out.