Creating queries and mutations for basic CRUD operations
Make sure to have Node (TypeScript) and Nest JS installed globally before you begin. You can install NestJS CLI by following command.
npm i -g @nestjs/cli
Type following on the terminal prompt
nest new nest-graphql-api
You can change the project name if you wish.
npm install graphql @nestjs/graphql graphql-tools apollo-server-express mysql typeorm @nestjs/typeorm class-validator
Replace YOUR_ACCOUNT_USERNAME and YOUR_ACCOUNT_PASSWORD with the actual username and password of your MySQL server instance.
You might need to grant the additional rights for the user to get connected via node and typeorm
GraphQLModule.forRoot({
typePaths: ['./**/*.graphql'],
playground: true,
}),
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'SqlAdmin$123',
database: 'flight_db',
entities: [
"dist/**/*.entity.{ts,js}"
],
synchronize: true,
}),
CREATE DATABASE flight_db
CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
SET NAMES 'utf8';
USE flight_db;
CREATE TABLE flight (
id int(11) NOT NULL AUTO_INCREMENT,
flight_code varchar(10) NOT NULL,
origin varchar(10) NOT NULL,
destination varchar(10) NOT NULL,
air_time varchar(5) NOT NULL,
airport varchar(50) NOT NULL,
distance int(11) NOT NULL,
PRIMARY KEY (id)
)
ENGINE = INNODB,
AUTO_INCREMENT = 9005,
AVG_ROW_LENGTH = 173,
CHARACTER SET utf8mb4,
COLLATE utf8mb4_0900_ai_ci;
USE flight_db;
INSERT INTO flight(id, flight_code, origin, destination, air_time, airport, distance) VALUES
(1, '9E-3331', 'JFK', 'BOS', '41', 'General Edward Lawrence Logan Intl', 0),
(2, '9E-3331', 'JFK', 'BOS', '46', 'General Edward Lawrence Logan Intl', 0),
(3, '9E-3338', 'JFK', 'ORD', '146', 'Chicago Ohare Intl', 0),
(4, '9E-3338', 'JFK', 'ORD', '150', 'Chicago Ohare Intl', 0),
(5, '9E-3359', 'JFK', 'ORD', '141', 'Chicago Ohare Intl', 0),
(6, '9E-3359', 'JFK', 'ORD', '143', 'Chicago Ohare Intl', 0),
(7, '9E-3422', 'JFK', 'BOS', '43', 'General Edward Lawrence Logan Intl', 0),
(8, '9E-3422', 'JFK', 'BOS', '38', 'General Edward Lawrence Logan Intl', 0),
(9, '9E-3422', 'JFK', 'BOS', '34', 'General Edward Lawrence Logan Intl', 0),
(10, '9E-3422', 'JFK', 'BOS', '37', 'General Edward Lawrence Logan Intl', 0);
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
flight.entity.ts
- Make sure your file name ends with .entity.ts
@Entity('flight')
export class FlightEntity {
@PrimaryGeneratedColumn('increment')
id: number;
@Column({ length: 10 })
flight_code: string;
@Column({ length: 10 })
origin: string;
@Column({ length: 10 })
destination: string;
@Column({ length: 5 })
air_time: string;
@Column()
distance: number;
@Column({ length: 50 })
airport: string;
}
Class needs to be marked with
@Entity annotation (Pass the table name if the class name and table name are different). Primary Key Column needs to be marked with
@PrimaryGeneratedColumn('increment') annotation.
flight.graphql
- Make sure your file name ends with .graphql
as the Entry in App Module looks for the typePaths: ['./**/*.graphql']
.
type Flight {
id: Int
flight_code: String!
origin: String!
destination: String!
air_time: String!
distance: Int
airport: String!
}
input FlightInput {
flight_code: String!
origin: String!
destination: String!
air_time: String!
distance: Int
airport: String!
}
These are the type definitions and Input types simply needs to be denoted by input
keyword
! symbol denotes not null field, however use them ONLY when the data field guaranties to return data all the time. String!
is strict versus String
(which means you can not have null data).
flight.graphql
- Further add the following types
type Query {
flights: [Flight!]
flight(id: Int): [Flight!]
}
type Mutation {
createFlight(input: FlightInput!): Flight
updateFlight(id: Int, input: FlightInput!): Flight
deleteFlight(id: Int): Flight
}
Queries and Mutations are also considered as special types in GraphQL and needs to be defined in within the schema. The above example specifies the SELECT
OR READ
operations via queries
while CREATE
, UPDATE
and DELETE
operations are defined as mutations.
Note the return types are specified as the Type defined in the schema above and Returned as either Single object (Flight
) or an Array ([Flight!]
).
export class FlightInput {
flight_code: string;
origin: string;
destination: string;
air_time: string;
distance: number;
airport: string;
}
flight.resolver.ts
For clarity the example has been shortened, see the source for complete implementation.
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { FlightEntity } from './flight.entity';
import { FlightService } from './flight.service';
import { FlightInput } from './flight.input';
@Resolver(of => FlightEntity)
export class FlightResolver {
constructor(private readonly flightService: FlightService) {}
@Query(() => [FlightEntity])
async flights() {
return this.flightService.getFlights();
}
...
@Mutation(() => FlightEntity)
async deleteFlight(@Args('id') id:number) {
return await this.flightService.deleteFlight(id);
}
}
Class needs to have @Resolver annotation and Every operation must have either @Query or @Mutation annotation depending upon its definition as per the schema.
Note that service is being injected via constructor
flight.service.ts
For clarity the example has been shortened, see the source for complete implementation.
import { Injectable, Inject } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { FlightEntity } from './flight.entity';
import { FlightInput } from './flight.input';
import e from 'express';
@Injectable()
export class FlightService {
constructor(
@InjectRepository(FlightEntity)
private flightsRepository: Repository<FlightEntity>,
) {}
async getFlights(): Promise<FlightEntity[]> {
return await this.flightsRepository.find({});
}
...
async deleteFlight(id: number) {
const existingFlight = await this.flightsRepository.findOne(id);
if (existingFlight) {
let result = this.flightsRepository.delete(existingFlight);
return existingFlight;
}
else{
return null;
}
}
}
flights.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { FlightService } from './flight.service';
import { FlightEntity } from './flight.entity';
import { FlightResolver } from './flight.resolver';
@Module({
imports: [TypeOrmModule.forFeature([FlightEntity])],
providers: [FlightService, FlightResolver],
})
export class FlightsModule {}
Check how the Resolver and Service classes have been referenced and the TypeORM has been supplied with the entity references.
Import the module in app.module.ts in the imports: []
after the TypeORM and GraphQL references. All such modules needs to be referenced in the App module to initiate the corresponding resolvers and services by NestJS.
type and execute the following command on the terminal
npm run start
OR (if need hot loading)
npm run start:dev
Executing Queries should look like
Executing Mutations should look like