The project is hosted on GitHub: y0ngb1n/spring-boot-samples, feel free to Star, Fork 😘
GraphQL is both a query language for APIs and a runtime for fulfilling your data queries. GraphQL provides a complete description of the data in your API, allowing clients to request exactly the data they need, with no more and no less, making it easier for APIs to evolve over time and enabling powerful developer tools.
Define Schema#
# src/main/resources/schema.graphql
schema {
query: Query
}
type Query {
allBooks: [Book]
book(id: String): Book
}
type Book {
isbn: String
title: String
publisher: String
authors: [String]
publishedDate: String
}
Load and Parse the Defined Schema#
@Service
public class GraphQLService {
@Value("classpath:schema.graphql")
private Resource resource;
@Getter
private GraphQL graphQL;
@Autowired
private AllBooksDataFetcher allBooksDataFetcher;
@Autowired
private BookDataFetcher bookDataFetcher;
@PostConstruct
private void loadSchema() throws IOException {
// Get the locally defined Schema file
File schemaFile = resource.getFile();
// Parse the Schema file
TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(schemaFile);
RuntimeWiring wiring = buildRuntimeWiring();
GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(typeRegistry, wiring);
graphQL = GraphQL.newGraphQL(schema).build();
}
private RuntimeWiring buildRuntimeWiring() {
return RuntimeWiring.newRuntimeWiring()
.type("Query", typeWiring -> typeWiring
.dataFetcher("allBooks", allBooksDataFetcher)
.dataFetcher("book", bookDataFetcher)
).build();
}
}
Provide DataFetcher#
Equivalent to providing the implementation of Query in the Schema:
type Query {
allBooks: [Book]
book(id: String): Book
}
AllBooksDataFetcher
corresponds to the implementation of allBooks: [Book]
:
@Component
public class AllBooksDataFetcher implements DataFetcher<List<Book>> {
@Autowired
private BookRepository bookRepository;
@Override
public List<Book> get(DataFetchingEnvironment dataFetchingEnvironment) {
return bookRepository.findAll();
}
}
BookDataFetcher
corresponds to the implementation of book(id: String): Book
:
@Component
public class BookDataFetcher implements DataFetcher<Book> {
@Autowired
private BookRepository bookRepository;
@Override
public Book get(DataFetchingEnvironment dataFetchingEnvironment) {
String isn = dataFetchingEnvironment.getArgument("id");
return bookRepository.findById(isn).orElse(null);
}
}
Provide GraphQL API#
@RestController
@RequestMapping(path = "/v1/books")
public class BookController {
@Autowired
private GraphQLService graphQLService;
@PostMapping
public ResponseEntity<Object> getAllBooks(@RequestBody String query) {
ExecutionResult execute = graphQLService.getGraphQL().execute(query);
return new ResponseEntity<>(execute, HttpStatus.OK);
}
}
Start and Test#
$ mvn install
...
[INFO] BUILD SUCCESS
...
$ mvn spring-boot:run
...
2019-08-24 19:35:11.700 INFO 14464 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-08-24 19:35:11.702 INFO 14464 --- [ main] i.g.y.s.graphql.GraphQLApplication : Started GraphQLApplication in 16.808 seconds (JVM running for 25.601)
Query Partial Fields
$ curl -X POST \
http://127.0.0.1:8080/v1/books \
-H 'Content-Type: text/plain' \
-d '{
allBooks {
isbn
title
}
}' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 579 0 524 100 55 34933 3666 --:--:-- --:--:-- --:--:-- 38600
{
"errors": [],
"data": {
"allBooks": [
{
"isbn": "9787111213826",
"title": "Thinking in Java (4th Edition)"
},
...
]
},
"extensions": null,
"dataPresent": true
}
$ curl -X POST \
http://127.0.0.1:8080/v1/books \
-H 'Content-Type: text/plain' \
-d '{
book(id: "9787121362132") {
title
}
}' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 210 0 159 100 51 1691 542 --:--:-- --:--:-- --:--:-- 2234
{
"errors": [],
"data": {
"book": {
"title": "Highly Available and Scalable Microservice Architecture: Based on Dubbo, Spring Cloud, and Service Mesh"
}
},
"extensions": null,
"dataPresent": true
}
Query All Fields
$ curl -X POST \
http://127.0.0.1:8080/v1/books \
-H 'Content-Type: text/plain' \
-d '{
allBooks {
isbn
title
authors
publisher
publishedDate
}
}' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1139 0 1044 100 95 750 68 0:00:01 0:00:01 --:--:-- 818
{
"errors": [],
"data": {
"allBooks": [
{
"isbn": "9787111213826",
"title": "Thinking in Java (4th Edition)",
"authors": [
"Bruce Eckel"
],
"publisher": "Mechanical Industry Press",
"publishedDate": "2007-06-01"
},
...
]
},
"extensions": null,
"dataPresent": true
}
$ curl -X POST \
http://127.0.0.1:8080/v1/books \
-H 'Content-Type: text/plain' \
-d '{
book(id: "9787121362132") {
title
authors
publisher
publishedDate
}
}' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 421 0 320 100 101 312k 98k --:--:-- --:--:-- --:--:-- 411k
{
"errors": [],
"data": {
"book": {
"title": "Highly Available and Scalable Microservice Architecture: Based on Dubbo, Spring Cloud, and Service Mesh",
"authors": [
"Cheng Chao",
"Liang Guizhao",
"Qin Jinwei",
"Fang Zhibin",
"Zhang Yi",
"Du Qi",
"Yin Qi",
"Xiao Guanyu"
],
"publisher": "Electronic Industry Press",
"publishedDate": "2019-05-01"
}
},
"extensions": null,
"dataPresent": true
}
Query Multiple Data
$ curl -X POST \
http://127.0.0.1:8080/v1/books \
-H 'Content-Type: text/plain' \
-d '{
allBooks {
isbn
title
}
book(id: "9787121362132") {
title
authors
publisher
publishedDate
}
}' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 930 0 785 100 145 3866 714 --:--:-- --:--:-- --:--:-- 4581
{
"errors": [],
"data": {
"allBooks": [
{
"isbn": "9787111213826",
"title": "Thinking in Java (4th Edition)"
},
{
"isbn": "9787111421900",
"title": "Understanding the Java Virtual Machine: Advanced Features and Best Practices (2nd Edition)"
},
{
"isbn": "9787115221704",
"title": "Refactoring: Improving the Design of Existing Code (2nd Edition)"
},
{
"isbn": "9787121362132",
"title": "Highly Available and Scalable Microservice Architecture: Based on Dubbo, Spring Cloud, and Service Mesh"
},
{
"isbn": "9787302392644",
"title": "The Mythical Man-Month (40th Anniversary Chinese Edition)"
}
],
"book": {
"title": "Highly Available and Scalable Microservice Architecture: Based on Dubbo, Spring Cloud, and Service Mesh",
"authors": [
"Cheng Chao",
"Liang Guizhao",
"Qin Jinwei",
"Fang Zhibin",
"Zhang Yi",
"Du Qi",
"Yin Qi",
"Xiao Guanyu"
],
"publisher": "Electronic Industry Press",
"publishedDate": "2019-05-01"
}
},
"extensions": null,
"dataPresent": true
}
As can be seen, the API remains unchanged while only the query content is modified, and it automatically responds with different results.
Reference Links#
- https://graphql.org/
- https://developer.github.com/v4/
- https://www.baeldung.com/graphql
- https://www.baeldung.com/spring-graphql
- https://youtu.be/zX2I7-aIldE
- https://leader.js.cool/#/basic/db/graphql
- https://github.com/glennreyes/graphpack
- Comprehensive Analysis of GraphQL, Frontend and Backend Data Interaction Solutions in the Context of Ctrip Microservices
- Frontend Developers Understanding GraphQL, This Article is Enough
- The Road to GraphQL
- Discussing the History, Components, and Ecosystem of GraphQL