Skip to content

Commit ecd6e6b

Browse files
author
Kevin Lacker
authored
Merge pull request graphql#85 from lacker/fix-mutations
fix missing return types
2 parents ebd8a0b + a4e775c commit ecd6e6b

File tree

1 file changed

+69
-32
lines changed

1 file changed

+69
-32
lines changed

site/graphql-js/Tutorial-Mutations.md

Lines changed: 69 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,28 @@ next: /graphql-js/authentication-and-express-middleware/
88

99
If you have an API endpoint that alters data, like inserting data into a database or altering data already in a database, you should make this endpoint a `Mutation` rather than a `Query`. This is a simple as making the API endpoint part of the top-level `Mutation` type instead of the top-level `Query` type.
1010

11-
Let's say we have a “message of the day” server, where anyone can update the message of the day along with the author of the message, and anyone can read the current one. The GraphQL schema for this is simply:
11+
Let's say we have a “message of the day” server, where anyone can update the message of the day, and anyone can read the current one. The GraphQL schema for this is simply:
1212

1313
```javascript
1414
type Mutation {
15-
setMessage(message: String)
15+
setMessage(message: String): String
1616
}
1717

1818
type Query {
1919
getMessage: String
2020
}
2121
```
2222

23+
It's often convenient to have a mutation that maps to a database create or update operation, like `setMessage`, return the same thing that the server stored. That way, if you modify the data on the server, the client can learn about those modifications.
24+
2325
Both mutations and queries can be handled by root resolvers, so the root that implements this schema can simply be:
2426

2527
```javascript
2628
var fakeDatabase = {};
2729
var root = {
2830
setMessage: function ({message}) {
2931
fakeDatabase.message = message;
32+
return message;
3033
}
3134
getMessage: function () {
3235
return fakeDatabase.message;
@@ -36,25 +39,33 @@ var root = {
3639

3740
You don't need anything more than this to implement mutations. But in many cases, you will find a number of different mutations that all accept the same input parameters. A common example is that creating an object in a database and updating an object in a database often take the same parameters. To make your schema simpler, you can use “input types” for this, by using the `input` keyword instead of the `type` keyword.
3841

39-
For example, instead of a single message of the day, let's say we have one message per author, where the author is just a unique username, and we want a mutation API both for creating a new message and for updating an old message. We could use the schema:
42+
For example, instead of a single message of the day, let's say we have many messages, indexed in a database by the `id` field, and each message has both a `content` string and an `author` string. We want a mutation API both for creating a new message and for updating an old message. We could use the schema:
4043

4144
```javascript
4245
input MessageInput {
43-
content: String!
44-
author: String!
46+
content: String
47+
author: String
48+
}
49+
50+
type Message {
51+
id: ID!
52+
content: String
53+
author: String
4554
}
4655

4756
type Query {
48-
getMessage(author: String)
57+
getMessage(id: ID!): Message
4958
}
5059

5160
type Mutation {
52-
createMessage(message: MessageInput)
53-
updateMessage(message: MessageInput)
61+
createMessage(input: MessageInput): Message
62+
updateMessage(id: ID!, input: MessageInput): Message
5463
}
5564
```
5665

57-
Input types can't have fields that are other objects, only basic scalar types and list types.
66+
Here, the mutations return a `Message` type, so that the client can get more information about the newly-modified `Message` in the same request as the request that mutates it.
67+
68+
Input types can't have fields that are other objects, only basic scalar types, list types, and other input types.
5869

5970
Naming input types with `Input` on the end is a useful convention, because you will often want both an input type and an output type that are slightly different for a single conceptual object.
6071

@@ -68,39 +79,59 @@ var { buildSchema } = require('graphql');
6879
// Construct a schema, using GraphQL schema language
6980
var schema = buildSchema(`
7081
input MessageInput {
71-
content: String!
72-
author: String!
82+
content: String
83+
author: String
84+
}
85+
86+
type Message {
87+
id: ID!
88+
content: String
89+
author: String
7390
}
7491
7592
type Query {
76-
getMessage(author: String): String
93+
getMessage(id: ID!): Message
7794
}
7895
7996
type Mutation {
80-
createMessage(message: MessageInput)
81-
updateMessage(message: MessageInput)
97+
createMessage(input: MessageInput): Message
98+
updateMessage(id: ID!, input: MessageInput): Message
8299
}
83100
`);
84101

102+
// If Message had any complex fields, we'd put them on this object.
103+
class Message {
104+
constructor(id, {content, author}) {
105+
this.id = id;
106+
this.content = content;
107+
this.author = author;
108+
}
109+
}
110+
85111
// Maps username to content
86112
var fakeDatabase = {};
87113

88-
// The root provides the top-level API endpoints
89114
var root = {
90-
getMessage: function ({author}) {
91-
return fakeDatabase[author];
92-
},
93-
createMessage: function ({message}) {
94-
if (fakeDatabase[message.author]) {
95-
throw new Error('a message already exists with this author');
115+
getMessage: function ({id}) {
116+
if (!fakeDatabase[id]) {
117+
throw new Error('no message exists with id ' + id);
96118
}
97-
fakeDatabase[message.author] = message.content;
119+
return new Message(id, fakeDatabase[id]);
120+
},
121+
createMessage: function ({input}) {
122+
// Create a random id for our "database".
123+
var id = require('crypto').randomBytes(10).toString('hex');
124+
125+
fakeDatabase[id] = input;
126+
return new Message(id, input);
98127
},
99-
updateMessage: function ({message}) {
100-
if (!fakeDatabase[message.author]) {
101-
throw new Error('no message exists with this author');
128+
updateMessage: function ({id, input}) {
129+
if (!fakeDatabase[id]) {
130+
throw new Error('no message exists with id ' + id);
102131
}
103-
fakeDatabase[message.author] = message.content;
132+
// This replaces all old data, but some apps might want partial update.
133+
fakeDatabase[id] = input;
134+
return new Message(id, input);
104135
},
105136
}
106137

@@ -110,18 +141,22 @@ app.use('/graphql', graphqlHTTP({
110141
rootValue: root,
111142
graphiql: true,
112143
}));
113-
app.listen(4000);
114-
console.log('Running a GraphQL API server at localhost:4000/graphql');
144+
app.listen(4000, () => {
145+
console.log('Running a GraphQL API server at localhost:4000/graphql');
146+
});
147+
115148
```
116149

117-
To call a mutation, you must use the keyword `mutation` before your GraphQL query. To pass an input type, provide the data written as if it's a JSON object. For example, to call `createMessage` as defined above you could use this GraphQL operation:
150+
To call a mutation, you must use the keyword `mutation` before your GraphQL query. To pass an input type, provide the data written as if it's a JSON object. For example, with the server defined above, you can create a new message and return the `id` of the new message with this operation:
118151

119152
```javascript
120153
mutation {
121-
createMessage(message: {
154+
createMessage(input: {
122155
author: "andy",
123156
content: "hope is a good thing",
124-
})
157+
}) {
158+
id
159+
}
125160
}
126161
```
127162

@@ -139,7 +174,9 @@ xhr.onload = function () {
139174
console.log('data returned:', xhr.response);
140175
}
141176
var query = `mutation CreateMessage($input: MessageInput) {
142-
createMessage(message: $input)
177+
createMessage(input: $input) {
178+
id
179+
}
143180
}`;
144181
xhr.send(JSON.stringify({
145182
query: query,

0 commit comments

Comments
 (0)