Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/hardyscc/nodejs-graphql-sample
Build a basic CRUD backend application in NodeJS with GraphQL & MySQL.
https://github.com/hardyscc/nodejs-graphql-sample
graphql graphql-playground mysql nestjs nodejs typeorm typescript
Last synced: 25 days ago
JSON representation
Build a basic CRUD backend application in NodeJS with GraphQL & MySQL.
- Host: GitHub
- URL: https://github.com/hardyscc/nodejs-graphql-sample
- Owner: hardyscc
- Created: 2020-06-24T03:05:41.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2023-03-06T15:34:12.000Z (almost 2 years ago)
- Last Synced: 2024-10-12T18:24:49.425Z (3 months ago)
- Topics: graphql, graphql-playground, mysql, nestjs, nodejs, typeorm, typescript
- Language: TypeScript
- Homepage:
- Size: 3.97 MB
- Stars: 3
- Watchers: 2
- Forks: 0
- Open Issues: 12
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# NodeJS GraphQL Backend Tutorial
## Overview
Build a basic CRUD backend application in NodeJS with GraphQL & MySQL.
## Technologies
1. [NodeJS](https://nodejs.org/)
1. [TypeScript](https://www.typescriptlang.org/)
1. [NestJS](https://nestjs.com/)
1. [TypeGraphQL](https://typegraphql.com/)
1. [TypeORM](https://typeorm.io/)
1. [MySQL](https://www.mysql.com/)## Create an Empty Project
Setup an empty project using [Nest CLI](https://docs.nestjs.com/cli/overview).
```bash
npm install -g @nestjs/cli
nest new nodejs-graphql-sample
cd nodejs-graphql-sample
```> Please use `npm` as the default package manager for this tutorial.
> For more information, please see [NestJS First Steps](https://docs.nestjs.com/first-steps).
Start the server in watch mode.
```bash
npm run start:dev
```Goto http://localhost:3000 You should see the `Hello World` message.
## NestJS GraphQL Module
### Install and Config
Install the required packages.
```bash
npm install @nestjs/graphql graphql-tools graphql apollo-server-express type-graphql
```Update the `src/app.module.ts` as follow.
```ts
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { AppController } from './app.controller';
import { AppService } from './app.service';@Module({
imports: [
GraphQLModule.forRoot({
autoSchemaFile: true,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
```Update the `src/main.ts` to accept the `PORT` environment variable.
```ts
async function bootstrap() {
...
await app.listen(process.env.PORT || 4000);
}
...
```> Notes that the default backend port has been changed to `4000` as well, to avoid the port conflict with frontend.
### Generate the Skeleton Template for User Module and Resolver
```bash
nest generate module user
nest generate resolver user --no-spec
```The following folder and files will be created.
```sh
src/user/user.resolver.ts
src/user/user.module.ts
```Please notes that `src/app.module.ts` also updated automatically as follow.
```ts
...
import { UserModule } from './user/user.module';@Module({
imports: [
...
UserModule,
],
...
})
export class AppModule {}```
### Create the User Entity and Input Type
Create the following file `src/user/user.entity.ts`.
```ts
import { Field, ID, ObjectType } from '@nestjs/graphql';@ObjectType()
export class User {
@Field(() => ID)
id: string;@Field()
name: string;@Field({ nullable: true })
nickName?: string;
}
```Create the following file `src/user/create-user.input.ts`.
```ts
import { Field, InputType } from '@nestjs/graphql';@InputType()
export class CreateUserInput {
@Field()
name: string;@Field({ nullable: true })
nickName?: string;
}
```### Implement the User Resolver
Update `src/user/user.resolver.ts` and add the `users` query and `createUser` mutation.
```ts
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { CreateUserInput } from './create-user.input';
import { User } from './user.entity';const users: User[] = [];
@Resolver('User')
export class UserResolver {
@Mutation(() => User)
createUser(@Args('input') input: CreateUserInput) {
const user = new User();
user.name = input.name;
user.nickName = input.nickName;
user.id = String(Math.floor(Math.random() * 1000000000));
users.push(user);
return user;
}@Query(() => [User])
users() {
return users;
}
}
```> just temporary keep the users list in memory, will change to use database later.
### Test the GraphQL Endpoint
Start the server instance in watch mode.
```bash
npm run start:dev
```Goto the GraphQL Playground - http://localhost:4000/graphql.
1. Create a new user
```graphql
mutation {
createUser(input: { name: "Tommy" }) {
id
}
}
```Output :
```json
{
"data": {
"createUser": {
"id": "95678594"
}
}
}
```2. Query the users
```graphql
query {
users {
id
name
}
}
```Output :
```json
{
"data": {
"users": [
{
"id": "95678594",
"name": "Tommy"
}
]
}
}
```## NestJS TypeORM Module
### Install and Config
Install the required packages.
```bash
npm install @nestjs/typeorm typeorm mysql2
```Add the TypeOrmModule Configuration into `src/app.module.ts` as follow.
```ts
...
import { TypeOrmModule } from "@nestjs/typeorm";
import { User } from "./user/user.entity";
...const databaseUrl =
process.env.DATABASE_URL ||
'mysql://usr:User12345@localhost:3306/development';@Module({
imports: [
...
TypeOrmModule.forRoot({
type: 'mysql',
url: databaseUrl,
database: databaseUrl.split('/').pop(),
entities: [User],
synchronize: true,
logging: true,
}),
...
]
})
export class AppModule {}
```Add the TypeOrmModule Configuration into `src/user/user.module.ts` as follow.
```ts
...
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from "./user.entity";
...@Module({
imports: [TypeOrmModule.forFeature([User])],
...
})
export class UserModule {}
```> for more information, please see [NestJS Database](https://docs.nestjs.com/techniques/database).
### Add the TypeORM Decorators into User Entity
Update the following file `src/user/user.entity.ts`.
```ts
import { Field, ID, ObjectType } from '@nestjs/graphql';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';@ObjectType()
@Entity()
export class User {
@Field(() => ID)
@PrimaryGeneratedColumn('uuid')
id: string;@Field()
@Column()
name: string;@Field({ nullable: true })
@Column({ nullable: true })
nickName?: string;
}
```### Create an User Service
This is a helper service which using `TypeORM's Repository API` to access the MySQL database.
```
nest generate service user --no-spec
```Update the generated file `src/user/user.service.ts` as follow.
```ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateUserInput } from './create-user.input';
import { User } from './user.entity';@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private readonly userRepository: Repository,
) {}create(input: CreateUserInput) {
const user = this.userRepository.create(input);
return this.userRepository.save(user);
}findOneById(id: string) {
return this.userRepository.findOneOrFail(id);
}async delete(id: string) {
const { affected } = await this.userRepository.delete(id);
return affected !== 0;
}find() {
return this.userRepository.find();
}
}
```### Update the User Resolver
Update the following file `src/user/user.resolver.ts`.
```ts
import { NotFoundException } from '@nestjs/common';
import { Args, Mutation, Query, Resolver, ID } from '@nestjs/graphql';
import { CreateUserInput } from './create-user.input';
import { User } from './user.entity';
import { UserService } from './user.service';@Resolver(User)
export class UserResolver {
constructor(private readonly userService: UserService) {}@Query(() => User)
async user(@Args({ name: 'id', type: () => ID }) id: string) {
const user = await this.userService.findOneById(id);
if (!user) {
throw new NotFoundException(id);
}
return user;
}@Mutation(() => User)
createUser(@Args('input') input: CreateUserInput) {
return this.userService.create(input);
}@Mutation(() => ID, { nullable: true })
async deleteUser(@Args({ name: 'id', type: () => ID }) id: string) {
return (await this.userService.delete(id)) ? id : null;
}@Query(() => [User])
users() {
return this.userService.find();
}
}
```### Test the GraphQL Endpoint with TypeORM
Start a MySQL docker instance.
```bash
docker run -d -e "MYSQL_ROOT_PASSWORD=Admin12345" -e "MYSQL_USER=usr" -e "MYSQL_PASSWORD=User12345" -e "MYSQL_DATABASE=development" -p 3306:3306 --name some-mysql bitnami/mysql:5.7.27
```Start the server instance in watch mode.
```bash
npm run start:dev
```Goto the GraphQL Playground - http://localhost:4000/graphql.
1. Create some users
```graphql
mutation {
a: createUser(input: { name: "John" }) {
id
}
b: createUser(input: { name: "Mary" }) {
id
}
}
```Output:
```json
{
"data": {
"a": {
"id": "6ad2b68d-15a8-4e3e-9062-6343324faa7e"
},
"b": {
"id": "eb777cfa-65d4-4a36-9344-5452284647e6"
}
}
}
```2. Query the users
```graphql
query {
users {
id
name
}
}
```Output :
```json
{
"data": {
"users": [
{
"id": "6ad2b68d-15a8-4e3e-9062-6343324faa7e",
"name": "John"
},
{
"id": "eb777cfa-65d4-4a36-9344-5452284647e6",
"name": "Mary"
}
]
}
}
```3. Delete one of the user by the id
```graphql
mutation {
deleteUser(id: "6ad2b68d-15a8-4e3e-9062-6343324faa7e")
}
```Output :
```json
{
"data": {
"deleteUser": "6ad2b68d-15a8-4e3e-9062-6343324faa7e"
}
}
```4. Test the MySQL database
Run the mysql command using the same docker instance.
```bash
docker exec -it some-mysql mysql -uroot -p"Admin12345"
```Select the data from user table.
```sql
mysql> use development;
mysql> select * from user;
+--------------------------------------+------+----------+
| id | name | nickName |
+--------------------------------------+------+----------+
| eb777cfa-65d4-4a36-9344-5452284647e6 | Mary | NULL |
+--------------------------------------+------+----------+
```