GraphQL é a nova palavra-chave no desenvolvimento de API. Enquanto as APIs RESTful continuam sendo a forma mais popular de expor dados de aplicativos, elas vêm com muitas limitações que o GraphQL planeja resolver.

GraphQL é uma linguagem de consulta criada pelo Facebook, transformada em um projeto de código aberto em 2015. Ele oferece uma sintaxe intuitiva e flexível para descrever e acessar dados em uma API.

Este guia irá explorar como construir um projeto GraphQL Node.js. Usaremos o GraphQL para construir um aplicativo Todo no framework web do Express.js para Node.

O que é GraphQL?

A partir da documentação oficial: “GraphQL é uma linguagem de consulta para APIs e um tempo de execução para preencher essas consultas com seus dados existentes. GraphQL fornece uma descrição completa e compreensível dos dados em sua API, dá aos clientes o poder de pedir exatamente o que eles precisam e nada mais, facilita a evolução das APIs ao longo do tempo e permite poderosas ferramentas de desenvolvimento”

GraphQL é um tempo de execução do lado do servidor para executar consultas usando o tipo de sistema que você definiu para seus dados. Além disso, o GraphQL não está ligado a nenhum banco de dados ou mecanismo de armazenamento específico. Ao invés disso, ele é apoiado pelo seu código e armazenamento de dados existentes. Você pode obter uma comparação detalhada dessas tecnologias com o guia GraphQL vs RESTful API.

Para criar um serviço GraphQL, você começa definindo tipos de schemas e criando campos usando esses tipos. Em seguida, você fornece um resolvedor de função a ser executado em cada campo e digita sempre que os dados são solicitados pelo lado do cliente.

Terminologia do GraphQL

O sistema do tipo GraphQL é usado para descrever quais dados podem ser consultados e quais dados você pode manipular. É o núcleo do GraphQL. Discutiremos diferentes maneiras de descrever e manipular os dados no GraphQ.

Tipos de objetos

Os tipos de objetos GraphQL são modelos de dados contendo campos fortemente digitados. Deve haver um mapeamento de 1 para 1 entre seus modelos e os tipos de GraphQL. Abaixo está um exemplo de tipos de GraphQL:

type User {
  id: ID! # The "!" means required
  firstname: String
  lastname: String
  email: String
  username: String
  todos: [Todo] # Todo is another GraphQL type
}

Query

GraphQL Query define todas as consultas que um cliente pode executar na API GraphQL. Você deve definir um RootQuery que conterá todas as consultas existentes por convenção.

Abaixo, definimos e mapeamos as consultas para a API RESTful correspondente:

type RootQuery {
  user(id: ID): User           # Corresponds to GET /api/users/:id
  users: [User]                # Corresponds to GET /api/users
  todo(id: ID!): Todo    # Corresponds to GET /api/todos/:id
  todos: [Todo]          # Corresponds to GET /api/todos
}

Mutações

Se as consultas do GraphQL são pedidos GET, as mutações são POST, PUT, PATCH, e DELETE pedidos que manipulam a API do GraphQL.

Colocaremos todas as mutações em um único RootMutation para demonstrar:

type RootMutation {
  createUser(input: UserInput!): User             # Corresponds to POST /api/users
  updateUser(id: ID!, input: UserInput!): User    # Corresponds to PATCH /api/users
  removeUser(id: ID!): User                       # Corresponds to DELETE /api/users
  createTodo(input: TodoInput!): Todo
  updateTodo(id: ID!, input: TodoInput!): Todo
  removeTodo(id: ID!): Todo
}

Você notou o uso de -input tipos para as mutações como UserInput, TodoInput. É sempre a melhor prática definir sempre os tipos de entrada para criar e atualizar seus recursos.

Um tipo de entrada pode ser definido como:

input UserInput {
  firstname: String!
  lastname: String
  email: String!
  username: String!
}

Resolvers

Os resolvers dizem ao GraphQL o que fazer quando cada consulta ou mutação é solicitada. É uma função básica que faz o trabalho duro de acertar a camada do banco de dados para fazer as operações CRUD (criar, ler, atualizar, excluir), acertar um endpoint interno RESTful API, ou chamar um microsserviço para atender a solicitação do cliente.

Você pode criar um novo arquivo resolvers.js e adicionar o seguinte código:

import sequelize from '../models';
export default function resolvers () {
  const models = sequelize.models;
  return {
    // Resolvers for Queries
    RootQuery: {
      user (root, { id }, context) {
        return models.User.findById(id, context);
      },
      users (root, args, context) {
        return models.User.findAll({}, context);
      }
    },
    User: {
      todos (user) {
        return user.getTodos();
      }
    },
  }
  // Resolvers for Mutations
  RootMutation: {
    createUser (root, { input }, context) {
      return models.User.create(input, context);    
    },
    updateUser (root, { id, input }, context) {
      return models.User.update(input, { ...context, where: { id } });
    },
    removeUser (root, { id }, context) {
      return models.User.destroy(input, { ...context, where: { id } });
    },
    // ... Resolvers for Todos go here
  }
}

Schema

Um Schema GraphQL é o que expõe o GraphQL ao mundo. Portanto, tipos, consultas e mutações estão contidos em schemas e expostos ao mundo.

Abaixo está como expor tipos, consultas e mutações para o mundo:

schema {
  query: RootQuery
  mutation: RootMutation
}

No script acima, incluímos o RootQuery e RootMutation que criamos anteriormente para ser exposto ao mundo.

Como o GraphQL funciona com o Node.js e Express.js?

GraphQL fornece uma implementação para todas as principais linguagens de programação, e o Node.js não está isento. No site oficial do GraphQL, há uma seção para suporte ao JavaScript, e também, há outras implementações do GraphQL para tornar simples a escrita e a codificação.

GraphQL Apollo fornece uma implementação para Node.js e Express.js e facilita começar a usar o GraphQL.

Você aprenderá como criar e desenvolver seu primeiro aplicativo GraphQL no Node.js e Express.js backend framework usando o GraphQL Apollo na próxima seção.

Configurando o GraphQL com o Express.js

A construção de um servidor GraphQL API com o Express.js é simples para começar. Nesta seção, vamos explorar como construir um servidor GraphQL.

Inicialize o projeto com o Express.js

Primeiro, você precisa instalar e configurar um novo projeto Express.js. Crie uma pasta para o seu projeto e instale o Express.js usando este comando:

cd <project-name> && npm init -y
npm install express

O comando acima cria um novo arquivo package.json e instala a biblioteca Express.js em seu projeto.

A seguir, estruturaremos nosso projeto como mostrado na imagem abaixo. Ele conterá diferentes módulos para as funcionalidades do projeto, tais como usuários, todos, etc.

Arquivos para o graphql-todo
Arquivos para o graphql-todo.

Inicialize o GraphQL

Vamos começar instalando as dependências do GraphQL Express.js. Execute o seguinte comando para instalar:

npm install apollo-server-express graphql @graphql-tools/schema --save

Criando schemas e Types

A seguir, vamos criar um arquivo index.js na pasta de módulos e adicionar o seguinte snippet de código:

const { gql } = require('apollo-server-express');
const users = require('./users');
const todos = require('./todos');
const { GraphQLScalarType } = require('graphql');
const { makeExecutableSchema } = require('@graphql-tools/schema');
const typeDefs = gql`
  scalar Time
  type Query {
    getVersion: String!
  }
  type Mutation {
    version: String!
  }
`;
const timeScalar = new GraphQLScalarType({
  name: 'Time',
  description: 'Time custom scalar type',
 serialize: (value) => value,
});
const resolvers = {
  Time: timeScalar,
  Query: {
    getVersion: () => `v1`,
  },
};
const schema = makeExecutableSchema({
  typeDefs: [typeDefs, users.typeDefs, todos.typeDefs],
  resolvers: [resolvers, users.resolvers, todos.resolvers],
});
module.exports = schema;

Análise do código

Vamos rever o snippet de código passo a passo:

Passo 1

Primeiro, importamos as bibliotecas necessárias e criamos os tipos de consulta e mutação padrão. A consulta e a mutação definiram apenas a versão da API GraphQL por enquanto. Entretanto, estendamos a consulta e a mutação para incluir outros schemas conforme prosseguimos.

Importação de GraphQL e extensões
Importação de GraphQL e extensões.
Passo 2:

Criamos um novo tipo de escalar por tempo e nosso primeiro resolvedor para a consulta e mutação criada acima. Além disso, também geramos um schema usando a função makeExecutableSchema.

O schema gerado incluirá todos os outros schemas que você importou e ainda mais após criá-los e importá-los.

Criando um tipo escalar para o tempo, bem como nosso primeiro resolver.
Criando um tipo escalar para o tempo, bem como nosso primeiro resolver.

O snippet de código acima mostra que importamos diferentes schemas para a função makeExecutableSchema. Esta abordagem nos ajuda na estruturação do aplicativo por complexidade. Em seguida, criaremos os schemas Todo e Usuário que importamos.

Criando o schema Todo

O Schema Todo mostra operações simples de CRUD que os usuários do aplicativo podem realizar. Abaixo está o schema que implementa a operação de Todo CRUD.

const { gql } = require('apollo-server-express');
const createTodo = require('./mutations/create-todo');
const updateTodo = require('./mutations/update-todo');
const removeTodo = require('./mutations/delete-todo');
const todo = require('./queries/todo');
const todos = require('./queries/todos');
const typeDefs = gql`
  type Todo {
    id: ID!
    title: String
    description: String
    user: User
  }
  input CreateTodoInput {
    title: String!
    description: String
    isCompleted: Boolean
  }
  input UpdateTodoInput {
    title: String
    description: String
    isCompleted: Boolean
  }  extend type Query {
    todo(id: ID): Todo!
    todos: [Todo!]
  }
  extend type Mutation {
    createTodo(input: CreateTodoInput!): Todo
    updateTodo(id: ID!, input: UpdateTodoInput!): Todo
    removeTodo(id: ID!): Todo
  }
`;
// Provide resolver functions for your schema fields
const resolvers = {
  // Resolvers for Queries
  Query: {
    todo,
    todos,
  },
  // Resolvers for Mutations
  Mutation: {
    createTodo,
    updateTodo,
    removeTodo,
  },
};
module.exports = { typeDefs, resolvers };

Análise do código

Vamos rever o snippet de código passo a passo:

Passo 1:

Primeiro, criamos um Schema Todo usando GraphQL type, input, e extend. A palavra-chave extend é usada para herdar e adicionar novas consultas e mutações à consulta raiz existente e à mutação que criamos acima.

Criando o Schema Todo
Criando o Schema Todo.
Passo 2:

Em seguida, criamos um resolvedor, usado para recuperar os dados corretos quando uma determinada consulta ou mutação é chamada.

Criando um resolver
Criando um resolver.

Com a função resolver implementada, podemos criar métodos individuais para a lógica de negócios e manipulação do banco de dados, como mostrado no exemplo do create-todo.js.

Crie um arquivo create-user.js na pasta ./mutations/ e adicione a lógica do negócio para criar um novo Todo em seu banco de dados.

const models = require('../../../models');
module.exports = async (root, { input }, context) => {
  return models.todos.push({ ...input });
};

O snippet de código acima é uma forma simplificada de criar um novo Todo em nosso banco de dados usando o ORM Sequelize. Você pode aprender mais sobre o Sequelize e como configurá-lo com o Node.js.

Você pode seguir o mesmo passo para criar muitos schemas dependendo do seu aplicativo ou você pode clonar o projeto completo do GitHub.

Em seguida, configuraremos o servidor com o Express.js e executar o aplicativo Todo recém-criada com GraphQL e Node.js.

Configurando e executando o servidor

Finalmente, configuraremos nosso servidor usando a biblioteca apollo-server-express que instalamos anteriormente.

O apollo-server-express é um pacote simples do Apollo Server para Express.js, é recomendado porque foi desenvolvido para se encaixar no desenvolvimento do Express.js.

Usando os exemplos que discutimos acima, vamos configurar o servidor Express.js para trabalhar com o recém-instalado apollo-server-express.

Crie um arquivo server.js no diretório raiz e cole no seguinte código:


const express = require('express');
const { ApolloServer } = require('apollo-server-express');
const schema = require('./modules');
const app = express();
async function startServer() {
  const server = new ApolloServer({ schema });
  await server.start();
  server.applyMiddleware({ app });
}
startServer();
app.listen({ port: 3000 }, () =>
  console.log(`Server ready at http://localhost:3000`)
);

O código acima criou com sucesso o primeiro servidor GraphQL CRUD para Todos e Usuários. Você pode iniciar o servidor de desenvolvimento e acessar o playground via http://localhost:3000/graphql. Se tudo der certo, você verá a seguinte tela:

A tela de verificação
A tela de verificação.

Resumo

GraphQL é uma tecnologia moderna suportada pelo Facebook que simplifica o tedioso trabalho envolvido na criação de APIs de larga escala com padrões arquitetônicos RESTful.

Este guia se aprofundou no GraphQL e mostrou como criar sua primeira API GraphQL usando Express.js.

Deixe-nos saber o que você constrói com GraphQL nos comentários abaixo.

Solomon Eseme

Sou um Engenheiro de Software e Criador de Conteúdo voltado para a construção de produtos de alta performance e inovadores, seguindo as melhores práticas e padrões da indústria. Também adoro escrever sobre isso no Masteringbackend.com. Siga-me no X, LinkedIn e About Me.