Skip to main content

Class 1: Your First GraphQL Service

Duration: 30 minutes Difficulty: Beginner Prerequisites: Basic Spring Boot knowledge, Java 17+, IDE of choice

What You'll Learn

By the end of this class, you will:

  • Understand what GraphQL is and why it matters
  • Create a Spring Boot project with GraphQL support
  • Write your first GraphQL schema
  • Implement your first query resolver
  • Test queries using GraphiQL

What is GraphQL?

Before we write code, let's understand what we're building.

GraphQL is a query language for your API. Unlike REST, where the server decides what data to return, GraphQL lets clients ask for exactly what they need.

┌─────────────────────────────────────────────────────────────────────┐
│ REST vs GraphQL │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ REST (Multiple endpoints, fixed responses): │
│ GET /movies/1 → { id, title, year, director, actors... } │
│ GET /movies/1/actors → [{ id, name, bio, awards... }] │
│ GET /directors/5 → { id, name, movies... } │
│ │
│ GraphQL (One endpoint, flexible responses): │
│ POST /graphql │
│ query { │
│ movie(id: 1) { │
│ title ← Only what you need │
│ director { name } ← Nested in one request │
│ } │
│ } │
│ │
└─────────────────────────────────────────────────────────────────────┘

Step 1: Create the Project

Let's create a new Spring Boot project. You have two options:

  1. Go to start.spring.io

  2. Configure your project:

    • Project: Maven
    • Language: Java
    • Spring Boot: 3.2.x (or latest stable)
    • Group: com.example
    • Artifact: moviedb
    • Name: moviedb
    • Package name: com.example.moviedb
    • Packaging: Jar
    • Java: 17 or 21
  3. Add dependencies:

    • Spring Web
    • Spring for GraphQL
  4. Click "Generate" and extract the ZIP file

Option B: Add to Existing Project

If you have an existing Spring Boot project, add these dependencies to your pom.xml:

<dependencies>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Spring GraphQL -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-graphql</artifactId>
</dependency>

<!-- For testing (optional but recommended) -->
<dependency>
<groupId>org.springframework.graphql</groupId>
<artifactId>spring-graphql-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

Step 2: Enable GraphiQL

GraphiQL is an in-browser IDE for exploring GraphQL APIs. Let's enable it.

Open src/main/resources/application.properties (or create application.yml) and add:

# application.properties
spring.graphql.graphiql.enabled=true
spring.graphql.graphiql.path=/graphiql

Or in YAML format:

# application.yml
spring:
graphql:
graphiql:
enabled: true
path: /graphiql

Step 3: Create Your First Schema

GraphQL uses a Schema Definition Language (SDL) to define your API. Create this file:

📁 src/main/resources/graphql/schema.graphqls

type Query {
hello: String!
movie(id: ID!): Movie
movies: [Movie!]!
}

type Movie {
id: ID!
title: String!
releaseYear: Int!
genre: String!
}

Let's break this down:

ElementMeaning
type QueryThe entry point for read operations
hello: String!A field that returns a non-null String
movie(id: ID!)A field that takes an ID argument
[Movie!]!A non-null list of non-null Movies
type MovieA custom object type
The Exclamation Mark

! means non-null. String can be null, String! cannot.

  • [Movie] - nullable list of nullable movies
  • [Movie!] - nullable list of non-null movies
  • [Movie!]! - non-null list of non-null movies

Step 4: Create the Movie Model

Create a simple Java class to represent a Movie:

📁 src/main/java/com/example/moviedb/model/Movie.java

package com.example.moviedb.model;

public class Movie {
private String id;
private String title;
private int releaseYear;
private String genre;

// Constructor
public Movie(String id, String title, int releaseYear, String genre) {
this.id = id;
this.title = title;
this.releaseYear = releaseYear;
this.genre = genre;
}

// Getters
public String getId() {
return id;
}

public String getTitle() {
return title;
}

public int getReleaseYear() {
return releaseYear;
}

public String getGenre() {
return genre;
}
}
Java Records

If you're using Java 16+, you can use a record instead:

public record Movie(String id, String title, int releaseYear, String genre) {}

Step 5: Create the Query Controller

Now let's implement the resolvers that handle our GraphQL queries:

📁 src/main/java/com/example/moviedb/controller/MovieController.java

package com.example.moviedb.controller;

import com.example.moviedb.model.Movie;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.stereotype.Controller;

import java.util.List;

@Controller
public class MovieController {

// Sample data - we'll use a database in later classes
private final List<Movie> movies = List.of(
new Movie("1", "The Shawshank Redemption", 1994, "Drama"),
new Movie("2", "The Godfather", 1972, "Crime"),
new Movie("3", "The Dark Knight", 2008, "Action"),
new Movie("4", "Pulp Fiction", 1994, "Crime"),
new Movie("5", "Forrest Gump", 1994, "Drama")
);

@QueryMapping
public String hello() {
return "Welcome to the Movie Database GraphQL API!";
}

@QueryMapping
public List<Movie> movies() {
return movies;
}

@QueryMapping
public Movie movie(@Argument String id) {
return movies.stream()
.filter(m -> m.getId().equals(id))
.findFirst()
.orElse(null);
}
}

Let's understand the annotations:

AnnotationPurpose
@ControllerMarks this as a Spring controller
@QueryMappingMaps a method to a GraphQL query field
@ArgumentInjects a GraphQL argument into the method
Method Naming Convention

By default, @QueryMapping uses the method name to match the schema field. So movies() maps to Query.movies. You can override this: @QueryMapping("allMovies")

Step 6: Run and Test

Start your application:

./mvnw spring-boot:run

Or run the main class from your IDE.

Open your browser and navigate to: http://localhost:8080/graphiql

You should see the GraphiQL interface!

Your First Query

In the left panel, type:

query {
hello
}

Click the Play button (▶️). You should see:

{
"data": {
"hello": "Welcome to the Movie Database GraphQL API!"
}
}

Query All Movies

query {
movies {
id
title
releaseYear
}
}

Response:

{
"data": {
"movies": [
{
"id": "1",
"title": "The Shawshank Redemption",
"releaseYear": 1994
},
{
"id": "2",
"title": "The Godfather",
"releaseYear": 1972
}
// ... more movies
]
}
}

Query a Single Movie

query {
movie(id: "3") {
title
genre
releaseYear
}
}

Response:

{
"data": {
"movie": {
"title": "The Dark Knight",
"genre": "Action",
"releaseYear": 2008
}
}
}

Try Requesting Different Fields

That's the power of GraphQL! Try this:

query {
movies {
title
}
}

You only get title back - nothing else. No over-fetching!

Understanding the Request/Response

Let's look at what's happening under the hood:

┌─────────────────────────────────────────────────────────────────────┐
│ GraphQL Request Flow │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Client sends POST to /graphql with query in body │
│ │
│ POST /graphql │
│ Content-Type: application/json │
│ { │
│ "query": "{ movies { title } }" │
│ } │
│ │
│ 2. Spring GraphQL parses and validates the query │
│ │
│ 3. For each field, it calls the corresponding resolver │
│ Query.movies → MovieController.movies() │
│ │
│ 4. Response is returned as JSON │
│ { "data": { "movies": [...] } } │
│ │
└─────────────────────────────────────────────────────────────────────┘

Project Structure So Far

Your project should look like this:

moviedb/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/moviedb/
│ │ │ ├── MoviedbApplication.java
│ │ │ ├── controller/
│ │ │ │ └── MovieController.java
│ │ │ └── model/
│ │ │ └── Movie.java
│ │ └── resources/
│ │ ├── application.properties
│ │ └── graphql/
│ │ └── schema.graphqls
│ └── test/
├── pom.xml
└── mvnw

Exercises

Before moving to the next class, try these exercises:

Exercise 1: Add a New Field

Add a rating field (type Float) to the Movie type and model. Update your sample data with ratings.

Solution

schema.graphqls:

type Movie {
id: ID!
title: String!
releaseYear: Int!
genre: String!
rating: Float!
}

Movie.java: Add a rating field and update the constructor.

MovieController.java: Update sample data:

new Movie("1", "The Shawshank Redemption", 1994, "Drama", 9.3)

Exercise 2: Add a New Query

Add a query called moviesByGenre(genre: String!): [Movie!]! that filters movies by genre.

Solution

schema.graphqls:

type Query {
# ... existing queries
moviesByGenre(genre: String!): [Movie!]!
}

MovieController.java:

@QueryMapping
public List<Movie> moviesByGenre(@Argument String genre) {
return movies.stream()
.filter(m -> m.getGenre().equalsIgnoreCase(genre))
.toList();
}

Exercise 3: Explore GraphiQL

Use the "Docs" panel on the right side of GraphiQL to explore your schema. Click on types to see their fields.

Common Issues and Solutions

Issue: Schema file not found

Error: No schema found Solution: Make sure your schema file is at src/main/resources/graphql/schema.graphqls (note the .graphqls extension)

Issue: Query returns null

Error: Field returns null unexpectedly Solution: Check that your method name matches the schema field name, or use @QueryMapping("fieldName")

Issue: Port already in use

Error: Port 8080 already in use Solution: Either stop the other application or change the port in application.properties:

server.port=8081

Summary

In this class, you learned:

✅ GraphQL is a query language that lets clients request exactly the data they need ✅ Spring GraphQL integrates seamlessly with Spring Boot ✅ The schema (.graphqls file) defines your API contract ✅ @QueryMapping connects schema fields to Java methods ✅ @Argument injects GraphQL arguments into your resolvers ✅ GraphiQL provides an interactive way to test your API

What's Next?

In Class 2: Schema Design Fundamentals, we'll dive deeper into:

  • All GraphQL scalar types
  • Enums and custom types
  • Input types for complex arguments
  • Schema design best practices

Your Movie API is about to get a lot more interesting!


Homework: Add at least 5 more movies to your sample data with various genres. We'll use them in the next class.