|
| 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 | +=== Database setup |
| 21 | + |
| 22 | +First of all we are going to setup a mongo database to work with, we are not going to get in details with this so you can go to the https://docs.nestjs.com/techniques/mongodb[Nestjs documentation] to find more. |
| 23 | + |
| 24 | +First we need to install a couple of dependencies: |
| 25 | + |
| 26 | +[source,bash] |
| 27 | +---- |
| 28 | +yarn add @nestjs/mongoose mongoose |
| 29 | +yarn add -D @types/mongoose |
| 30 | +---- |
| 31 | + |
| 32 | +And add MongooseModule to `app.module.ts` |
| 33 | + |
| 34 | +[source,typescript] |
| 35 | +---- |
| 36 | +import { Module } from '@nestjs/common'; |
| 37 | +import { MongooseModule } from '@nestjs/mongoose'; |
| 38 | +
|
| 39 | +@Module({ |
| 40 | + imports: [ |
| 41 | + MongooseModule.forRoot('mongodb://localhost:27017'), |
| 42 | + ], |
| 43 | +}) |
| 44 | +export class AppModule {} |
| 45 | +---- |
| 46 | + |
| 47 | +Now let's create our schema, inside `todos/schemas` we are going to create a file `todo.schema.ts` with: |
| 48 | + |
| 49 | +[source,typescript] |
| 50 | +---- |
| 51 | +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; |
| 52 | +import { Document } from 'mongoose'; |
| 53 | +
|
| 54 | +@Schema() |
| 55 | +export class Todo extends Document { |
| 56 | + @Prop() |
| 57 | + id: number; |
| 58 | +
|
| 59 | + @Prop() |
| 60 | + task: string; |
| 61 | +
|
| 62 | + @Prop() |
| 63 | + status: boolean; |
| 64 | +} |
| 65 | +
|
| 66 | +export const TodoSchema = SchemaFactory.createForClass(Todo); |
| 67 | +---- |
| 68 | + |
| 69 | +Then import it on `todo.module.ts`. |
| 70 | + |
| 71 | +[source,typescript] |
| 72 | +---- |
| 73 | +import { Module } from '@nestjs/common'; |
| 74 | +import { MongooseModule } from '@nestjs/mongoose'; |
| 75 | +import { Todo, TodoSchema } from './schemas/todo.schema'; |
| 76 | +
|
| 77 | +@Module({ |
| 78 | + imports: [MongooseModule.forFeature([{ name: Todo.name, schema: TodoSchema }])] |
| 79 | +}) |
| 80 | +export class TodosModule {} |
| 81 | +---- |
| 82 | + |
| 83 | +=== Service |
| 84 | + |
| 85 | +To interact with the database we need a service, first we create a module with `devon4node generate module todos` and inside create the service `todos.service.ts`. |
| 86 | + |
| 87 | +[source,typescript] |
| 88 | +---- |
| 89 | +import { Injectable } from '@nestjs/common'; |
| 90 | +import { InjectModel } from '@nestjs/mongoose'; |
| 91 | +import { Model } from 'mongoose'; |
| 92 | +import { Todo } from './schemas/todo.schema'; |
| 93 | +
|
| 94 | +@Injectable() |
| 95 | +export class TodosService { |
| 96 | + constructor(@InjectModel(Todo.name) private readonly todoModel: Model<Todo>) {} |
| 97 | +
|
| 98 | + async create(task: string): Promise<Todo> { |
| 99 | + const object = { |
| 100 | + task: task |
| 101 | + } |
| 102 | + const createdTodo = new this.todoModel(object); |
| 103 | + return createdTodo.save(); |
| 104 | + } |
| 105 | +
|
| 106 | + async findAll(): Promise<Todo[]> { |
| 107 | + return this.todoModel.find().exec(); |
| 108 | + } |
| 109 | +} |
| 110 | +---- |
| 111 | + |
| 112 | +To finish this section we need to create the corresponding schema on `todos/schemas/todo.schema`: |
| 113 | + |
| 114 | +[source,typescript] |
| 115 | +---- |
| 116 | +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; |
| 117 | +import { Document } from 'mongoose'; |
| 118 | +
|
| 119 | +@Schema() |
| 120 | +export class Todo extends Document { |
| 121 | + @Prop() |
| 122 | + id: number; |
| 123 | +
|
| 124 | + @Prop() |
| 125 | + task: string; |
| 126 | +
|
| 127 | + @Prop() |
| 128 | + status: boolean; |
| 129 | +} |
| 130 | +
|
| 131 | +export const TodoSchema = SchemaFactory.createForClass(Todo); |
| 132 | +---- |
| 133 | + |
| 134 | +=== GraphQL |
| 135 | + |
| 136 | +To install it: |
| 137 | + |
| 138 | +[source,bash] |
| 139 | +---- |
| 140 | +yarn add @nestjs/graphql graphql-tools graphql apollo-server-express |
| 141 | +---- |
| 142 | + |
| 143 | +=== Squema first |
| 144 | +This tutorial uses the schema first method. |
| 145 | + |
| 146 | +First we need to import GraphQLModule to our `app.module.ts`. |
| 147 | + |
| 148 | +[source,typescript] |
| 149 | +---- |
| 150 | +... |
| 151 | +import { GraphQLModule } from '@nestjs/graphql'; |
| 152 | +import { join } from 'path'; |
| 153 | +
|
| 154 | +@Module({ |
| 155 | + imports: [ |
| 156 | + ... |
| 157 | + GraphQLModule.forRoot({ |
| 158 | + typePaths: ['./**/*.graphql'], |
| 159 | + definitions: { |
| 160 | + path: join(process.cwd(), 'src/graphql.ts'), |
| 161 | + outputAs: 'class', |
| 162 | + }, |
| 163 | + }), |
| 164 | + ], |
| 165 | +}) |
| 166 | +export class AppModule {} |
| 167 | +---- |
| 168 | + |
| 169 | +The `typePaths` indicates the location of the schema definition files. |
| 170 | + |
| 171 | +The `definitions` indicates the file where the typescript definitions, adding the `outputAs: 'class'` saves those definitions as classes. |
| 172 | + |
| 173 | +==== Resolver |
| 174 | + |
| 175 | +Resolvers has the instructions to turn graphQL orders into the data requested. |
| 176 | + |
| 177 | +To create a resolver we go to todos module and then create a new `todos.resolver.ts` file, import the decorators needed and set our resolver. |
| 178 | + |
| 179 | +[source,typescript] |
| 180 | +---- |
| 181 | +import { Resolver, Args, Mutation, Query } from '@nestjs/graphql'; |
| 182 | +import { TodosService } from './services/todos.service'; |
| 183 | +import { Todo } from './schemas/todo.schema'; |
| 184 | +
|
| 185 | +@Resolver() |
| 186 | +export class TodosResolver { |
| 187 | + constructor(private readonly todosService: TodosService) {} |
| 188 | +
|
| 189 | + @Query() |
| 190 | + findAll(): Promise<Todo[]> { |
| 191 | + return this.todosService.findAll(); |
| 192 | + } |
| 193 | + @Mutation() |
| 194 | + createTodo(@Args('task') task: string): Promise<Todo> { |
| 195 | + return this.todosService.create(task); |
| 196 | + } |
| 197 | +} |
| 198 | +
|
| 199 | +---- |
| 200 | + |
| 201 | +`@Resolver()` indicates that the next class is a resolver. |
| 202 | + |
| 203 | +`@Query` is used to get data. |
| 204 | + |
| 205 | +`@Mutation` is used to create or modify data. |
| 206 | + |
| 207 | +The `@mutation` will create the next schema in or autogenerated schema file: |
| 208 | +[source,typescript] |
| 209 | +---- |
| 210 | +type Mutation { |
| 211 | + createTodo( task: String ): Todo |
| 212 | +} |
| 213 | +---- |
| 214 | + |
| 215 | +And the `@Query` would do the same: |
| 216 | +[source,typescript] |
| 217 | +---- |
| 218 | +type Query { |
| 219 | + todos: [Todo] |
| 220 | +} |
| 221 | +---- |
| 222 | + |
| 223 | +Here we have also an argument decorator `@Args` which is an object with the arguments passed into the field in the query. |
| 224 | + |
| 225 | +Learn more about resolvers and their argument decorators on the https://docs.nestjs.com/graphql/resolvers#schema-first[NestJS documentation]. |
| 226 | + |
| 227 | + |
| 228 | + |
| 229 | +=== Code first |
| 230 | +On this approach we are going to use decorators to generate de schema. |
0 commit comments