Что такое graphql

Problem They Solve

Imagine we’re implementing some kind of search functionality in our GraphQL schema. Our query would probably start by looking like this:


So far so good — but now we need to describe the fields to return. If our server could only search against one type of data, like users, we could simply leverage a list of plain-old User types:

But let’s say our server is pretty smart and can search multiple types of data! It’ll still pick up users whose name contains “cat”, but also surface information about books and movies. These don’t have usernames or friends, but instead have bespoke fields like “director” and “author.” How should our query look now?

We could return individual fields per object-type:

But we lose the ability to control sorting on the server — for example, my friend named Catherine should appear earlier than the movie Catwoman. Wouldn’t it be great if we could say, “Hey GraphQL, if the search result is a User, then here’s the data I want — but if it’s a Movie, then send this data.”

Welcome to Interfaces and Unions! When the value for a field can be multiple types of data, you can use fragments with interfaces or unions to perform a sort of switch statement per-type:

The above query uses to express the “if” conditions we want. Note that this query doesn’t have an explicit mention of whether or not searchResult returns a union or interface — in either case we’d use the same fragments, and our GraphQL tooling would detect whether the query was valid or not.

Implemented with Unions, our GraphQL server schema might look something like this:

Or using an Interface, the GraphQL schema may look more like this:

Notice how now each of the types has a searchPreviewText in order to implement the Searchable interface. Our original query would still work, but it means we can have this sort of concise query and still be valid:

Списки и обязательные значения¶

Типы объектов, скаляры и (перечисления) — не единственные виды типов, которые вы можете определить в GraphQL. Но когда вы используете типы в других частях схемы, или в определенях переменных вашего запроса, вы можете применить дополнительные модификаторы типов, которые влияют на проверку этих значений. Взглянем на пример:

Здесь, мы используем тип и делаем его , добавляя восклицательный знак после имени типа. Это значит, что наш сервер всегда ожидает вернуть непустое значение этого поля, а если вернет, это вызовет ошибку исключения в GraphQL.

Модификатор типа может быть так же использован, когда определяются аргументы поля, что побуждает сервер GraphQL возвращать ошибку проверки на переданного аргумента.

Request

Переменные

Response

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

Модификаторы and могут комбинироваться. Например, вы можете иметь с со значениями :

Это значит, что список сам по себе может быть , но не может иметь члены. Например, в JSON:


Теперь, скажем, мы определим Non-Null List of Strings:

Это значит, что список сам по себе не может быть , но может содержать значения:

Вы можете произвольно разместить любое количество модификаторов и , в соответствии с вашими нуждами.

Dependency container

Now open up and set up a . inside the array parameter.

\Doctrine\DBAL\Connection::class => function (ContainerInterface $c) {    $settings = $c->get('settings');    $connectionParams = ,        'user'     => $settings,        'password' => $settings,        'host'     => $settings,        'driver'   => $settings,    ];    $connection = \Doctrine\DBAL\DriverManager::getConnection($connectionParams);    # Set up SQL logging    $connection->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\DebugStack());    return $connection;}

We receive the settings from the . After that we set up DBAL, but notice that we are also setting up which allows us to track what queries get executed. That’s pretty neat when you wanna check how many queries GraphQL executes when resolving fields.

Now we can inject the to any class constructor that we set up in our Routes,

Setting up GraphQL

Now, Slim already sets us up with a nice project structure but we won’t be touching existing files under . You can explore the skeleton to figure out what it does but we’ll just be adding our own route with a and GraphQL files.

Сравнение GraphQL API и REST API

  • Зависимость от протокола передачи данных. GraphQL не зависит от протокола передачи данных, может использовать любой (http, ws, ssh, cli, etc.) REST базируется на http протоколе, и зависит от него.
  • Единая точка входа. ( Entry point ) В GraphQL для работы с данными мы всегда обращаемся к единой точке входа — GraphQL серверу. Изменяя структуру, поля, параметры запроса мы работаем с разными данными. В REST API каждый путь (route) представляет собой отдельную точку входа.
  • Возможность возвращать разные форматы данных. GraphQL может возвращать только JSON формат. REST в данном случае более гибкий. REST API может возвращать данные в различных форматах — JSON, XML и т.д., это зависит от заголовков http запроса, и от самой имплементации API.
  • Декларация, документация, инструменты разработки GraphQL дает возможность написания документации непосредственно в коде (inline documentation). В GraphQL мы можем описать любой созданный нами тип. Для этого при создании типа нужно в поле «description» описать, для чего данный тип нужен. Эту документацию умеют парсить различные утилиты, IDE, что очень упрощает работу с GraphQL. Также, GraphQL из коробки имеет свой IDE, который работает в браузере и называется GraphiQL. GraphiQL хранит историю запросов, подсвечивает синтаксис, подсказывает поля, которые мы можем запросить у текущего типа, парсит документацию. Это заметно упрощает написание запросов. GraphiQL можно включить через конфигурацию GraphQL, с помощью настройки graphiql = true и перейдя по пути <имя домена>/graphiql. Либо же можно использовать одно из расширений для браузера, которые имеют практически одинаковую функциональность но разный UI. REST не имеет подобной функциональности, но есть возможность её реализовать используя SWAGGER.
  • Возможность формировать структуру и объем данных на клиенте Cобственно, это одна из основных фичей GraphQL, формат и структура данных определяется на стороне клиента. В REST формат и структура данных жестко определены на сервере.
  • Передача аргументов в запрос Делая вывод из предыдущих пунктов, вы наверняка уже знаете ответ, за счет того что GraphQL — это единая точка входа то в GraphQL, мы имеем возможность передать аргументы на любой уровень вложенности. В REST каждый путь представлен как отдельная точка входа, в таком случае передать аргументы мы можем только для всего запроса.

Nullability¶

Большинство типов систем, которые распознают обеспечивает как общий тип, и обнуляемого вариант этого типа, где по типам по умолчанию не включают , если это явно не заявлено. Однако в системе типа GraphQL, каждое поле обнуляется по умолчанию. Это происходит потому, что есть много вещей, которые могут пойти наперекосяк в сетевой службе при поддержке баз данных и других услуг. База данных может упасть, асинхронное действие может потерпеть неудачу, может быть выброшено исключение. Помимо просто системных сбоев, разрешение может часто быть зернистое, где отдельные поля в запросе могут иметь разные правила авторизации.

По недобросовестности каждое поле к , по любой из этих причин может привести к полю, возвращенному в , вместо того, чтобы провалить запрос. Вместо этого GraphQL обеспечивает варианты типов, которые делают гарантию клиентам, что, если требуется, то поле не будет возвращать . Вместо того, чтобы, в случае возникновения ошибки, предыдущее родительское поле будет «нулевым» вместо этого.

При разработке схемы GraphQL, важно иметь в виду все те проблемы, которые могли бы пойти не так, и если «нулевой» является соответствующее значение для неудачного поля. Как правило, это так, но время от времени, это не так

В этих случаях используют ненулевые типы, чтобы обеспечить эту гарантию.

Alternatively use Standard Server

You can opt to use the GraphQL instead of which gives you more features out of the box, including query batching.

The standard server accepts PSR-7 request and response items, which Slim complies with! So this is rather simple. Replace the executeQuery() line with:

# Create server configuration$config = \GraphQL\Server\ServerConfig::create()    ->setSchema($schema)    ->setContext($context)    ->setQueryBatching(true);# Allow GraphQL Server to handle the request and response$server = new \GraphQL\Server\StandardServer($config);$response = $server->processPsrRequest($request, $response, $response->getBody());

Possible alternatives

There are other wrappers on top of Webonyx in the PHP land.

Let’s cite a few of them:

  • API Platform is a Symfony only bundle that let’s you build on API. It supports both REST and GraphQL. It comes with its own set of concepts («resources», «data providers»…) and is really one layer of abstraction above GraphQLite. My understanding is that it is a great fit if your API is «resource» oriented, or if you want both REST and GraphQL endpoints. GraphQLite is a thinner layer, closer to the GraphQL spec.
  • overblog/GraphQLBundle is a Symfony only bundle that wraps Webonyx’s library too. Compared to GraphQLite, the schema is declared using YAML configuration files (instead of annotations).
  • laravel/GraphQL: a very thin layer above Webonyx
  • Laravel Lighthouse: a «Schema first» Laravel library that uses Type Language and decorators to implement the schema

Advanced Scalar Data Types

Very soon after playing with GraphQL, you will discover that SDL provides only primitive data types, and advanced scalar data types such as Date, Time, and DateTime, which are an important part of every API, are missing. Fortunately, we have a library which helps us to solve this problem, and it’s called graphql-iso-date. After installing it, we will need to define new advanced scalar data types in our schema and connect them to the implementations provided by the library:

Along with date and time, there also exists other interesting scalar data type implementations, which can be useful for you depending on your use case. For example, one of them is graphql-type-json, which gives us the ability to use dynamic typing in our GraphQL schema and pass or return untyped JSON objects using our API. There also exists library graphql-scalar, which gives us the ability to to define custom GraphQL scalars with advanced sanitization/validation/transformation.

If needed, you can also define your custom scalar data type and use it in your schema, as shown above. This isn’t difficult, but discussion of it is outside the scope of this article—if interested, you can find more advanced information in the Apollo documentation.

Splitting Schema

After adding more functionality to your schema, it will start growing and we will understand that it’s impossible to keep the whole set of definitions in one file, and we need to split it up into small pieces to organize the code and make it more scalable for a bigger size. Fortunately schema builder function , provided by Apollo, also accepts schema definitions and resolvers maps in the form of an array. This gives us the ability to split our schema and resolvers map into smaller parts. This is exactly what I have done in my sample project; I have divided the API into the following parts:

  • – API for user authentication and registration
  • – CRUD API for author entries
  • – CRUD API for book entries
  • – Root of schema and common definitions (like advanced scalar types)
  • – CRUD API for user management

During the splitting schema, there is the one thing we must consider. One of the parts must be the root schema and the other ones must extend the root schema. This sounds complex, but in reality it’s quite simple. In the root schema, queries and mutations are defined like this:

And in the other ones, they are defined like this:

And that’s all.

Posts

  • Using DataLoader to batch GraphQL requests
  • Introducing Relay and GraphQL
  • GraphQL Introduction
  • Unofficial Relay FAQ
  • Your First GraphQL Server
  • GraphQL Overview — Getting Started with GraphQL and Node.js
  • 4 Reasons you should try out GraphQL
  • Moving from REST to GraphQL
  • Writing a Basic API with GraphQL
  • Building a GraphQL Server with Node.js and SQL
  • GraphQL at The Financial Times
  • Relay for visual learners
  • Relay and Routing
  • Learn Golang + GraphQL + Relay, Part 1: Your first Golang GraphQL server
  • Learn Golang + GraphQL + Relay, Part 2: Your first Relay application
  • From REST to GraphQL
  • GraphQL: A data query language
  • Subscriptions in GraphQL and Relay
  • Relay 101: Building A Hacker News Client
  • GraphQL Shorthand Notation Cheatsheet
  • The GitHub GraphQL API
  • Github GraphQL API React Example
  • Testing a GraphQL Server using Jest
  • How to implement viewerCanSee in GraphQL
  • Create an infinite loading list with React and GraphQL
  • REST vs GraphQL
  • Authentication and Authorization for GraphQL APIs
  • Build a GraphQL API with Siler on top of Swoole
  • Fluent GraphQL clients: how to write queries like a boss

Controller

First, create a file . This will be the class with a method our router will call when a request comes to

Let’s set it up with the following base code:

<?phpnamespace App\Application\Controllers;use Doctrine\DBAL\Connection;use Psr\Http\Message\ResponseInterface as Response;use Psr\Http\Message\ServerRequestInterface as Request;use Psr\Log\LoggerInterface;class GraphQLController{    protected $db;    protected $logger;    public function __construct(Connection $connection, LoggerInterface $logger)    {        $this->db = $connection;        $this->logger = $logger;    }    public function index(Request $request, Response $response) {}}

Let’s break this down a little. This is a that has a method called and a constructor. The constructor can have dependencies injected to it which are set up in the file and are stored inside the .

In here we are injecting the instance but a as well. You might’ve notice that inside the file. That’s Monolog. I won’t be going into detail about it but it’s a very nifty tool for logging. We will be using it to log our queries.

Now let’s make the app router call the method whenever a HTTP call is made to our GraphQL.

Tools

  • graphiql — An in-browser IDE for exploring GraphQL.
  • GraphiQL.app — A light, Electron-based wrapper around GraphiQL.
  • GraphQLviz — GraphQLviz marries GraphQL (schemas) with Graphviz.
  • graphqlviz — GraphQL API visualizer in Node.js
  • GraphQL AST Explorer — Explore the AST of a GraphQL document interactively
  • GraphQLHub — Query public API’s schemas (e.g. Reddit, Twitter, Github, etc) using GraphiQL
  • js-graphql-intellij-plugin — GraphQL language support for IntelliJ IDEA and WebStorm, including Relay.QL tagged templates in JavaScript and TypeScript.
  • gdom — DOM Traversing and Scraping using GraphQL.
  • Annotated GraphQL Server — Server for annotated GraphQL showing how to transform a REST api into a GraphQL endpoint with annotations.
  • Model Visualizer — A small webapp that generates an ERD-like visualization of a GraphQL endpoint from an introspection query.
  • GraphQL Network — A chrome dev-tools extension for debugging GraphQL network requests.
  • eslint-plugin-graphql — An ESLint plugin that checks your GraphQL strings against a schema.
  • AST Explorer — Select «GraphQL» at the top, explore the GraphQL AST and highlight different parts by clicking in the query.
  • vim-graphql — A Vim plugin that provides GraphQL file detection and syntax highlighting.
  • GraphQL CMS — Use your existing GraphQL schema to generate simple for use, fully functional CMS in a couple steps.
  • graphdoc — Static page generator for documenting GraphQL Schema.
  • graphql-autocomplete — Autocomplete and lint from a GraphQL endpoint in Atom.
  • GraphQL Code Generator — GraphQL code generator based on schema and documents.
  • GraphQL IDE — An extensive IDE for exploring GraphQL API’s.
  • Swagger to GraphQL — GraphQL types builder based on REST API described in Swagger. Allows to migrate to GraphQL from REST for 5 minutes
  • GraphQL Voyager — Represent any GraphQL API as an interactive graph.
  • GraphQL Docs — Instantly create beautiful GraphQL API docs hosted online.
  • GraphQL Faker — Mock or extend your GraphQL API with faked data. No coding required.
  • ts-graphql-plugin — A language service plugin complete and validate GraphQL query in TypeScript template strings.
  • Apollo Launchpad — Like JSFiddle for GraphQL server code, write and deploy a GraphQL API directly from your browser.
  • Apollo Tracing — GraphQL extension that enables you to easily get resolver-level performance information as part of a GraphQL response.
  • Altair GraphQL Client — A beautiful feature-rich GraphQL Client for all platforms.
  • Apollo Storybook Decorator — Wrap your React Storybook stories with Apollo Client, provide mocks for isolated UI testing with GraphQL
  • GraphQL Metrics — instrument GraphQL resolvers, logging response times and statuses (if there was an error or not) to the console as well as to InfluxDB.
  • GraphQL Rover — GraphQL schema interactive navigation, rearrange nodes, search and explore types and fields.
  • json-graphql-server — Get a full fake GraphQL API with zero coding in less than 30 seconds, based on a JSON data file.
  • Insomnia — An full-featured API client with first-party GraphQL query editor
  • Prisma — Turn your database into a GraphQL API. Prisma lets you design your data model and have a production ready GraphQL API online in minutes.
  • tuql — Automatically create a GraphQL server from any sqlite database.
  • Bit — Organize GraphQL API as components to be consumed with NPM or modified from any project (example).
  • RAN Toolkit — Production-ready toolkit/boilerplate with support for GraphQL, SSR, Hot-reload, CSS-in-JS, caching, and more.
  • Apollo GraphQL VSCode Extension — Rich editor support for GraphQL client and server development that seamlessly integrates with the Apollo platform
  • GRAPHQL DESIGNER — A developer’s web-app tool to rapidly prototype a full stack CRUD implementation of GraphQL with React.
  • GraphQL Inspector — Tooling for GraphQL. Compare schemas, validate documents, find breaking changes, find similar types, schema coverage.
  • graphql-typed-client — A tool that generates a strongly typed client library for any GraphQL endpoint. The client allows writing GraphQL queries as plain JS objects (with type safety and awesome code completion experience)
  • OpenAPI-to-GraphQL — Take any OpenAPI Specification (OAS) or swagger and create a GraphQL interface — Two minute video and resources here
  • SwitchQL — Automated transcription of database schemas into GraphQL schemas, resolvers, queries, and mutations
  • VulcanJS — The full-stack React+GraphQL framework
  • Apollo Client Developer Tools — GraphQL debugging tools for Apollo Client in the Chrome developer console
  • GraphQL Birdseye – View any GraphQL schema as a dynamic and interactive graph.

Creating a Basic GraphQL Server

To run a basic GraphQL server, we must create a new project, initialize it with npm, and configure Babel. To configure Babel, first install the required libraries with the following command:

After installing Babel, create a file with the name in our project’s root directory and copy the following configuration there:

Also edit the file and add the following command to the section:

Once we have configured Babel, install the required GraphQL libraries with the following command:

After installing the required libraries, to run a GraphQL server with minimal setup, copy this code snippet in our file:

After this, we can run our server using the command , and if we navigate in a web browser to the URL , GraphQL’s interactive visual shell, called Playground, will open, where we can execute GraphQL queries and mutations and see the result data.

In the GraphQL world, API functions are divided into three sets, called queries, mutations, and subscriptions:

  • Queries are used by the client to request the data it needs from the server.
  • Mutations are used by the client to create/update/delete data on the server.
  • Subscriptions are used by the client to create and maintain real-time connection to the server. This enables the client to get events from the server and act accordingly.

In our article, we will discuss only queries and mutations. Subscriptions are a huge topic—they deserve their own article and are not required in every API implementation.

Hands on GraphQL Tutorial

As outlined earlier, we’ll be building a skeleton idea for GraphQL API, and you will need to know the basics of Node.js and Express before proceeding. The source code of the project made for this GraphQL example is available here.

We are going to handle two types of resources:

  • Users, for which we will handle a basic CRUD.
  • Products, for which we will have a bit of detail to show more of the power of GraphQL.

The users will contain the following structure:

  • id
  • firstname
  • lastname
  • email
  • password
  • permissionLevel

The products will contain the following structure:

  • id
  • name
  • description
  • price

As for the coding standard, we are going to use TypeScript for this project. In the source file, you will be able to configure everything to start coding with TypeScript.

Перечисляемые типы¶

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

  • Проверять, что любые аргументы этого типа содержат разрешенное значение
  • Сообщить через систему типов, что поле будет всегда содержать одно из конечного множества значений

Вот как может выглядеть определение enum в GraphQL schema language:

Это значит, что где бы мы не использовали тип в нашей схеме, мы будем ожидать значение , , или .

Отметим, что имплементации сервиса GraphQL в различные языки будет иметь их собственные, зависящие от языка, пути использования . В языках, которые поддерживают как объект первого класса, имплементация может получить его преимущества; в языке вроде JavaScript без поддержки , эти значения могут быть связаны с набором чисел (массив). Однако, эти делатили не видны клиенту, который может работать полностью в терминах строковых имен значений перечисления.

Authentication and Authorization

In the majority of API implementations, there is a requirement to restrict global access and provide some kind of rule-based access policies. For this we have to introduce in our code: Authentication—to confirm user identity—and Authorization, to enforce rule-based access policies.

In the GraphQL world, like the REST world, generally for authentication we use JSON Web Token. To validate the passed JWT token, we need to intercept all incoming requests and check the authorization header on them. For this, during the creation of Apollo server, we can register a function as a context hook, which will be called with the current request that creates the context shared across all resolvers. This can be done like this:

Here, if the user will pass a correct JWT token, we verify it and store the user object in context, which will be accessible for all resolvers during the request execution.

We verified user identity, but our API is still globally accessible and nothing prevents our users from calling it without authorization. One way to prevent this is to check the user object in context directly in every resolver, but this is a very error-prone approach because we have to write a lot of boilerplate code and we can forget to add the check when adding a new resolver. If we take a look at REST API frameworks, generally such kinds of problems are solved using HTTP request interceptors, but in the case of GraphQL, it doesn’t make sense because one HTTP request can contain multiple GraphQL queries, and if we still add it, we get access only to the raw string representation of the query and have to parse it manually, which definitely isn’t a good approach. This concept doesn’t translate well from REST to GraphQL.

So we need some kind of way to intercept GraphQL queries, and this way is called prisma-graphql-middleware. This library lets us run arbitrary code before or after a resolver is invoked. It improves our code structure by enabling code reuse and a clear separation of concerns.

The GraphQL community has already created a bunch of awesome middleware based on Prisma middleware library, which solves some specific use cases, and for user authorization, there exists a library called graphql-shield, which helps us to create a permission layer for our API.

After installing graphql-shield, we can introduce a permission layer for our API like this:

And we can apply this layer as middleware to our schema like this:

Here when creating a shield object, we set to true, because by default, the shield’s behavior is to catch and handle errors which occur inside resolvers, and this wasn’t acceptable behavior for my sample application.

In the example above, we only restricted access to our API for authenticated users, but the shield is very flexible and, using it, we can implement a very rich authorization schema for our users. For example, in our sample application, we have two roles: and , and only users with the role can call user administration functionality. This is implemented like this:

One more thing I want to mention is how to organize middleware functions in our project. As with schema definitions and resolvers maps, it’s better to split them per schema and keep in separate files, but unlike Apollo server, which accepts arrays of schema definitions and resolvers maps and stitches them for us, Prisma middleware library doesn’t do this and accepts only one middleware map object, so if we split them we have to stitch them back manually. To see my solution for this problem, please see the class in the sample project.


С этим читают