Desenvolvimento

Como criar CRUD Básico com NestJS, GraphQL e MySQL

26 de julho de 2022

Neste artigo, vamos aprender a criar um CRUD básico com NestJS, GraphQL e MySQL.

Entretanto, antes de qualquer coisa, vamos entender o conceito de todos esses frameworks? Então, vamos lá!

Segundo a documentação, NestJS é:

Nest (NestJS) é mais um framework, voltado principalmente para construção de aplicações Node.js para
servidores Back-end. Dessa forma, uma das vantagens de se usar o NestJS é a alta escalabilidade do lado do servidor e a estrutura automática criada incialmente, seguindo os princípios do S.O.L.I.D.

[adrotate banner=”4″]

Como o NestJS utiliza TypeScript, é possível a criação de códigos fortemente tipados, mas também é possível utilizá-lo com JS puro.

A construção do código inicialmente combina elementos de OOP (Programação Orientada a Objetos), FP (Programação Funcional) e FRP (Programação Reativa Funcional). Além disso, o Nest faz uso de estruturas robustas de servidor HTTP como o Express (o padrão). Bem como, opcionalmente, pode ser configurado para usar o Fastify também!

O Nest fornece um nível de abstração acima dessas estruturas comuns do Node.js (Express/Fastify), mas
também expõe suas APIs diretamente ao desenvolvedor. Dessa forma, oferecendo aos desenvolvedores a liberdade de usar a infinidade de módulos de terceiros que estão disponíveis para a plataforma subjacente.

O que é GraphQL?

Segundo a documentação, GraphQL é:

GraphQL é uma linguagem de consulta para APIs de alto desempenho, relacionado ao tempo de consulta aos principais SGBDs. Dessa forma, o GraphQL fornece uma descrição completa e compreensível dos dados e requisições de sua API, oferecendo aos clientes o poder de solicitar exatamente o que eles precisam e nada mais!

Iniciando com NestJS e GraphQL

Primeiramente, para iniciar nosso projeto CRUD, iremos realizar todas as instalações necessárias para a criação do projeto.

Instalações necessárias

Extensões:

  1. JavaScript (ES6) code snippets
  2. Tabnine AI Autocomplete
  3. Path Intellisense
  4. Nodejs Snippets
  5. Material Icon Theme
  6. GitLens — Git supercharged
  7. Gitmoji
  8. Dracula Official
  9. ENV
  10. [Deprecated] Bracket Pair Colorizer 2
  11. Code Spell Checker

Projeto no github

Para visualizar o projeto deste artigo, clique neste link.

Instalações e start do projeto CRUD

  1. Abra o terminal do S.O como admin e execute o comando para instalação do NestJS de maneira Global ⇒ npm i -g @nestjs/cli
  2. Agora, execute o terminal em um local da sua preferência para criação do projeto, e digite o comando ⇒ nest new nest-api-withgraphql

Então, após executar o comando acima, a imagem abaixo descreve o que vocês devem visualizar de saída.

3. Na imagem 1, escolha o seu gerenciador de pacotes favorito! Neste caso, vamos escolher o YARN.

Dessa forma, após as instalações para o NestJS estiverem finalizadas, a imagem abaixo descreve o que você deve visualizar de saída.

  1. Na imagem 2, digite o comando cd .NOME_DO_PROJETO para acessar a pasta criada com o projeto que definimos. Logo após entrarmos no projeto, vamos abri-lo no VSCode com o comando code .. Perfeito, agora podemos fechar o terminal!
  2. Para entender a estrutura criada, leia a documentação deste link.

Integrando o NestJS com Typeorm e MySql

Documentações de referência para projeto CRUD: https://docs.nestjs.com/techniques/database e https://typeorm.io/.

  1. Antes de tudo vamos abrir o terminal em nosso VSCode, através do comando CTRL + J
  2. Agora vamos fazer a instalação das dependências do typeorm e mysql:
    yarn add @nestjs/typeorm typeorm mysql2

Observação: vou fazer a instalação do mysql, porque este é o SGBD que mais utilizei na minha trajetória como programador, mas você pode utilizar outros, como postgresql, mssql, etc. É só fazer a instalação da lib e adicionar os parâmetros de conexão conforme apresentado no código abaixo.

Dentro do diretório src , vamos modificar o arquivo app.module.ts.

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm'
/*
Arquivo para importação dos módulos utilizados no projeto e conexão com o banco de dados!
Sejam bem-vindos ao coração da nossa API!
*/
@Module({
imports: [
TypeOrmModule.forRoot({
type: "mysql",
host: "localhost",
port: 3306,
username: "root",
password: "",
database: "graphql_database",
entities: [
"dist/**/*.entity{.ts,.js}"
],
synchronize: true
})
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule { }
  1. Importe o TypeOrmModule e adicione-o dentro do array de imports: []. Isso vai possibilitar a utilização do TypeORM em nossa API. A função forRoot() precisa receber alguns parâmetros de conexão com o banco, através de um json.

Caso queira saber mais sobre esses parâmetros, consulte a documentação neste link.

4. Ative os serviços de mysql e apache no XAMMP ou outro software da sua preferência!

  1. Levando em consideração que você já saiba como criar um banco de dados, faça os passos descritos na imagem abaixo para criação desse banco de dados no DBeaver!

Configurando o GraphQL

Documentações de referência para projeto CRUD: https://docs.nestjs.com/graphql/quick-start, https://www.npmjs.com/package/graphql, https://www.npmjs.com/package/@nestjs/apollo, https://www.npmjs.com/package/apollo-server-express e https://www.npmjs.com/package/@nestjs/graphql.

  1. Instalação da dependências: yarn add @nestjs/graphql @nestjs/apollo graphql graphql-tools apollo-server-express
  2. Dentro do diretório src, vamos modificar o arquivo app.module.ts novamente, com a nova funcionalidade do GraphQLModule:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm'
//importações para utilização do graphql
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { join } from 'path';
@Module({
imports: [
TypeOrmModule.forRoot({
type: "mysql",
host: "localhost",
port: 3306,
username: "root",
password: "",
database: "graphql_database",
entities: [
"dist/**/*.entity{.ts,.js}"
],
synchronize: true
}),
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule { }

Adicione GraphQLModule dentro do array de imports: []. Conforme o código supracitado! Assim, vamos utilizar o autoSchemaFile para fazer a geração do arquivo de schema.gql com as tipagens de forma automática e utilizar o servidor do Apollo para utilização do GraphQL.

Criando o Module, Service, Resolver e Entity de Users

  1. Comando para criação do modulo: nest g module user

Dessa forma, a imagem abaixo descreve o que você deve visualizar de saída.

O comando irá instanciar automaticamente o módulo de usuário e criar o arquivo. Assim, o mesmo acontecerá para os próximos passos.

  1. Comando para criação do serviço: nest g s user
  2. Comando para criação do resolver: nest g r user
  3. Vamos criar nosso arquivo de entidade no caminho src/user com o seguinte nome user.entity.ts
  1. Na entidade, vamos especificar todas as colunas que teremos em nossa tabela de users no banco de dados.

Configurando nossa entidade de Usuários

Documentações de referência para projeto CRUD: https://docs.nestjs.com/graphql/resolvers e https://docs.nestjs.com/graphql/cli-plugin.

As entidades se assemelham bastante com a estrutura de migrations. Então, é nesse arquivo que definimos como vai ficar estruturado as colunas da tabela em nosso banco de dados!

  1. Vamos adicionar ao arquivo que criamos na última etapa do tópico anterior (src/user/user.entity.ts), o seguinte trecho de código:
// importações
import { Field, ID, ObjectType } from '@nestjs/graphql';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@ObjectType() //Informa que é um objeto
@Entity() //Especifica ao código que essa classe se trata de uma entidade
export class User {
@PrimaryGeneratedColumn() // Especifica ao NestJS que a coluna id é uma coluna de chave primaria que é gerada automaticamente
@Field(() => ID) // Informa ao graphql o tipo da coluna
id: string; // tipagem da coluna
@Column()
name: string;
@Column()
email: string;
}
  1. Para não precisar especificar @Field() para colunas de tipos comum e deixar o código mais limpo, podemos utilizar o seguinte trecho de código no arquivo nest-cli.json na raiz do nosso projeto.
{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"plugins": [
{
"name": "@nestjs/graphql/plugin",
"options": {
"typeFileNameSuffix": [
".input.ts",
".args.ts",
".entity.ts",
".type.ts"
]
}
}
]
}
}

DTO e Validators

Documentação de referência para projeto CRUD: https://docs.nestjs.com/techniques/validation.

  1. Instalação da lib para validação: yarn add class-validator
  2. Instalação da lib para transformação da classe: yarn add class-transformer
  3. Dentro do caminho src/user, crie uma pasta e nomeie como dto e adicione dois arquivos dentro desta pasta chamada createuser.input.ts e update-user.input.ts
  4. Dentro do arquivo create-user.input.ts, adicione o seguinte trecho de código:
// importações
import { InputType } from "@nestjs/graphql";
import { IsEmail, IsNotEmpty, IsString } from "class-validator";
@InputType() //Define que essa classe é do tipo input, que são os valores que serão recebidos pelo endpoint
export class CreateUserInput {
@IsString()// Validação do tipo do input
@IsNotEmpty({ message: "Campo de name obrigatório" })//Validação para não aceitar o valor vazio para chave
name: string; //Tipagem da coluna
@IsEmail()// Validação do tipo do input
@IsNotEmpty({ message: "Campo de email obrigatório" })//Validação para não aceitar o valor vazio para chave
email: string; //Tipagem da coluna
}

Então, dentro do arquivo update-user.input.ts, adicione o seguinte trecho de código:

// importações
import { InputType } from "@nestjs/graphql";
import { IsEmail, IsNotEmpty, IsOptional, IsString } from "class-validator";
@InputType() //Define que essa classe é do tipo input, que são os valores que serão recebidos pelo endpoint
export class UpdateUserInput {
@IsString()// Validação do tipo do input
@IsNotEmpty({ message: "Campo de name obrigatório" })//Validação para não aceitar o valor vazio para chave
@IsOptional()// Define o campo como opcional
name?: string; //Tipagem da coluna
@IsEmail()// Validação do tipo do input
@IsNotEmpty({ message: "Campo de email obrigatório" })//Validação para não aceitar o valor vazio para chave
@IsOptional()// Define o campo como opcional
email?: string; //Tipagem da coluna
}

6. Adicione, no arquivo src/main.ts, o uso global das validações:

import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe()) //Para utilizar as validações globalmente
await app.listen(3000);
}
bootstrap();

Implementando o Service de User

Documentação de referência para projeto CRUD: https://docs.nestjs.com/providers#services

Service é o arquivo responsável por conter todas nossas funções que faram comunicação direta com o banco. Além disso, ele se assemelha muito aos controllers em outros frameworks.

Portanto, vamos modificar o arquivo src/user/user.service.ts com o seguinte trecho de código:

// importações
import { Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateUserInput } from './dto/create-user.input';
import { UpdateUserInput } from './dto/update-user.input';
import { User } from './user.entity'
@Injectable() // para fazer injeção de dependências no nestjs
export class UserService {
//injeta o repositório utilizando a entidade de usuário
constructor(
@InjectRepository(User)
private userRepository: Repository<User>
) { }
//método para listagem de usuários
async findAllUsers(): Promise<User[]> {
const users = await this.userRepository.find()
return users
}
//método para trazer um usuário por id
async findUserById(id: string): Promise<User> {
const user = await this.userRepository.findOneBy({ id })
if (!user) {
throw new NotFoundException("Usuário não encontrado")
}
return user
}
//método para criar um usuário
async createUser(data: CreateUserInput): Promise<User> {
const user = this.userRepository.create(data);
const userSaved = await this.userRepository.save(user)
if (!userSaved) {
throw new InternalServerErrorException('Falha na criação do usuário')
}
return userSaved
}
//método para alterar um usuário
async updateUser(id: string, data: UpdateUserInput): Promise<User> {
const user = await this.findUserById(id);
await this.userRepository.update(user, { ...data });
const userUpdated = this.userRepository.create({ ...user, ...data })
return userUpdated;
}
//método para exclusão de um usuário
async deleteUser(id: string): Promise<boolean> {
const user = await this.findUserById(id);
const deleted = await this.userRepository.delete(user);
if (deleted) {
return true;
}
return false;
}
}

Implementando o Resolver de User e importando o TypeORM para o módulo de User

Documentação de referência para projeto CRUD: https://docs.nestjs.com/graphql/resolvers#resolvers

Os Resolvers fornecem instruções para transformar uma operação do GraphQL (@Query @Mutation) em dados. Assim, aqui ficará definido o que uma função recebe e o que ela retorna!

  1. Vamos modificar o arquivo src/user/user.resolver.ts com o seguinte trecho de código:
// importações
import { Args, Mutation, Resolver, Query } from '@nestjs/graphql';
import { CreateUserInput } from './dto/create-user.input';
import { UpdateUserInput } from './dto/update-user.input';
import { User } from './user.entity';
import { UserService } from './user.service';
@Resolver('User')
export class UserResolver {
//Ejetar nosso userService
constructor(private userService: UserService) { }
@Query(() => [User]) //informa ao graphql que esse método vai retornar um array de usuários e que não há modificação do banco com ela
async users(): Promise<User[]> {
const users = await this.userService.findAllUsers()
return users
}
@Query(() => User) //informa ao graphql que esse método vai retornar um usuário em especifico e que não há modificação do banco
async user(@Args('id') id: string): Promise<User> {
const user = await this.userService.findUserById(id)
return user
}
@Mutation(() => User) //informa ao graphql que esse método vai modificar o estado no nosso banco de dados
async createUser(@Args('data') data: CreateUserInput): Promise<User> {
const user = await this.userService.createUser(data)
return user
}
@Mutation(() => User) //informa ao graphql que esse método vai modificar o estado no nosso banco de dados
async updateUser(
@Args('id') id: string,
@Args('data') data: UpdateUserInput
): Promise<User> {
const user = this.userService.updateUser(id, data);
return user;
}
@Mutation(() => Boolean) //informa ao graphql que esse método vai modificar o estado no nosso banco de dados
async deleteUser(
@Args('id') id: string
): Promise<boolean> {
const deleted = await this.userService.deleteUser(id);
return deleted;
}
}

2. Vamos modificar o arquivo src/user/user.module.ts com o seguinte trecho de código:

import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserResolver } from './user.resolver';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
@Module({
imports: [
TypeOrmModule.forFeature([User])
],
providers: [UserService, UserResolver]
})
export class UserModule { }

Testando nossos endpoints

  1. Comando para execução do servidor: yarn run start:dev

Com isso, a saída deve se apresentar como na imagem abaixo:

  1. Cole o seguinte endereço no seu navegador: http://localhost:3000/graphql

Dessa forma, após colar o endereço supracitado no navegador, a seguinte página deve aparecer:

Esse é o playground do graphql. Ou seja, a ferramenta que auxilia em nossas requisições!

  1. Para utilizar o playground, vamos primeiro fazer uma listagem de todos os usuários! Digite o código abaixo para fazer essa requisição:
query{
users{
id
name
email
}
}

Assim, ao fazer a consulta na aba esquerda, a aba direita da ferramenta deve exibir o seguinte resultado:

Isso acontece porque não temos nenhum usuário registrado.

  1. Para registrar um usuário, crie uma nova aba no playground e cole o seguinte trecho de código.
mutation {
createUser(
data: {
name: "Edith Silva"
email: "[email protected]"
}
) {
id
name
email
}
}

Dessa forma, a resposta deve aparecer como na imagem abaixo:

Então, à essa altura, já conseguimos listar o usuário criado!

  1. Agora é só você criar mais 3 novas abas, uma para alterar, outra para mostrar pelo ID e outra para excluir, os Body’s estão disponíveis conforme o trecho de código abaixo:
// Em uma nova aba do playground, adicione o body abaixo para fazer uma busca pelo id
query{
user(id: "1"){
id
name
email
}
}
// Em uma nova aba do playground, adicione o body abaixo para fazer uma Alteração
mutation {
updateUser(id: "1", data: { email: "[email protected]" }) {
id
name
email
}
}
//Em uma nova aba do playground, adicione o body abaixo para fazer uma exclusão
mutation {
deleteUser(id: "1")
}

Veja também:

Entenda gerenciador de pacotes

Uma fração do CRUD

Assim encerramos nossa implementação de um CRUD básico de usuários, utilizando o NestJS, GraphQL e SGBD MySQL. O que foi ensinado nesse artigo é uma pequena fração de tudo que o CRUD essas demais tecnologias podem nos entregar!

Dessa forma, sugiro que a partir daqui, vocês explorem cada vez mais a própria documentação e qualquer dúvida, estou aberto para discussões através do Discord (Manoel Fernandes Neto#5644) ou Linkedin.

Autor: Manoel Fernandes Neto.

[adrotate banner=”5″]

Imagem padrão

Autor

Julia Ilkiu

Artigos relacionados

Receba nossa
newsletter

Assine nossa newsletter e receba as últimas
novidades sobre o mundo da tecnologia.

    Eu autorizo a Luby a usar meus dados para o envio de conteúdos personalizados.