• Build a GraphQL Server With Spring Boot and MySQL
  • Yasas Sandeepa
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: samyu2000
  • Proofread by: Zenblo, Regon-cao

Build the GraphQL server application using SpringBoot and MySQL

Have you ever considered client direct control of API requests? Can the client request the data it actually needs and get it exactly? If you’re surprised by these questions, I’m pretty sure you’ve never heard of GraphQL.

The answer to the above question is yes, because you can do it using GraphQL. If you haven’t heard of it, don’t worry. I’ll take you through the latest and amazing world of GraphQL, and you won’t be sorry if this article makes you a huge profit.

In general, you need to be familiar with the Java language, the Spring Boot framework, and REST APIs. No previous experience with GraphQL is required. You may be very interested in this topic. So what are you waiting for? But before getting hands-on, I’ll give you an overview of GraphQL and what makes it so special.

Get started with GraphQL

I’m sure you’re struggling to find out more about GraphQL. What exactly is GraphQL? Take a deep breath and listen to me. Simply put, it is a data query and manipulation language that can be used in the API.

GraphQL provides a simple terminal that can be used to receive query requests from the front end, return corresponding query results, and accurately retrieve some data as required. So I have to say: Don’t underestimate the power of this universal client.

Look at this GIF and it becomes clear. This is part of the functionality of the application we will implement.

GraphQL is now widely used by hundreds of companies large and small, including Facebook (where GraphQL was originally invented in 2012, Open source in 2015), Credit Karma, GitHub, Intuit, PayPal, The New York Times, and more.

Hey, wait. REST needs to be introduced. Isn’t it useful enough? So let’s look at this.

GraphQL vs. REST

REST apis are familiar to us because of their widespread use. Today, however, GraphQL is the order of the day because it is more flexible and performs better.

So, how is REST fundamentally different from GraphQL? REST is an architectural idea for developing web applications. GraphQL is a query language, a technical specification, and a collection of single-point tools. Let me give you an example just to give you a better idea of what this is all about.

Suppose you need to look up book information. Also, you need to look up the author information (the book and the author are two different entities). The typical approach is to send two GET requests.

localhost:8080/book/:id
localhost:8080/author/:id
Copy the code

But with GraphQL you can get all the information from a single API endpoint.

localhost:8080/graphql
Copy the code

As in the previous GIF, if you want to aggregate some information on a terminal, you can filter some unwanted fields. With REST, however, you can only get the entire data set, not filter some data.

Sometimes, the data in the response cannot be used directly (such as nested data), and you have to make a separate request to get the data you actually need. On the other hand, there’s a lot of data in the response that you don’t need, you just need one or two fields.

This phenomenon is called underread and overread. GraphQL can solve these problems and optimize your application. REST is like a restaurant without waiters compared to GraphQL.

However, there is a learning curve with GraphQL, and while there are some differences from REST apis, it is certainly worth learning. If you can build large, user-friendly applications, it’s satisfying for the user to just get the data he needs and nothing else.

One of the added benefits of using GraphQL is that the performance of your applications improves dramatically because you don’t have to process a lot of data. Any performance improvement is a huge win.

Many programming languages, such as Java, JavaScript, Python, Scala, and so on, support GraphQL. You can visit the GraphQL website to learn about the various server and client languages.

Since I am more familiar with Java and JavaScript and have fewer articles on these technologies, I considered writing a Spring Boot application. There are also articles and manuals about Node.js, and it’s not that hard to implement.

If you need to know how to implement the GraphQL server using Node.js, please leave a comment and I’d love to write an article on this topic.

Okay, enough discussion. Let’s get into practice. There’s no better way to get your hands dirty.

Basics: Create projects

I’m working on an APP to get users and their posts. I’ve named this project WriteUp, and I’ll be redeveloping it later.

I built this APP from scratch. If you’re familiar with Spring Boot, you can quickly scan the basics. As a first step, we need to create a Spring Boot project using Spring Initializr or IntelliJ Idea.

Make sure you add these dependencies to your project.

  1. Spring Data JPA: For handling most JDBC-based database access operations, reducing template file code in JPA
  2. MySQL Driver: Used to manage the connection between Java programs and MySQL databases
  3. Lombok: Reduce the code in the model object classes and use Lombok annotations to automatically create GET /set methods.

Everything is ready. Let’s take a break and wait for IntelliJ to configure dependencies for the project.

Important points: Configuration basics

For the initial setup, we can create the entity model and add some virtual data to the MySQL database.

I created a package named Model, in which the User and Post classes are defined. The code is relatively simple, and because of annotations, the GET /set methods are generated automatically, so I don’t need to define them. Remember, however, that you must create a constructor that does not contain the ID field, which will be called at instantiation time.

package com.example.writeup.model;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
import java.util.Date;

@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "USER")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "USER_ID")
    private Integer userId;

    @Column(name = "FIRST_NAME")
    private String firstName;

    @Column(name = "LAST_NAME")
    private String lastName;

    @Column(name = "DOB")
    private Date dob;

    @Column(name = "ADDRESS")
    private String address;

    @Column(name = "POST_ID")
    private Integer postId;

    public User(String firstName, String lastName, Date dob, String address, Integer postId) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.dob = dob;
        this.address = address;
        this.postId = postId; }}Copy the code

Let’s create the Repository layer, which is used to connect to the database. I create a new package called Repository, in which I create two JpaRepository subinterfaces, one for User and one for Post.

It’s possible to inherit CrudRepository, but I prefer to inherit JpaRepository because its find method returns a plain list object, unlike CrudRepository, which returns an iterable list object. (For a more detailed view of these Repository interfaces, see here.)

package com.example.writeup.repository;

import com.example.writeup.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User.Integer> {}Copy the code

Next, I write a component class that adds virtual data to the database. You also need to create the Service package in which you define a DataLoader class. This class is responsible for adding some virtual data when the application is initialized.

package com.example.writeup.service;

import com.example.writeup.model.Post;
import com.example.writeup.model.User;
import com.example.writeup.repository.PostRepository;
import com.example.writeup.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.ThreadLocalRandom;

@Service
public class DataLoader {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PostRepository postRepository;

    @PostConstruct
    public void loadData(a){

        User user1 = new User("Yasas" ,"Sandeepa",DataLoader.getRandomDate(),"Mount Pleasant Estate Galle".1);
        User user2 = new User("Sahan" ,"Rambukkna",DataLoader.getRandomDate(),"Delkanda Nugegoda".2);
        User user3 = new User("Ranuk" ,"Silva",DataLoader.getRandomDate(),"Yalawatta gampaha".3);

        Post post1 = new Post("Graphql with SpringBoot",DataLoader.getRandomDate());
        Post post2 = new Post("Flutter with Firebase",DataLoader.getRandomDate());
        Post post3 = new Post("Nodejs Authentication with JWT",DataLoader.getRandomDate());

        postRepository.save(post1);
        postRepository.save(post2);
        postRepository.save(post3);

        userRepository.save(user1);
        userRepository.save(user2);
        userRepository.save(user3);

    }


    public static Date getRandomDate(a){
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, 1990);
        calendar.set(Calendar.MONTH, 1);
        calendar.set(Calendar.DATE, 2);
        Date date1 = calendar.getTime();
        calendar.set(Calendar.YEAR, 1996);
        Date date2 = calendar.getTime();
        long startMillis = date1.getTime();
        long endMillis = date2.getTime();
        long randomMillisSinceEpoch = ThreadLocalRandom
                .current()
                .nextLong(startMillis, endMillis);

        return newDate(randomMillisSinceEpoch); }}Copy the code

All right. Now you need to configure it in the application.properties file. Also make sure that the database is created and that credentials are set up in your application to connect to the MySQL database.

server.port=7000

#mysql properties
spring.jpa.generate-ddl=true
spring.datasource.url=jdbc:mysql://localhost/writeup
spring.datasource.username=user
spring.datasource.password=password
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=create
Copy the code

The basic architecture has been completed. Hey, where’s the controller? If you think you need a controller and I forgot, you’re dead wrong. In the REST API, we use controllers to handle multiple endpoints. But in GraphQL, as you know, you only need one API endpoint, so you don’t need a controller.

Ok, so let’s do an example to see if everything works.

Well, as you can see, the project’s infrastructure is working fine. Let’s move on.

Key steps: Assemble GraphQL related functions for the project

First things first! You need to add GraphQL dependencies for your project. Add the two dependencies in the Dependencies node of the POM.xml file, and click the M icon in the upper right corner to update the project.

<! -- GraphQL dependencies --> <dependency> <groupId>com.graphql-java</groupId> < artifactId > graphql - spring - the boot - starter < / artifactId > < version > 5.0.2 < / version > < / dependency > < the dependency > < the groupId > com. Graphql - Java < / groupId > < artifactId > graphql - Java - tools < / artifactId > < version > 5.2.4 < / version > < / dependency >Copy the code

GraphQL has two main building blocks: schemas and parsers. The first step in implementing GraphQL in your application is to define a schema.

The most basic component in the GraphQL schema is type, which represents an object (like a student, animal, etc.) and the properties of the object that you can retrieve from the programs you develop.

type Director {
  name: String!
  age: Int
  films: [Film]
}
Copy the code

In languages such as Java, there are many primitive and non-primitive data types. But there are only a few data types (also known as the Scalar type) that we need to know about here.

  • Int: a signed 32-bit integer
  • Float: signed double-precision floating-point value
  • String: a String encoded in UTF-8
  • Boolean: The value ranges from true to false
  • ID: unique identifier

Of course, you can define a Scalar type according to your needs. (e.g. Date, Currency, etc.)

Most of the data types in the schema are just plain old types, but two are special.

  1. Query: An entry point to get data
  2. Mutation: Entry to update data

You can visit the GraphQL website to learn more about these concepts.

OK, let’s define the schema. You need to create the GraphQL directory in the resources directory and create a schema.graphqls file in the GraphQL directory. (Make sure the file extension is.graphqls, which is a schema file)

If you have the GraphQL plugin installed in IntelliJ, you will see the GraphQL icon when you create the file. You can install the plug-in by searching in the plug-in panel. It is useful for GraphQL schema file development.

Here is the schema file I defined. First, I define the Query attribute, which is easy to understand. I’ll explain Mutation types later.

schema { query: Query, } type Query{ # Fetch All Users getAllUsers:[User] } type User { userId : ID! , firstName :String, lastName :String, dob:String, address:String, postId : Int, }Copy the code

Be sure to add these comments to the code line. In this way, you can view the description information when testing the server using third-party tools. (We will use the Altair client for most tests, not Postman.)

Now we need to define the parser. (If you don’t understand what getAllUsers is here (it’s not a method, it’s a field) and what it stands for, I’ll explain later)

A parser is a function field given a parent object, parameters, and execution context. They are responsible for returning the result of the corresponding function to this field.

There are several ways to implement parsers. Many large industry-level projects do this by creating a package called GraphQL in the root directory, where the parser interface and corresponding implementation are defined. The mapping types of requests and responses can also be defined in these packages.

Since this is to help you understand the concept, I implemented it in the project’s Service package.

package com.example.writeup.service;

import com.coxautodev.graphql.tools.GraphQLQueryResolver;
import com.example.writeup.model.User;
import com.example.writeup.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class UserService implements GraphQLQueryResolver {

    @Autowired
    private UserRepository userRepository;

    public List<User> getAllUsers(a) {
        returnuserRepository.findAll(); }}Copy the code

I create a new class called UserService that provides a concrete implementation of GraphQLQueryResolver, which is the interface in the GraphQL-Java-Tools library.

Then, to get the database connection, I automatically inject the UserRepository object into the UserService. (Of course, this injection method is not recommended for final release. You should click on the Autowired annotation, and a yellow icon will appear to the left of the code area. Clicking on the yellow icon will bring up the recommended practices, and then modify the code according to the recommended practices.)

Now you should think of the getALlUsers field in the GraphQL schema file. It is the same as the method name in the UserService class. So I define this method in this class and return a list of User objects as declared in the schema file.

Finally, we need to configure the GraphQL-related properties in application.properties.

#graphql properties
graphql.servlet.corsEnabled=true
graphql.servlet.mapping=/graphql
graphql.servlet.enabled=true
Copy the code

As you can see from the configuration, the/GraphQL endpoint can receive requests. So you don’t have to define the controller.

Everything is ready. Our server is waiting for access. To test it, you should see something like this: Started WriteupApplication… (JVM running…). .

Now continue testing. As I mentioned earlier, you can use the Altair client to test those endpoints. Altair has both a desktop version and a browser plug-in for testing. You can download and install the Altair client here.

We now use Altair to access the endpoint on the server.

If you reload the document section, you can see the annotated fields. Click on it for more details. Enter the query on the left, and you can see that Altair provides input completion. Click the Run query or send request button to get the query results.

Look how cool it is. Using GraphQL, we can get the information we need. I guess by now you should understand the concepts and be able to use them. Next, I’ll introduce the Mutation types.

As a first step, you can add a Mutation type field to the schema file. I have developed the ability to update addresses as an example. As shown below, I have added mutation to the schema file. Below is the complete schema file code.

schema { query: Query, mutation: Mutation, } type Query{ # Fetch All Users getAllUsers:[User] } type Mutation { # Update the user address updateUserAddress(userId:Int,address:String): User } type User { userId : ID! , firstName :String, lastName :String, dob:String, address:String, postId : Int, }Copy the code

In UserRepository, I added a query to update the user. The corresponding SQL statement takes input parameters, which are substituted by the method parameters.

package com.example.writeup.repository;

import com.example.writeup.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
public interface UserRepository extends JpaRepository<User.Integer> {

    @Transactional
    @Modifying
    @Query(value = "UPDATE user SET address = ? 1 WHERE user_id = ? 2 ", nativeQuery = true)
    int updateUserAddress(String address, Integer user_id);

}
Copy the code

In UserService, you need to implement the methods declared by the GraphQLMutationResolver interface. We also define the updateUserAdress method in this class. All methods have public access and no access restrictions. The code for the UserService class is as follows.

package com.example.writeup.service;

import com.coxautodev.graphql.tools.GraphQLMutationResolver;
import com.coxautodev.graphql.tools.GraphQLQueryResolver;
import com.example.writeup.model.User;
import com.example.writeup.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService implements GraphQLQueryResolver.GraphQLMutationResolver {

    @Autowired
    private UserRepository userRepository;

    public List<User> getAllUsers(a) {
        return userRepository.findAll();
    }

    public User updateUserAddress(Integer userId, String address) throws Exception {
        try {
            userRepository.updateUserAddress(address, userId);
            User user = userRepository.findById(userId).get();
            return user;
        } catch (Exception e) {
            throw newException(e); }}}Copy the code

All right. We also defined mutation. Now let’s test. You can check whether the function is implemented by checking the database.

Too good. You already know most of the concepts in GraphQL. But there’s a lot more to explore. Wait, I have something to add.

In addition to Query and Mutation, GraphQL also supports a type of operation called Subscriptions.

Similar to Query, Subscription provides the ability to Query data. Unlike Query, however, it maintains a connection to the GraphQL server (in layman’s terms, using a Web Socket). It provides the function of the server to actively push update messages.

Subscription is useful if you need to notify clients of background updates in real time, such as user notifications, important updates, file changes, and so on.

There are many more topics to discuss about using GraphQL, such as error handling, integration with Spring-Security, text validation, and more. I will also post some articles on these topics for your reference.

conclusion

This is a small demonstration project for the content of this article, and the full source code for the project is in the Github repository below.

Source: WriteUp Project source code.

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.