Como criar uma API GraphQL com o Nest.js?

Neste artigo, você aprenderá a criar uma API GraphQL com o NestJS aproveitando sua arquitetura limpa e a segurança de tipos.

O NestJS é uma estrutura NodeJS que ajuda os usuários a criar aplicativos de back-end escalonáveis de nível empresarial, fornecendo segurança de tipo e um padrão arquitetônico rigoroso.

O GraphQL é uma linguagem de consulta para APIs (interfaces de programação de aplicativos) amplamente usada como alternativa ao REST devido à sua velocidade e estabilidade, que são alcançadas ao responder às consultas com os dados solicitados com precisão. Esse recurso do GraphQL resolve o problema de overfetching e underfetching apresentado pelo REST.

Configuração de seu ambiente de desenvolvimento

Se você não tiver a CLI do Nest.js instalada globalmente em seu sistema, execute o comando abaixo para instalá-la:

npm i -g @nestjs/cli

A CLI do Nest.js contém vários comandos que o ajudam a gerar código padrão para facilitar o processo de desenvolvimento.

Em seguida, crie o projeto e gere os arquivos iniciais necessários executando o comando abaixo:

nest new graphql-api

Em seguida, execute o comando abaixo para entrar no diretório do projeto:

cd graphql-api

Em seguida, instale as dependências necessárias executando o comando abaixo:

npm i @nestjs/graphql @nestjs/apollo graphql apollo-server-express

As dependências instaladas acima incluem:

  • nest/graphql
  • nestjs/apollo
  • graphql
  • apollo-server-express

Em seguida, instale o pacote TypeORM e o driver de banco de dados sqlite executando o comando abaixo:

npm install @nestjs/typeorm typeorm sqlite3

Você precisará dos pacotes instalados acima para se conectar, ler e gravar em um banco de dados.

Configuração do GraphQL no Nest.js

O Nest.js oferece duas maneiras de criar uma API GraphQL: as abordagens code-first e schema-first.

A abordagem code-first apresenta o uso de decoradores e classes TypeScript para gerar o esquema GraphQL correspondente.

A abordagem schema-first apresenta o uso de arquivos SDL (GraphQL Schema Definition Language), enquanto o Nest.js gera definições TypeScript usando classes ou interfaces baseadas no GraphQL Schema.

Este tutorial apresentará o uso da abordagem code-first para evitar a troca de contexto entre diferentes idiomas.

Para usar a abordagem de código primeiro, navegue até o arquivo de entrada do seu aplicativo, app.module.ts, e importe GraphQLModule de @nestjs/graphql. Assim:

import { GraphQLModule } from '@nestjs/graphql';

Em seguida, em sua matriz de importações, chame o método forRoot do GraphQLModule. Esse método recebe um objeto de configuração como argumento. As opções de configuração são passadas por meio do driver GraphQL subjacente (este tutorial apresenta o uso do driver Apollo). Defina a propriedade autoSchemaFile no objeto de configuração como schema.gql. Dessa forma:

//app.module.ts
GraphQLModule.forRoot({ autoSchemaFile: 'schema.gql' }),

A propriedade autoSchemaFile é o caminho do arquivo em que o esquema gerado automaticamente será armazenado.

Conexão a um banco de dados

Para conectar seu aplicativo a um banco de dados, navegue até o arquivo app.module.ts e importe TypeOrmModule de @nestjs/typeorm.

Assim:

import { TypeOrmModule } from '@nestjs/typeorm';

Em seguida, adicione o bloco de código abaixo à sua matriz de importações:

TypeOrmModule.forRoot({
      type: 'sqlite',
      database: ':memory:',
      entities: ['dist/**/*.entity{.ts,.js}'],
      synchronize: true,
    }),

O bloco de código acima cria uma conexão de banco de dados entre o seu aplicativo e um banco de dados Sqlite e compartilha a conexão com todos os módulos do seu aplicativo.

Geração de recursos

Em seguida, crie um módulo para organizar seu código executando o comando abaixo:

nest g module blog

O comando acima gera um módulo de blog para seu aplicativo.

Em seguida, execute o comando abaixo para criar um serviço:

nest g service blog

O bloco de código acima gera e registra um serviço no seu aplicativo. Esse serviço conterá e manipulará toda a lógica comercial do seu aplicativo.

Em seguida, execute o comando abaixo para criar um resolvedor:

nest g resolver

O bloco de código acima gera e registra um resolvedor no seu aplicativo. Um resolvedor especifica instruções para transformar as consultas e mutações do GraphQL em dados e retorna a forma dos dados especificados em um esquema de forma síncrona ou assíncrona.

Criação de uma entidade

Uma entidade é uma coleção de campos que especifica como os dados são armazenados em um banco de dados.

Para criar uma entidade, na pasta do módulo (blog), crie um arquivo de entidade seguindo a convenção .entity.ts, por exemplo, blog.entity.ts.

Em seu arquivo de entidade, importe a coluna, a entidade e a PrimaryGeneratedColumn do typeorm. Assim:

import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

O decorador Column adiciona os campos que ele anota como colunas em um banco de dados. O decorador Entity marca a classe que ele anota como uma entidade. O decorador PrimaryGeneratedColumn marca a propriedade que ele anota como o identificador exclusivo de sua tabela e a gera automaticamente.

Em seguida, crie e exporte uma classe, Blog. Em seguida, anote a classe com o decorador Entity para marcá-la como uma entidade. Assim:

@Entity()
export class Blog {}

Como este tutorial apresenta uma API de blog, adicione uma propriedade id, title, body, author e date à sua classe. Em seguida, anote cada uma das propriedades com o decorador Column, exceto a propriedade id. Anote a propriedade id com o decorador PrimaryGeneratedColumn.

Assim:

import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Blog {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @Column()
  body: string;

  @Column()
  author: string;

  @Column()
  date: string;
}

Criação de um tipo de objeto

Um tipo de objeto é uma definição de esquema GraphQL que representa o objeto com o qual o cliente precisa interagir. Por exemplo, se você estiver criando uma API de blog, precisará definir o esquema/entidade do blog com uma classe TypeScript e anotar cada campo com os decoradores apropriados.

Em seguida, em seu arquivo de entidade, importe o tipo Int e os decoradores Field e ObjectType de @nestjs/graphql. Assim:

import { Field, Int, ObjectType } from '@nestjs/graphql';

O decorador Field marca uma propriedade de classe específica como um campo GraphQL. Somente as propriedades anotadas com esse decorador serão definidas no esquema. Ele aceita uma função de tipo de retorno opcional e um objeto de configuração como argumentos.

O tipo Int é necessário para eliminar a possível ambiguidade entre os sistemas de tipos TypeScript e GraphQL. Ele é necessário apenas para propriedades com valores numéricos, pois eles podem ser inteiros ou flutuantes.

O decorador ObjectType marca uma classe como um tipo GraphQL.

Em seguida, anote sua classe Blog com o decorador ObjectType para marcá-la como um tipo GraphQL. Assim:

@ObjectType()
export class Blog {}

Por fim, especifique as propriedades necessárias do seu tipo dentro da classe e anote cada uma delas com o decorador Field. Por exemplo:

//blog.entity.ts
import { Field, Int, ObjectType } from '@nestjs/graphql';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
@ObjectType()
export class Blog {
  @PrimaryGeneratedColumn()
  @Field((type) => Int)
  id: number;

  @Column()
  @Field()
  title: string;

  @Column()
  @Field()
  body: string;

  @Column()
  @Field()
  author: string;

  @Column()
  @Field()
  date: string;
}

Seu Object Type finalizado deve se parecer com o bloco de código acima.

Observe que o id com um tipo de número no bloco de código acima foi explicitamente especificado como um número inteiro para evitar ambiguidade entre os sistemas de tipos do TypeScript e do GraphQL.

Por fim, injete a entidade no aplicativo para criar uma camada de acesso entre o aplicativo e o banco de dados.

Para isso, navegue até o arquivo blog.module.ts, importe TypeOrmModule de @nestjs/typeorm e importe sua entidade Blog.

Em seguida, adicione o bloco de código abaixo ao decorador do módulo:

imports: [TypeOrmModule.forFeature([Blog])],

O bloco de código acima cria um repositório que atuará como uma camada de acesso entre seu aplicativo e o banco de dados.

Criação de um serviço

Navegue até o arquivo de serviço gerado anteriormente, blog.service.ts, e importe InjectRepository de @nestjs/typeorm e seu tipo de objeto.

Assim:

import { InjectRepository } from '@nestjs/typeorm';
import { Blog } from './blog.entity';

Em seguida, adicione o bloco de código abaixo à sua classe BlogService para injetar seu repositório:

constructor(
    @InjectRepository(Blog)
    private blogRepository: Repository<Blog>,
  ) {}

Em seguida, adicione o bloco de código abaixo à sua classe de serviço para implementar a lógica de recuperação de todos os blogs no banco de dados:

async getAll(): Promise<Blog[]> {
    return await this.blogRepository.find();
  }

Em seguida, você precisará implementar a lógica para adicionar um novo blog ao seu banco de dados, mas, primeiro, será necessário criar um Input Type.

Criação de um tipo de entrada

Para criar um tipo de entrada para suas mutações que aceitam objetos complexos, crie uma pasta dtos na pasta do seu blog. Em seguida, na pasta dtos, crie um novo arquivo, create-blog.input.ts.

Em seu arquivo create-blog.input.ts, importe os decoradores Field e InputType. Assim:

import { Field, InputType } from '@nestjs/graphql';

O decorador InputType marca uma classe como um tipo de entrada GraphQL.

Em seguida, crie e exporte uma classe, BlogInput. Em seguida, anote a classe com o decorador InputType. Assim:

@InputType()
export class BlogInput {}

Por fim, adicione todas as propriedades necessárias do blog e anote-as com o decorador Field.

Assim:

//create-blog.input.ts
import { Field, InputType } from '@nestjs/graphql';

@InputType()
export class BlogInput {
  @Field()
  id: number;

  @Field()
  title: string;

  @Field()
  body: string;

  @Field()
  author: string;

  @Field()
  date: string;
}

Seu tipo de entrada finalizado deve se parecer com o bloco de código acima.

Em seguida, importe o tipo de entrada para o arquivo de serviço e adicione o bloco de código abaixo à classe de serviço para implementar a lógica de adição de um blog ao banco de dados:

async createBlog(blogInput: BlogInput): Promise<Blog> {
    const newBlog = this.blogRepository.create(blogInput);

    return this.blogRepository.save(newBlog);
  }

O tipo de entrada é aplicado da mesma forma que um objeto regular de transferência de dados do Nest.js, como um mecanismo de validação para os dados recebidos. Como os dtos, você também pode executar validação adicional nos dados usando o pacote class-validator.

Criação de um resolvedor

Navegue até o arquivo Resolver gerado anteriormente, blog.resolver.ts, e importe a entidade, o tipo de entrada e o serviço. Assim:

import { Blog } from './blog.entity';
import { BlogService } from './blog.service';
import { BlogInput } from './dto/create-blog.input';

Em seguida, adicione o bloco de código abaixo à sua classe Resolver para injetar seu serviço no resolvedor:

constructor(private blogService: BlogService) {}

Em seguida, importe os decoradores Args, Mutation e Query de @nestjs/graphql.

Assim:

import { Args, Mutation, Query } from '@nestjs/graphql';

O decorador Args extrai o objeto arguments da plataforma subjacente e preenche o parâmetro decorado com o valor de todos os argumentos ou de um único argumento especificado.

O decorador Mutation especifica uma rota como uma mutação.

O decorador Query especifica uma rota como uma consulta.

Em seguida, passe a função abaixo para o decorador Resolver para especificar o tipo de objeto:

(of) => Blog

Em seguida, adicione o bloco de código abaixo à sua classe Resolver para resolver o método getAll.

 @Query((type) => [Blog])
  async getAllBlogs() {
    return this.blogService.getAll();
  }

Por fim, adicione o bloco de código abaixo à sua classe Resolver para resolver o método createBlog.

 @Mutation((returns) => Blog)
  createBlog(@Args('blogInput') blogInput: BlogInput): Promise<Blog> {
    return this.blogService.createBlog(blogInput);
  }

Observação: não se esqueça de especificar a função do tipo de retorno para cada decorador.

Sua classe Resolver finalizada deve se parecer com o bloco de código abaixo:

@Resolver((of) => Blog)
export class BlogResolver {
  constructor(private blogService: BlogService) {}

  @Query((type) => [Blog])
  async getAllBlogs() {
    return this.blogService.getAll();
  }

  @Mutation((returns) => Blog)
  createBlog(@Args('blogInput') blogInput: BlogInput): Promise<Blog> {
    return this.blogService.createBlog(blogInput);
  }
}

Testar sua API usando o GraphQL Playground

Para testar seu aplicativo usando o playground GraphQL, disponível por padrão, execute o comando abaixo e navegue até http://localhost:3000/graphql.

npm start

Em seguida, você verá um playground onde poderá testar suas consultas e mutações, conforme mostrado na imagem abaixo.

Playground do GraphQL

Para recuperar todos os blogs do banco de dados, execute a consulta abaixo:

query getAll{
  getAllBlogs {
    id,
    title,
    body,
    author,
    date
  }
}

Um exemplo é mostrado na imagem abaixo:

Para adicionar um blog ao seu banco de dados, execute a mutação abaixo:

mutation createBlog {
  createBlog (blogInput: {
    id: 1, 
    title: "GraphQL is great", 
    body: "GraphQL APIs are faster than REST APIs", 
    author: "David Ekete",
    date: "01-02-03"
  }) {
    id,
    title,
    body,
    author,
    date,
  }
}

Um exemplo é mostrado na imagem abaixo:

Playground do GraphQL

Back4app e GraphQL

Devido às muitas vantagens que o GraphQL tem sobre o REST, como o controle de busca de dados, a plataforma Back4app fornece uma API GraphQL integrada aos seus aplicativos de análise em execução na plataforma.

Essa integração permite que você verifique a integridade do backend do seu servidor, execute operações de criação, leitura, atualização e exclusão (CRUD) no seu banco de dados e muito mais, escrevendo consultas e mutações GraphQL simples.

Por exemplo, para verificar a integridade do seu servidor de back-end usando a API GraphQL, execute a consulta abaixo:

query Health {
  health
}

A consulta acima retornará:

{
  "data": {
    "health": true
  }
}

Para começar a usar o recurso GraphQL, navegue até o painel do Parse, selecione e clique no seu aplicativo. Na interface do usuário do aplicativo, selecione e clique em Core, depois selecione e clique em API console e escolha o console GraphQL.

Seguir as instruções acima abrirá um console GraphQL no qual você poderá executar suas consultas e mutações semelhantes ao playground GraphQL que você usou no desenvolvimento. Você pode saber mais sobre o recurso GraphQL do Back4app na documentação do Back4app.

Conclusão

Este artigo abordou como você pode criar uma API GraphQL no Nest.js e testar a API com o playground do GraphQL. As APIs GraphQL têm várias vantagens sobre as APIs REST.

No entanto, eles não são ideais para todos os aplicativos. Não deixe de considerar os prós e os contras do GraphQL e do REST antes de escolher o que seria adequado para seu aplicativo.

Independentemente de sua escolha, GraphQL ou REST, você ainda precisará implantar seu aplicativo. O Back4app é um BaaS de baixo código baseado em tecnologias de código aberto que simplifica o processo de criação de back-end, eliminando tarefas repetitivas usando modelos reutilizáveis e gerenciando a maior parte de sua infraestrutura de back-end.

Alguns dos recursos do Back4app incluem um banco de dados em tempo real, funções de nuvem, APIs e SDKs nativos, um sistema completo de gerenciamento de usuários e muito mais.

Essa plataforma também apresenta um nível gratuito que permite que você explore a plataforma sem pagar uma taxa.

Boa construção!

PERGUNTAS FREQUENTES

O que é GraphQL?

GraphQL é uma linguagem de consulta e manipulação de dados de código aberto para APIs, criada pelo Facebook em 2012.

O que é Nest.JS?

Nest.JS é uma estrutura Node.js de código aberto para criar aplicativos eficientes e escaláveis ​​do lado do servidor.

Como criar uma API GraphQL com Nest.JS?

– Configurar GraphQL em Nest.js
– Conectar a um banco de dados
– Gerar recursos
– Criar uma entidade
– Criar um tipo de objeto
– Criar um serviço


Leave a reply

Your email address will not be published.