Въведение в GraphQL

1. Общ преглед

GraphQL е език за заявки, създаден от Facebook с цел изграждане на клиентски приложения, базирани на интуитивен и гъвкав синтаксис, за описание на техните изисквания за данни и взаимодействия.

Едно от основните предизвикателства при традиционните REST повиквания е неспособността на клиента да поиска персонализиран (ограничен или разширен) набор от данни. В повечето случаи, след като клиентът поиска информация от сървъра, той или получава всички, или нито едно от полетата.

Друга трудност е работата и поддържането на множество крайни точки. С нарастването на платформата броят им ще се увеличава. Поради това клиентите често трябва да искат данни от различни крайни точки.

Когато изграждате GraphQL сървър, е необходимо да имате само един URL адрес за всички извличане и мутиране на данни. По този начин клиентът може да поиска набор от данни, като изпрати низ за заявка, описващ какво иска, до сървър.

2. Основна номенклатура GraphQL

Нека да разгледаме основната терминология на GraphQL.

  • Заявка: е операция само за четене, поискана към GraphQL сървър
  • Мутация: е операция за четене-запис, поискана към GraphQL сървър
  • Преобразувател: В GraphQL г. резолвера- е отговорен за картографиране на операцията и кода, движещи се по гръб, което е отговорно за дръжка на искането. Той е аналогичен на MVC бекенд в приложение RESTFul
  • Тип: A Type определя формата на получените данни, които могат да бъдат върнати от сървъра GraphQL, включително области, които са краища на други видове
  • Вход: като тип, но определя формата на входните данни, които се изпращат към GraphQL сървър
  • Скалар: е примитивен тип , като String , Int , Boolean , Float и т.н.
  • Интерфейс: Интерфейсът ще съхранява имената на полетата и техните аргументи, така че GraphQL обектите могат да наследяват от него, осигурявайки използването на конкретни полета
  • Схема: В GraphQL схемата управлява заявки и мутации, като определя какво е позволено да се изпълнява в сървъра GraphQL

2.1. Зареждане на схемата

Има два начина за зареждане на схема в GraphQL сървър:

  1. с помощта на GraphQL's Interface Definition Language (IDL)
  2. като използвате един от поддържаните програмни езици

Нека да демонстрираме пример с помощта на IDL:

type User { firstName: String }

Сега, пример за дефиниция на схема с използване на Java код:

GraphQLObjectType userType = newObject() .name("User") .field(newFieldDefinition() .name("firstName") .type(GraphQLString)) .build();

3. Език за дефиниция на интерфейса

Interface Definition Language (IDL) или Schema Definition Language (SDL) е най-краткият начин за определяне на GraphQL схема. Синтаксисът е добре дефиниран и ще бъде приет в официалната спецификация на GraphQL.

Например, нека създадем схема на GraphQL за потребител / имейлите могат да бъдат посочени по следния начин:

schema { query: QueryType } enum Gender { MALE FEMALE } type User { id: String! firstName: String! lastName: String! createdAt: DateTime! age: Int! @default(value: 0) gender: [Gender]! emails: [Email!]! @relation(name: "Emails") } type Email { id: String! email: String! default: Int! @default(value: 0) user: User @relation(name: "Emails") }

4. GraphQL-java

GraphQL-java е реализация, базирана на спецификацията и референтната реализация на JavaScript. Имайте предвид, че той изисква поне Java 8, за да работи правилно.

4.1. Анотации на GraphQL-java

GraphQL също така дава възможност да се използват анотации на Java, за да се генерира дефиницията на схемата, без целия шаблон, създаден чрез използването на традиционния IDL подход.

4.2. Зависимости

За да създадем нашия пример, нека първо започнем да импортираме необходимата зависимост, която разчита на модула Graphql-java-annotations:

 com.graphql-java graphql-java-annotations 3.0.3 

Ние също така прилагаме HTTP библиотека, за да улесним настройката в нашето приложение. Ще използваме Ratpack (въпреки че може да бъде реализиран и с Vert.x, Spark, Dropwizard, Spring Boot и др.).

Нека също импортираме зависимостта Ratpack:

 io.ratpack ratpack-core 1.4.6 

4.3. Изпълнение

Нека създадем нашия пример: прост API, който предоставя „CRUDL“ (Създаване, извличане, актуализиране, изтриване и списък) за потребителите. Първо, нека създадем нашия потребител POJO:

@GraphQLName("user") public class User { @GraphQLField private Long id; @GraphQLField private String name; @GraphQLField private String email; // getters, setters, constructors, and helper methods omitted }

В това POJO можем да видим анотацията @GraphQLName (“потребител”) , като индикация, че този клас е картографиран от GraphQL заедно с всяко поле, анотирано с @GraphQLField.

След това ще създадем клас UserHandler . Този клас наследява избрания HTTP конектор библиотека (в нашия случай, Ratpack) метод манипулатор, който ще управлява и да се позове на GraphQL на преобразувател на функция. По този начин, пренасочването на заявката (JSON полезни товари) към правилната операция за заявка или мутация:

@Override public void handle(Context context) throws Exception { context.parse(Map.class) .then(payload -> { Map parameters = (Map) payload.get("parameters"); ExecutionResult executionResult = graphql .execute(payload.get(SchemaUtils.QUERY) .toString(), null, this, parameters); Map result = new LinkedHashMap(); if (executionResult.getErrors().isEmpty()) { result.put(SchemaUtils.DATA, executionResult.getData()); } else { result.put(SchemaUtils.ERRORS, executionResult.getErrors()); LOGGER.warning("Errors: " + executionResult.getErrors()); } context.render(json(result)); }); }

Сега класът, който ще поддържа операциите за заявка, т.е. UserQuery. Както споменахме, всички методи, които извличат данни от сървъра до клиента, се управляват от този клас:

@GraphQLName("query") public class UserQuery { @GraphQLField public static User retrieveUser( DataFetchingEnvironment env, @NotNull @GraphQLName("id") String id) { // return user } @GraphQLField public static List listUsers(DataFetchingEnvironment env) { // return list of users } }

Similarly to UserQuery, now we create UserMutation, which will manage all the operations that intend to change some given data stored on the server side:

@GraphQLName("mutation") public class UserMutation { @GraphQLField public static User createUser( DataFetchingEnvironment env, @NotNull @GraphQLName("name") String name, @NotNull @GraphQLName("email") String email) { //create user information } }

It is worth notice the annotations in both UserQuery and UserMutation classes: @GraphQLName(“query”) and @GraphQLName(“mutation”). Those annotations are used to define the query and mutation operations respectively.

With the GraphQL-java server able to run the query and mutation operations, we can use the following JSON payloads to test the request of the client against the server:

  • For the CREATE operation:
{ "query": "mutation($name: String! $email: String!){ createUser (name: $name email: $email) { id name email age } }", "parameters": { "name": "John", "email": "[email protected]" } } 

As the response from the server for this operation:

{ "data": { "createUser": { "id": 1, "name": "John", "email": "[email protected]" } } }
  • For the RETRIEVE operation:
{ "query": "query($id: String!){ retrieveUser (id: $id) {name email} }", "parameters": { "id": 1 } }

As the response from the server for this operation:

{ "data": { "retrieveUser": { "name": "John", "email": "[email protected]" } } }

GraphQL предоставя функции, които клиентът може да персонализира отговора. И така, при последната операция RETRIEVE, използвана като пример, вместо да върнем името и имейла, можем например да върнем само имейла:

{ "query": "query($id: String!){ retrieveUser (id: $id) {email} }", "parameters": { "id": 1 } }

И така, връщащата информация от GraphQL сървъра ще върне само исканите данни:

{ "data": { "retrieveUser": { "email": "[email protected]" } } }

5. Заключение

GraphQL е лесен и доста атрактивен начин за минимизиране на сложността между клиент / сървър като алтернативен подход към REST API.

Както винаги, примерът е достъпен в нашето хранилище на GitHub.