GET
or POST
requests. While GraphQL, in general, introduces some new concepts regarding data organization and interaction, the internal mechanisms of this technology rely on good old HTTP requests.GET
request to the endpoint /api/v1/flavors
it is expected that it will send a response that looks something like this: [ { "id": 1, "name": "The Lazy Person's Movie Theater", "description": "That elusive flavor that you begrudgingly carted yourself to the theater for, now in the comfort of your own home, you slob!" }, { "id": 2, "name": "What's Wrong With You Caramel", "description": "You're a crazy person that likes sweet popcorn. Congratulations." }, { "id": 3, "name": "Gnarly Chili Lime", "description": "The kind of popcorn you make when you need a good smack in the face."} ]
description
field, but are we going to sit back and pretend that we did not send this field to the client? And what else can we do? And when we, after a few months, are asked about why the application is so slow for users, we will only have to give the manager and no longer meet with the management of the company for which we have made this application.GET
or POST
requests to transfer data to the client and receive it from it. If we consider this idea in more detail, it turns out that there are two types of queries in GraphQL. The first type includes requests for reading data, which in GraphQL terminology are simply called queries and refer to the letter R (reading, reading) of the acronym CRUD. Requests of the second type are data change requests, which in GraphQL are called mutations. They refer to the books C, U, and D of the CRUD acronym, that is, they use them to create (create), update (update) and delete (delete) entries.https://myapp.com/graphql
, in the form of a GET
or POST
request. We'll talk more about this below. { flavors { name } }
{ // , . }
flavors
field. { flavors { // , flavor. } }
{ flavors { name } }
{ "data": { "flavors": [ { "name": "The Lazy Person's Movie Theater" }, { "name": "What's Wrong With You Caramel" }, { "name": "Gnarly Chili Lime" } ] } }
{ flavors { id name description } }
{ "data": { "flavors": [ { "id": 1, "name": "The Lazy Person's Movie Theater", description: "That elusive flavor that you begrudgingly carted yourself to the theater for, now in the comfort of your own home, you slob!" }, { "id": 2, "name": "What's Wrong With You Caramel", description: "You're a crazy person that likes sweet popcorn. Congratulations." }, { "id": 3, "name": "Gnarly Chili Lime", description: "A friend told me this would taste good. It didn't. It burned my kernels. I haven't had the heart to tell him." } ] } }
flavor
, then we can take advantage of the fact that GraphQL can work with arguments: { flavors(id: "1") { id name description } }
id
) of the object, information about which we need, but in such cases we can use dynamic identifiers: query getFlavor($id: ID) { flavors(id: $id) { id name description } }
getFlavor
can replace getFlavor
with something like pizza
, and the request remains operable) and declare the variables that the request expects. In this case, it is assumed that the request will be given an identifier ( id
) of a scalar type ID
(we will talk about types below).id
used when executing a query, this is how the response to such a query will look like: { "data": { "flavors": [ { "id": 1, "name": "The Lazy Person's Movie Theater", description: "That elusive flavor that you begrudgingly carted yourself to the theater for, now in the comfort of your own home, you slob!" } ] } }
nutrition
that contains information about the nutritional value of different types of popcorn: { flavors { id name nutrition { calories fat sodium } } }
flavor
object will contain an embedded nutrition
object. But it is not so. Using GraphQL, you can combine calls to independent, but related data sources in a single query, which allows you to get answers that make it easy to work with nested data without the need to denormalize the database: { "data": { "flavors": [ { "id": 1, "name": "The Lazy Person's Movie Theater", "nutrition": { "calories": 500, "fat": 12, "sodium": 1000 } }, ... ] } }
mutation updateFlavor($id: ID!, $name: String, $description: String) { updateFlavor(id: $id, name: $name, description: $description) { id name description } }
updateFlavor
mutation, specifying some variables - id
, name
and description
. Acting on the same scheme that is used in the description of requests, we “decorate” the variable fields (root mutation) using the keyword mutation
, followed by the name describing the mutation, and the set of variables that are needed to form the corresponding data change request.id
, name
and description
fields. This can come in handy when developing something like optimistic interfaces, eliminating the need to fulfill a request to get changed data after changing it. import express from 'express' import graphqlHTTP from 'express-graphql' import schema from './schema' const app = express() app.use('/graphql', graphqlHTTP({ schema: schema, graphiql: true })) app.listen(4000)
graphqlHTTP
function from the express-graphql
from Facebook, we “attach” the scheme (assuming that it is described in a separate file) and start the server on port 4000. That is, clients, if we talk about local use of this server, will be able to send requests over address http://localhost:4000/graphql
. import gql from 'graphql-tag' import mongodb from '/path/to/mongodb' // - . , `mongodb` MongoDB. const schema = { typeDefs: gql` type Nutrition { flavorId: ID calories: Int fat: Int sodium: Int } type Flavor { id: ID name: String description: String nutrition: Nutrition } type Query { flavors(id: ID): [Flavor] } type Mutation { updateFlavor(id: ID!, name: String, description: String): Flavor } `, resolvers: { Query: { flavors: (parent, args) => { // , args , { id: '1' } return mongodb.collection('flavors').find(args).toArray() }, }, Mutation: { updateFlavor: (parent, args) => { // , args { id: '1', name: 'Movie Theater Clone', description: 'Bring the movie theater taste home!' } // . mongodb.collection('flavors').update(args) // flavor . return mongodb.collection('flavors').findOne(args.id) }, }, Flavor: { nutrition: (parent) => { return mongodb.collection('nutrition').findOne({ flavorId: parent.id, }) } }, }, } export default schema
typeDefs
) and resolvers ( resolver
). The typeDefs
contains type declarations for the data used in the application. For example, earlier we talked about a request to get a list of flavor
objects from the server. In order for our server to perform a similar request, you need to do the following three steps:flavor
objects look (in the example above, this looks like a type declaration of type Flavor
).type Query
field type Query
(this is the flavors
property of type Query
).resolvers.Query
function-resolvers.Query object, written in accordance with the fields declared in the type Query
root field.typeDefs
. Here we give the scheme information about the shape (shape) of our data. In other words, we are telling GraphQL about various properties that may be contained in entities of the corresponding type. type Flavor { id: ID name: String description: String nutrition: Nutrition }
type Flavor
declaration indicates that the flavor
object may contain an id
field of type ID
, a name
field of type String
, a description
field of type String
and a nutrition
field of type Nutrition
.nutrition
we use the name of another type declared in typeDefs
. Here, the type Nutrition
design describes the nutritional form of popcorn.type Flavor
declaration, here we specify the names of the fields that will be contained in the nutrition
objects, using, as the data types of these fields (properties), what in GraphQL is called scalar data types. At the time of this writing, GraphQL supported 5 built-in scalar data types :Int
: 32-bit integer with a sign.Float
: A double-precision floating-point number with a sign.String
: a sequence of characters encoded in UTF-8.Boolean
: boolean true
or false
.ID
: a unique identifier often used to load objects multiple times or as a key in the cache. Values ​​of type ID
serialized in the same way as strings, but the indication that an ID
type has a certain value underlines the fact that this value is not intended to be shown to people, but for use in programs.nutrition
property described in the type Flavor
design, the Nutrition
type. type Query { flavors(id: ID): [Flavor] }
type Query
construction, which describes the root type of the Query
(the “root query” that we talked about earlier), we declare the name of the field that can be queried. When declaring this field, we, along with the data type that we expect to return, specify the arguments that can be received in the request.id
argument of a scalar type ID
. In response to such a request, an array of objects is expected whose device resembles a Flavor
device.type Query
has a definition of a field
, we need to describe what is called a resolver function.resolvers
, Query
, , flavors
, . flavors
, type Query
. typeDefs: gql`…`, resolvers: { Query: { flavors: (parent, args) => { // , args { id: '1' } return mongodb.collection('flavors').find(args).toArray() }, }, … },
parent
— , , args
, . context
, . «» ( — , ).flavors
MongoDB, args
( ) .find()
, , .nutrition
. , , Nutrition
, , , , flavor
. , / .type Flavor
nutrition
type Nutrition
, . , , flavor
. typeDefs: gql` type Nutrition { flavorId: ID calories: Int fat: Int sodium: Int } type Flavor { […] nutrition: Nutrition } type Query {…} type Mutation {…} `, resolvers: { Query: { flavors: (parent, args) => {…}, }, Mutation: {…}, Flavor: { nutrition: (parent) => { return mongodb.collection('nutrition').findOne({ flavorId: parent.id, }) } }, },
resolvers
, , Query
, Mutation
Flavor
. , typeDefs
.Flavors
, , nutrition
-. , Flavor
. , : « , nutrition
, type Flavor
».parent
, -. , parent
, , , flavors
. , flavor
, : { flavors { id name nutrition { calories } } }
flavor
, flavors
, nutrition
, parent
. , , , MongoDB, parent.id
, id
flavor
, .parent.id
, nutrition
flavorId
, flavor
.type Mutation
, , updateFlavor
, , . type Mutation { updateFlavor(id: ID!, name: String, description: String): Flavor }
updateFlavor
id
ID
( , !
, GraphQL , ), name
String
description
String
». , , Flavor
( — , id
, name
, description
, , , nutrition
). { typeDefs: gql`…`, resolvers: { Mutation: { updateFlavor: (parent, args) => { // , args { id: '1', name: 'Movie Theater Clone', description: 'Bring the movie theater taste home!' } // . mongodb.collection('flavors').update( { id: args.id }, { $set: { ...args, }, }, ) // flavor . return mongodb.collection('flavors').findOne(args.id) }, }, }, }
updateFlavor
, : , , — , flavor
.flavor
. Why is this so?flavor
, .args
? , . , , , 100% , . , , , , , .graphql
( express-graphql
, ) — . , GraphQL - . , -, , , , .Source: https://habr.com/ru/post/445268/
All Articles