Главная » Хабрахабр » [Из песочницы] Простой сервер с GraphQL вместо REST, реализация на java

[Из песочницы] Простой сервер с GraphQL вместо REST, реализация на java


Мне предложили познакомиться с GraphQL. Посмотреть, можно ли применить в работе. Поискав я понял, что в основном информация на английском и частично старая, там 3 версия библиотеки, а уже 5 есть. Хочу восполнить этот пробел. В данном варианте будет пример на сервлетах, т.е. без spring и без spring-boot.
Теоретическая часть:
GraphQL — новый взгляд на API. Ч.1 от VladimirZaets
Сравнение REST и GraphQL от bevalorous

А если долго обсуждать абстракцию то можно потеряться. Сразу покажу код, потому что GraphQL это абстракция. Оригинал тут.

в новых версиях нет некоторых классов. Я немного изменил код, т.к.

В помник добавляем зависимости: Создаем пустой мавен проект.

<dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-java</artifactId> <version>8.0</version> </dependency> <dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-java-tools</artifactId> <version>5.0.0</version> </dependency> <dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-java-servlet</artifactId> <version>5.0.0</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <scope>provided</scope> </dependency>

Чтобы не задумываться про сервер, возьмем jetty:

<plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>9.4.6.v20170531</version> </plugin>

Отличия от туториала:

  1. Наследование от SimpleGraphQLServlet с вызовом конструктора теперь «deprecated», надо использовать builder, что при наследовании невозможно, используем композицию.
  2. В сервлете можно создать SimpleGraphQLServlet объект.
  3. GraphQLRootResolver – больше нет, можно использовать специфичные: GraphQLMutationResolver и GraphQLQueryResolver

Основа готова. Делать мы будем по туториалу, без спринга или JAX-RS. В общем обычный сервлет:

@WebServlet(urlPatterns = "/graphql")
public class GraphQLEndpoint extends HttpServlet private static GraphQLSchema buildSchema() { LinkRepository linkRepository = new LinkRepository(); return SchemaParser.newParser() .file("schema.graphqls") .resolvers(new Query(linkRepository), new Mutation(linkRepository)) .build() .makeExecutableSchema(); } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { graph.service(req, resp); }
}

В нем метод service передает данные в SimpleGraphQLServlet. Все на этом наше дело заканчивается.

Обычный код (дто Link и LinkRepository)

public class Link { private final String url; private final String description; public Link(String url, String description) { this.url = url; this.description = description; } public String getUrl() { return url; } public String getDescription() { return description; } }

public class LinkRepository { private final List<Link> links; public LinkRepository() { links = new ArrayList<>(); //add some links to start off with links.add(new Link("http://howtographql.com", "Your favorite GraphQL page")); links.add(new Link("http://graphql.org/learn/", "The official docks")); } public List<Link> getAllLinks() { return links; } public void saveLink(Link link) { links.add(link); }
}

Теперь код запросов (GET запросы в REST) и мутации (запросы для изменений)

import com.coxautodev.graphql.tools.GraphQLQueryResolver;
public class Query implements GraphQLQueryResolver { private final LinkRepository linkRepository; public Query(LinkRepository linkRepository) { this.linkRepository = linkRepository; } public List<Link> allLinks() { return linkRepository.getAllLinks(); }
}

import com.coxautodev.graphql.tools.GraphQLMutationResolver;
public class Mutation implements GraphQLMutationResolver { private final LinkRepository linkRepository; public Mutation(LinkRepository linkRepository) { this.linkRepository = linkRepository; } public Link createLink(String url, String description) { Link newLink = new Link(url, description); linkRepository.saveLink(newLink); return newLink; }
}

Запускаем через jetty:run и кидаем запрос:

http://localhost:8080/graphql?query={allLinks{url}}

Получаем ответ:

{"data": {"allLinks": [ {"url":"http://howtographql.com"}, {"url":"http://graphql.org/learn/"} ] }
}

И тут первый главный момент: дто у нас имеет 2 поля, url и description, а мы в ответ получили только url. И правильно ведь мы только url и попросили в запросе {allLinks{url}}. Если изменить запрос на такой:

http://localhost:8080/graphql?query={allLinks{url,description}}

ответ получаем такой:

{"data": {"allLinks": [ { "url":"http://howtographql.com", "description":"Your favorite GraphQL page" }, { "url":"http://graphql.org/learn/", "description":"The official docks" } ] }
}

Вот теперь мы получили url и description. А все потому, что мы попросили их.

Запрос на изменение данных.

Тут немного сложнее и гораздо проще использовать UI утилиту.

Идем по адресу
2. 1. Заменяем 2 строчки: Копируем весь файл index.html
3.

Эти:

<link rel="stylesheet" href="./node_modules/graphiql/graphiql.css" />
<script src="./node_modules/graphiql/graphiql.js"></script>

На эти:

<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/graphiql@0.11.2/graphiql.css" />
<script src="//cdn.jsdelivr.net/npm/graphiql@0.11.2/graphiql.js"></script>

Заменяем index.html в проекте ...\src\main\webapp\index.html на только что списанный. 4.

Перезапускаем проект, заходим на localhost:8080 и попадаем на такую страницу

Не сильно много, поля которые можно запросить и методы которые можно вызвать и что в них передать.
Первый бонус: слева кнопка Docs за которой скрывается уже готовая документация к вашему проекту.

Так же это дает любимые автокомплиты.

Мутация у нас одна, «добавить ссылку». Теперь с его помощью отправим запрос на мутацию.

запрос текстом:

mutation createLink{ createLink(url:"http://test.com", description:"test mutation"){ url description }
}

Чуть позже я узнал, что достаточно слова mutation (createLink после него не обязательно писать)

mutation { createLink(url:"http://test.com", description:"test mutation"){ url description }
}

Т.е. мы просто вызываем метод по его имени и передаем ему параметры. Так же можно предать какую информацию мы хотим получить обратно.

Как посмотреть запрос мутации

1. Открыть вкладку разработчика F12 и открыть network, отправляем запрос.

На отправленном запросе ПКМ -> copy -> copy as cURL(bash) 2.

1. 2. Для тех кто пользуется curl этого достаточно, для тех кто хочет посмотреть postman идем дальше

Открываем postman и слева сверху нажимаем import. 3.

В открывшемся окне выбираем Paste Raw Text и вставляем туда скопированный запрос curl 4.

И можем увидеть тело запроса:

{"query":"mutation createLink{\n createLink(url:\"http://test.com\", description:\"test mutation\"){\n\t\turl\n description\n }\n}","variables":null,"operationName":"createLink"}
Именно так с “\n”.

К данному этапу уже есть простой сервер, там есть хранилище, и мы выставили url для запросов.

Фронт может сам определить, какая информация ему на данный момент нужна, чем можно сэкономить трафик. Сама идея мне понравилась. При добавлении нового функционала старый продолжает работать, фронт продолжает получать только то, что нужно, ни больше, ни меньше.

Пример кода на github
2 ветки:

master — то, как сделано в официальном туториале

update_version — обновленная версия, с новыми версиями зависимостей.

Ссылки:

Доки
2. 1. Видео, которое дало первое понимание официальный туториал (для разных языков)
3.


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Как открыть ИП в Германии, если ты программист, и не набить шишек

В последние недели Берлин — популярная тема на Хабре. Не случайно — в Германии много возможностей для работы в ИТ с комфортным переездом. Но бюрократия она и в Африке бюрократия, а если встают вопросы об индивидуальном предпринимательстве за рубежом — ...

4 октября, Москва — Backend Stories 2.0

В четверг, 4 октября, на площадке Deworkacy (ул. Большая Полянка, д.2/10, стр 1) мы проведём митап для backend-разработчиков. 00, подробности — под катом.19:20–19:30, Павел Дерендяев, вступительное слово. Начинаем в 19. 30–20:10, Иван Походня, «Как мы хотели в Java 11 и ...