Skip to content

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

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

Please set an alt value for this image...
  • IT Ops
  • Software Development
  • Guides
  • java

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

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.

Josh Cummings

Josh C.

Like many software craftsmen, Josh eats, sleeps, and dreams in code. He codes for fun, and his kids code for fun! Right now, Josh works as a full-time committer on Spring Security and loves every minute. Application Security holds a special place in his heart, a place diametrically opposed to and cosmically distant from his unending hatred for checked exceptions.

More about this author