Como criar uma API GraphQL segura do Node.js
Publicados: 2022-03-11Neste artigo, nosso objetivo é apresentar um guia rápido sobre como criar uma API GraphQL segura do Node.js.
Algumas perguntas que podem vir à mente podem ser:
- Qual é o objetivo de usar uma API GraphQL?
- O que é uma API GraphQL?
- O que é uma consulta GraphQL?
- Qual é o benefício do GraphQL?
- GraphQL é melhor que REST?
- Por que usamos Node.js?
Todas essas são perguntas válidas, mas antes de respondê-las, devemos mergulhar em uma breve visão geral do estado atual do desenvolvimento web:
- Quase todas as soluções que você encontrará hoje usam algum tipo de interface de programação de aplicativos (API).
- Mesmo que você use apenas uma rede social, como Facebook ou Instagram, ainda está conectado a um front-end que consome uma API.
- Se você estiver curioso, descobrirá que quase todos os serviços de entretenimento online usam um tipo diferente de API, incluindo serviços como Netflix, Spotify e YouTube.
Em praticamente todos os cenários, você encontrará uma API que não precisa conhecer em detalhes, por exemplo, não precisa saber como elas foram construídas e não precisa usar a mesma tecnologia que costumavam ser capaz de integrá-lo em seu próprio sistema. A API fornecida permite oferecer uma forma de comunicação entre serviços em um padrão comum que tanto o serviço quanto o cliente podem se comunicar sem depender de uma pilha de tecnologia específica.
Com uma API bem estruturada, é possível ter uma API sólida, sustentável e escalável que pode atender a vários tipos de clientes e aplicativos front-end.
Dito isto, o que é a API GraphQL?
GraphQL é uma linguagem de consulta para APIs, desenvolvida para uso interno no Facebook e publicada para uso público em 2015. Suporta leitura, escrita e atualizações em tempo real. Também é de código aberto e é comumente comparado ao REST e outras arquiteturas. É, em poucas palavras, baseado em:
- Consultas GraphQL - Isso permite que o cliente leia e manipule como os dados devem ser recebidos.
- Mutações GraphQL - Isto é como escrever dados no servidor. É a convenção do GraphQL sobre como gravar dados no sistema.
Embora este artigo deva demonstrar um cenário simples, mas real, sobre como criar e usar APIs do GraphQL, não forneceremos uma introdução detalhada ao GraphQL. A razão é simples, pois a equipe do GraphQL fornece documentação abrangente e lista várias práticas recomendadas em sua Introdução ao GraphQL.
O que é uma consulta GraphQL?
Conforme descrito anteriormente, uma consulta é a maneira como um cliente pode ler e manipular dados da API. Você pode passar o tipo de um objeto e selecionar que tipo de campos deseja receber de volta. Uma consulta simples seria como a seguinte:
query{ users{ firstName, lastName } }
Nesta consulta, estamos tentando alcançar todos os usuários do esquema de nossos usuários, mas recebendo apenas firstName
e lastName
. O resultado desta consulta seria como, por exemplo:
{ "data": { "users": [ { "firstName": "Marcos", "lastName": "Silva" }, { "firstName": "Paulo", "lastName": "Silva" } ] } }
É bastante simples para uso do cliente.
Qual é o objetivo de usar uma API GraphQL?
O objetivo de criar uma API é a capacidade de ter software como serviço que pode ser integrado por outros serviços externos. Mesmo que sua aplicação seja consumida por um único front-end, você pode considerar esse front-end como um serviço externo e, para isso, poderá trabalhar em projetos diferentes quando a comunicação entre os dois for fornecida via API.
Se você trabalha em uma grande equipe, ela pode ser dividida para criar uma equipe de front-end e back-end, permitindo que ambos usem a mesma tecnologia e facilitem seus trabalhos. Ao arquitetar uma API, é importante escolher a que melhor se adequa ao projeto e o que o aproxima da solução desejada.
Neste artigo, focaremos em um esqueleto para construir uma API que usa GraphQL.
O GraphQL é melhor que o REST?
Pode ser meio esquisito, mas não consigo evitar: depende .
O GraphQL é uma abordagem que se encaixa muito bem em vários cenários. REST é uma abordagem de arquitetura que também é comprovada em vários cenários. Hoje em dia, existem muitos artigos que explicam por que um é melhor que o outro ou por que você deve usar apenas REST em vez de GraphQL. E também, muitas maneiras de usar o GraphQL internamente e ainda manter os endpoints da API como uma arquitetura baseada em REST.
A melhor orientação seria conhecer os benefícios de cada abordagem, analisar a solução que você está criando, avaliar o quão confortável sua equipe está trabalhando com a solução e avaliar se você será capaz ou não de orientar sua equipe para aprender e se preparar acelere rapidamente antes de escolher entre as abordagens.
Este artigo é mais um guia prático do que uma comparação subjetiva de GraphQL e REST. Caso você queira ler uma comparação detalhada dos dois, sugiro que você confira outro de nossos artigos, GraphQL vs. REST - A GraphQL Tutorial.
No artigo de hoje, vamos nos concentrar na criação de uma API GraphQL usando Node.js.
Por que usamos o Node.js?
O GraphQL tem várias bibliotecas diferentes que você pode usar. Para os propósitos deste artigo, decidimos seguir com a ideia de usar JavaScript com Node.js devido ao seu uso generalizado e ao fato de que o Node.js permite que os desenvolvedores usem a sintaxe de front-end familiar para o desenvolvimento do lado do servidor.
Também é útil comparar com nossa abordagem com uma API baseada em REST, semelhante à que foi demonstrada em outro artigo do Toptal Engineering Blog: Criando uma API REST segura em Node.js. Este artigo também mostra o uso do Node.js com o Express para desenvolver uma API REST de esqueleto que permitirá comparar algumas diferenças entre essas duas abordagens. O Node.js também foi projetado com aplicativos de rede escaláveis, uma comunidade global e várias bibliotecas de código aberto que você pode encontrar no site do npm.
Desta vez, vamos mostrar como construir uma API de esqueleto com GraphQL, Node.js e Express!
Tutorial prático do GraphQL
Conforme descrito anteriormente, construiremos uma ideia básica para a API GraphQL, e você precisará conhecer os conceitos básicos do Node.js e do Express antes de continuar. O código fonte do projeto feito para este exemplo do GraphQL está disponível aqui.
Vamos lidar com dois tipos de recursos:
- Usuários, para os quais lidaremos com um CRUD básico.
- Produtos, para os quais teremos um pouco de detalhe para mostrar mais do poder do GraphQL.
Os usuários conterão a seguinte estrutura:
- identificação
- primeiro nome
- último nome
- o email
- senha
- nível de permissão
Os produtos conterão a seguinte estrutura:
- identificação
- nome
- Descrição
- preço
Quanto ao padrão de codificação, vamos usar o TypeScript para este projeto. No arquivo de origem, você poderá configurar tudo para começar a codificar com o TypeScript.
Vamos Codificar!
Em primeiro lugar, certifique-se de ter a versão mais recente do Node.js instalada. No momento da publicação, a versão atual é 10.15.3, conforme Nodejs.org.
Inicializando o Projeto
Vamos começar em uma nova pasta que podemos chamar node-graphql
. Lá, podemos abrir um terminal ou um console Git CLI e iniciar a mágica usando o seguinte comando: npm init
.
Configurando nossas dependências e TypeScript
Para acelerar o processo, substituir seu package.json
pelo seguinte em nosso repositório Git deve conter todas as dependências necessárias:
{ "name": "node-graphql", "version": "1.0.0", "description": "", "main": "dist/index.js", "scripts": { "tsc": "tsc", "start": "npm run tsc && node ./build/app.js" }, "author": "", "license": "ISC", "dependencies": { "@types/express": "^4.16.1", "@types/express-graphql": "^0.6.2", "@types/graphql": "^14.0.7", "express": "^4.16.4", "express-graphql": "^0.7.1", "graphql": "^14.1.1", "graphql-tools": "^4.0.4" }, "devDependencies": { "tslint": "^5.14.0", "typescript": "^3.3.4000" } }
Com o package.json
atualizado, basta acessar o terminal novamente e usar: npm install
. Ele instalará todas as dependências necessárias para executar essa API GraphQL no Node.js e no Express.
A próxima parte é configurar nosso modo TypeScript. Precisamos de um arquivo chamado tsconfig.json
em nossa pasta raiz com o seguinte:
{ "compilerOptions": { "target": "ES2016", "module": "commonjs", "outDir": "./build", "strict": true, "esModuleInterop": true } }
A lógica do código para esta configuração estará presente na pasta do aplicativo. Lá podemos criar um arquivo app.ts
e para testes básicos adicionar o seguinte código lá:
console.log('Hello Graphql Node API tutorial');
Pela nossa configuração, agora podemos executar npm start
e aguardar uma compilação e poder testar se tudo está funcionando corretamente. Em seu console de terminal, você deve ver nosso “tutorial da API do nó do Hello GraphQL”. Na cena de fundo, a configuração basicamente compila o código TypeScript em JavaScript puro e então executa nosso build na pasta build
.

Agora vamos configurar um esqueleto básico para nossa API GraphQL. Para iniciar nosso projeto, vamos adicionar três importações básicas:
- Expressar
- Express-graphql
- Ferramentas Graphql
Vamos começar a juntar tudo:
import express from 'express'; import graphqlHTTP from 'express-graphql'; import {makeExecutableSchema} from 'graphql-tools';
Agora devemos ser capazes de começar a codificar um pouco. O próximo passo é lidar com nosso aplicativo no Express e a configuração básica do GraphQL como:
import express from 'express'; import graphqlHTTP from 'express-graphql'; import {makeExecutableSchema} from 'graphql-tools'; const app: express.Application = express(); const port = 3000; let typeDefs: any = [` type Query { hello: String } type Mutation { hello(message: String) : String } `]; let helloMessage: String = 'World!'; let resolvers = { Query: { hello: () => helloMessage }, Mutation: { hello: (_: any, helloData: any) => { helloMessage = helloData.message; return helloMessage; } } }; app.use( '/graphql', graphqlHTTP({ schema: makeExecutableSchema({typeDefs, resolvers}), graphiql: true }) ); app.listen(port, () => console.log(`Node Graphql API listening on port ${port}!`));
O que estamos fazendo é:
- Ativando a porta 3000 para nosso aplicativo de servidor Express.
- Definindo quais consultas e mutações queremos usar como um exemplo rápido.
- Definindo como as consultas e mutações vão funcionar.
OK, mas o que está acontecendo para typeDefs e resolvedores, bem como relação a consultas e mutações?
- typeDefs - A definição do nosso esquema do que podemos esperar de consultas e mutações.
- Resolvedores - Ao invés da expectativa de campos ou parâmetros obrigatórios, aqui definimos as funções e comportamentos de como deveriam funcionar as consultas e mutações.
- Consultas - O “gets” que queremos ler do servidor.
- Mutações - Nossas solicitações que afetarão quaisquer dados que tenhamos em nosso próprio servidor.
Agora, vamos executar o npm start novamente para ver o que temos lá. Esperamos que o aplicativo seja executado com a seguinte mensagem: API do Node Graphql escutando na porta 3000!
Agora podemos tentar consultar e testar a API GraphQL em nosso próprio servidor via: http://localhost:3000/graphql
Ótimo, agora podemos escrever nossa primeira consulta que foi definida como "olá".
Observe que da forma que definimos no typeDefs
, a página pode nos ajudar a construir a consulta.
Isso é ótimo, mas como podemos alterar o valor? Mutações!
Agora, vamos ver o que acontece quando alteramos nosso valor na memória com uma mutação:
Agora podemos fazer operações CRUD básicas com nossa API GraphQL Node.js. Vamos avançar com nosso código agora.
Produtos
Para produtos, usaremos um módulo chamado produtos. Como um esforço para simplificar este artigo, vamos usar um banco de dados na memória apenas para demonstração. Vamos definir um modelo e um serviço para gerenciar produtos.
Nosso modelo será baseado da seguinte forma:
export class Product { private id: Number = 0; private name: String = ''; private description: String = ''; private price: Number = 0; constructor(productId: Number, productName: String, productDescription: String, price: Number) { this.id = productId; this.name = productName; this.description = productDescription; this.price = price; } }
O serviço que se comunicará com o GraphQL será definido como:
export class ProductsService { public products: any = []; configTypeDefs() { let typeDefs = ` type Product { name: String, description: String, id: Int, price: Int } `; typeDefs += ` extend type Query { products: [Product] } `; typeDefs += ` extend type Mutation { product(name:String, id:Int, description: String, price: Int): Product! }`; return typeDefs; } configResolvers(resolvers: any) { resolvers.Query.products = () => { return this.products; }; resolvers.Mutation.product = (_: any, product: any) => { this.products.push(product); return product; }; } }
Comercial
Para os usuários, seguiremos a mesma estrutura do módulo de produtos. Teremos um modelo e um serviço para os usuários. O modelo será definido como:
export class User { private id: Number = 0; private firstName: String = ''; private lastName: String = ''; private email: String = ''; private password: String = ''; private permissionLevel: Number = 1; constructor(id: Number, firstName: String, lastName: String, email: String, password: String, permissionLevel: Number) { this.id = id; this.firstName = firstName; this.lastName = lastName; this.email = email; this.password = password; this.permissionLevel = permissionLevel; } }
Enquanto isso, nosso serviço será como:
const crypto = require('crypto'); export class UsersService { public users: any = []; configTypeDefs() { let typeDefs = ` type User { firstName: String, lastName: String, id: Int, password: String, permissionLevel: Int, email: String } `; typeDefs += ` extend type Query { users: [User] } `; typeDefs += ` extend type Mutation { user(firstName:String, lastName: String, password: String, permissionLevel: Int, email: String, id:Int): User! }`; return typeDefs; } configResolvers(resolvers: any) { resolvers.Query.users = () => { return this.users; }; resolvers.Mutation.user = (_: any, user: any) => { let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512', salt).update(user.password).digest("base64"); user.password = hash; this.users.push(user); return user; }; } }
Como lembrete, o código-fonte está disponível para uso neste link.
Agora podemos jogar e testar nosso código. Vamos executar npm start
. Teremos o servidor rodando na porta 3000. Agora podemos acessar o GraphQL para testes em http://localhost:3000/graphql.
Vamos tentar uma mutação para adicionar um item à nossa lista de produtos:
Para testar se funcionou, usaremos agora uma consulta para produtos, mas recebendo apenas id
, name
e price
:
query{ products{ id, name, price } } The response will be: { "data": { "products": [ { "id": 100, "name": "My amazing product", "price": 400 } ] } }
E é isso; o produto está funcionando como esperado. Agora podemos jogar e mudar os campos se quisermos. Você pode tentar adicionar uma descrição:
query{ products{ id, name, description, price } }
Agora podemos ter as descrições dos nossos produtos. Vamos tentar usuários agora.
mutation{ user(id:200, firstName:"Marcos", lastName:"Silva", password:"amaz1ingP4ss", permissionLevel:9, email:"[email protected]") { id } }
E uma consulta será como:
query{ users{ id, firstName, lastName, password, email } }
Com uma resposta como:
{ "data": { "users": [ { "id": 200, "firstName": "Marcos", "lastName": "Silva", "password": "kpj6Mq0tGChGbZ+BT9Nw6RMCLReZEPPyBCaUS3X23lZwCCp1Ogb94/oqJlya0xOBdgEbUwqRSuZRjZGhCzLdeQ==", "email": "[email protected]" } ] } }
E agora nosso esqueleto GraphQL está pronto! Há muitos passos daqui para uma API útil e totalmente funcional, mas o núcleo básico agora está definido.
Resumo e Considerações Finais
Mesmo cortando arestas para encurtar, o artigo é bem grande com muitas informações básicas sobre o desenvolvimento de uma API GraphQL Node.js.
Vamos rever o que cobrimos até agora:
- Uso de Node.js com Express e GraphQL para construir uma API GraphQL;
- Uso básico do GraphQL;
- Uso básico de consultas e mutações;
- Abordagem básica para criar módulos para seu projeto;
- Testando nossa API GraphQL;
Para focar mais no lado do desenvolvimento das coisas, evitamos vários itens importantes que podem ser resumidos da seguinte forma:
- Validações para novos itens;
- Manipulação de erros adequadamente com um serviço de erro genérico;
- Validação de campos que um usuário pode usar em cada solicitação com um serviço genérico;
- Adicione um interceptor JWT para proteger a API;
- Lidar com hash de senha com uma abordagem mais eficaz;
- Adicionar testes unitários e de integração;
Lembre-se que temos o código fonte completo neste link do Git. Sinta-se à vontade para usar, bifurcar, abrir problemas, fazer pull requests e brincar com isso! Observe que todos os padrões e sugestões feitas neste artigo não são esculpidos em pedra.
Esta é apenas uma das muitas abordagens que podem ser usadas para começar a projetar sua própria API GraphQL. Além disso, não deixe de ler e explorar o GraphQL com mais detalhes, aprendendo o que ele tem a oferecer e como pode tornar suas APIs ainda melhores.