Skip to content

Commit 6ddb0c2

Browse files
authored
Merge pull request #375 from monicalopezgris/graphql
Graphql sample + documentation v1
2 parents f59c94d + a2c92a9 commit 6ddb0c2

File tree

75 files changed

+4875
-4944
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+4875
-4944
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@ Visit [key principles document](https://github.com/devonfw/.github/blob/master/k
3030
- [TODO example](https://github.com/devonfw/devon4node/tree/develop/samples/todo): simple backend example for a TODO management application.
3131
- [Employee example](https://github.com/devonfw/devon4node/tree/develop/samples/todo): simple backend example for a employee management application.
3232
- [Components example](https://github.com/devonfw/devon4node/tree/develop/samples/employee): simple backend example which will show you the execution order of the devon4node components.
33+
- [GraphQL example](https://github.com/devonfw/devon4node/tree/develop/samples/graphql): simple GraphQL example with starter configuration.
3334
- [My Thai Star](https://github.com/devonfw/my-thai-star/tree/develop/node): realistic example about the management of a restaurant. This example has also a frontend and it is compatible with other devonfw stacks.

documentation/Home.asciidoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ toc::[]
4141
- link:guides-logger.asciidoc[Logger]
4242
- link:guides-mailer.asciidoc[Mailer Module]
4343
- link:guides-eslint-sonarqube-config[ESLint SonarQube Configuration]
44+
- link:guides-graphql[GraphQL]
4445

4546
== devon4node applications
4647

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
:toc: macro
2+
3+
ifdef::env-github[]
4+
:tip-caption: :bulb:
5+
:note-caption: :information_source:
6+
:important-caption: :heavy_exclamation_mark:
7+
:caution-caption: :fire:
8+
:warning-caption: :warning:
9+
endif::[]
10+
11+
toc::[]
12+
:idprefix:
13+
:idseparator: -
14+
:reproducible:
15+
:source-highlighter: rouge
16+
:listing-caption: Listing
17+
18+
= GraphQL on Devon4Node
19+
20+
GraphQL is a query language that gets exactly the data that we ask for instead of static predefined responses.
21+
22+
For example, on a regular API a get by id method would return something like:
23+
24+
[source, json]
25+
----
26+
{
27+
"location": {
28+
"lon": 00.14,
29+
"lat": 54.11
30+
},
31+
"station": "dsrEE3Sg",
32+
"visibility": 5000,
33+
"wind":{
34+
"speed": 6.2,
35+
"deg": 78
36+
},
37+
"logs": [...]
38+
...
39+
}
40+
----
41+
But if we want to get *only* the wind data we have to create another endpoint that returns the specified data.
42+
43+
But instead with graphQL we can get different information without creating new endpoints, in this case we only want the wind data so it would return:
44+
45+
[source, json]
46+
----
47+
{
48+
"wind":{
49+
"speed": 6.2,
50+
"deg": 78
51+
}
52+
}
53+
----
54+
55+
To install it:
56+
57+
[source,bash]
58+
----
59+
yarn add @nestjs/graphql graphql-tools graphql apollo-server-express
60+
----
61+
62+
== Schema first
63+
64+
[NOTE]
65+
====
66+
This tutorial uses the schema first method.
67+
68+
We assume you have already a functioning TODO module / app.
69+
70+
If not you can use https://github.com/devonfw/devon4node/tree/develop/samples/graphql[Devon4node GraphQL sample]
71+
====
72+
73+
First we need to import GraphQLModule to our `app.module.ts`.
74+
75+
[source,typescript]
76+
----
77+
...
78+
import { GraphQLModule } from '@nestjs/graphql';
79+
import { join } from 'path';
80+
81+
@Module({
82+
imports: [
83+
// Your module import
84+
GraphQLModule.forRoot({
85+
typePaths: ['./**/*.graphql'],
86+
definitions: {
87+
path: join(process.cwd(), 'src/graphql.ts'),
88+
outputAs: 'class',
89+
},
90+
}),
91+
],
92+
})
93+
export class AppModule {}
94+
----
95+
96+
The `typePaths` indicates the location of the schema definition files.
97+
98+
The `definitions` indicates the file where the typescript definitions will automatically save, adding the `outputAs: 'class'` saves those definitions as classes.
99+
100+
=== Schema
101+
102+
Graphql is a typed language with `object types`, `scalars`, and `enums`.
103+
104+
We use `querys` to define the methods we are going to use for fetching data, and `mutations` are used for modifying this data, similar to how `GET` and `POST` work.
105+
106+
Let's define the elements, querys and mutations that our module is going to have.
107+
108+
For that we have to create a graphql file on our module, on this case we are going to name it "schema.graphql".
109+
110+
[source,typescript]
111+
----
112+
type Todo {
113+
id: ID
114+
task: String
115+
}
116+
117+
type Query {
118+
todos: [Todo]
119+
todoById: Todo
120+
}
121+
122+
type Mutation {
123+
createTodo(task: String): Todo
124+
deleteTodo(id: String): Todo
125+
}
126+
----
127+
128+
For more information about Types go to the official https://graphql.org/learn/schema/[graphQL documentation]
129+
130+
131+
=== Resolver
132+
133+
Resolvers has the instructions to turn GraphQL orders into the data requested.
134+
135+
To create a resolver we go to our module and then create a new `todo.resolver.ts` file, import the decorators needed and set the resolver.
136+
137+
[source,typescript]
138+
----
139+
import { Resolver, Args, Mutation, Query } from '@nestjs/graphql';
140+
import { TodoService } from '../services/todo.service';
141+
import { Todo } from '../schemas/todo.schema';
142+
143+
@Resolver()
144+
export class TodoResolver {
145+
constructor(private readonly todoService: TodoService) {}
146+
147+
@Query('todos')
148+
findAll(): Promise<Todo[]> {
149+
return this.todoService.findAll();
150+
}
151+
152+
@Query('todoById')
153+
findOneById(@Args('id') id: string): Promise<Todo | null> {
154+
return this.todoService.findOneById(id);
155+
}
156+
157+
@Mutation()
158+
createTodo(@Args('task') task: string): Promise<Todo> {
159+
return this.todoService.create(task);
160+
}
161+
162+
@Mutation()
163+
deleteTodo(@Args('id') id: string): Promise<Todo | null> {
164+
return this.todoService.delete(id);
165+
}
166+
}
167+
----
168+
169+
`@Resolver()` indicates that the next class is a resolver.
170+
171+
`@Query` is used to get data.
172+
173+
`@Mutation` is used to create or modify data.
174+
175+
Here we have also an argument decorator `@Args` which is an object with the arguments passed into the field in the query.
176+
177+
By default we can access the query or mutation using the method's name, for example:
178+
179+
For the `deleteTodo` mutation.
180+
181+
[source,typescript]
182+
----
183+
mutation {
184+
deleteTodo( id: "6f7ed2q8" ){
185+
id,
186+
task
187+
}
188+
}
189+
----
190+
191+
But if we write something different on the decorator, we change the name, for example:
192+
193+
For the `findAll` query, we named it `todos`.
194+
[source,typescript]
195+
----
196+
{
197+
todos{
198+
id,
199+
task
200+
}
201+
}
202+
----
203+
Also if we go back to the `schema.graphql`, we will see how we define the query with `todos`.
204+
205+
Learn more about resolvers, mutations and their argument decorators on the https://docs.nestjs.com/graphql/resolvers#schema-first[NestJS documentation].
206+
207+
208+
=== Playground
209+
210+
To test our backend we can use tools as Postman, but graphql already gives us a playground to test our resolvers, we can access by default on `http://localhost:3000/graphql`.
211+
212+
We can call a query, or several querys this way:
213+
214+
[source,typescript]
215+
----
216+
{
217+
findAll{
218+
id,
219+
task
220+
}
221+
}
222+
----
223+
224+
And the output will look something like:
225+
[source,typescript]
226+
----
227+
{
228+
"data": {
229+
"findAll": [
230+
{
231+
"id": "5fb54b30e686cb49500b6728",
232+
"task": "clean dishes"
233+
},
234+
{
235+
"id": "5fb54b3be686cb49500b672a",
236+
"task": "burn house"
237+
}
238+
]
239+
}
240+
}
241+
----
242+
243+
As we can see, we get a json "data" with an array of results.
244+
245+
And for our mutations it's very similar, in this case we create a todo with task "rebuild house" and we are going to ask on the response just for the task data, we don't want the id.
246+
247+
[source,typescript]
248+
----
249+
mutation{
250+
createTodo (
251+
task: "rebuild house"
252+
){
253+
task
254+
}
255+
}
256+
----
257+
258+
And the output
259+
260+
[source,json]
261+
----
262+
{
263+
"data": {
264+
"createTodo": {
265+
"task": "rebuild house"
266+
}
267+
}
268+
}
269+
----
270+
271+
In this case we return just one item so there is no array, we also got just the `task data` but if we want the `id` too, we just have to add it on the request.
272+
273+
To make the playground unavailable we can add an option to the app.module import:
274+
275+
[source,typescript]
276+
----
277+
...
278+
GraphQLModule.forRoot({
279+
...
280+
playground: false,
281+
}),
282+
...
283+
----
284+
285+
For further information go to the official https://docs.nestjs.com/graphql/quick-start[NestJS documentation]

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,4 @@
9696
"pre-commit": "lerna run lint"
9797
}
9898
}
99-
}
99+
}

0 commit comments

Comments
 (0)