Construindo APIs GraphQL com Node

Publicados: 2022-12-20

GraphQL é a nova palavra da moda no desenvolvimento de APIs. Embora as APIs RESTful continuem sendo a maneira mais popular de expor dados de aplicativos, elas vêm com muitas limitações que o GraphQL pretende resolver.

GraphQL é uma linguagem de consulta criada pelo Facebook, que foi transformada em um projeto de código aberto em 2015. 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 criar um aplicativo Todo na estrutura da Web Express.js para Node.

O que é GraphQL?

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

GraphQL é um tempo de execução do lado do servidor para executar consultas usando o sistema de tipo que você definiu para seus dados. Além disso, o GraphQL não está vinculado a nenhum banco de dados ou mecanismo de armazenamento específico. Em vez disso, ele é respaldado por 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, comece definindo tipos de esquema e criando campos usando esses tipos. Em seguida, você fornece um resolvedor de função para ser executado em cada campo e digita sempre que os dados são solicitados pelo lado do cliente.

Terminologia do GraphQL

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

tipos

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

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

Consultas

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 GraphQL forem solicitações GET , as mutações serão solicitações POST , PUT , PATCH e DELETE que manipulam a API 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 tipos -input para as mutações, como UserInput , TodoInput . É sempre uma prática recomendada sempre definir tipos de entrada para criar e atualizar seus recursos.

Você pode definir os tipos de entrada como o abaixo:

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

resolvedores

Os resolvedores dizem ao GraphQL o que fazer quando cada consulta ou mutação é solicitada. É uma função básica que faz o trabalho duro de atingir a camada do banco de dados para fazer as operações CRUD (criar, ler, atualizar, excluir), atingir um terminal de API RESTful interno ou chamar um microsserviço para atender à 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 } }; }

Esquema

O esquema do GraphQL é o que o GraphQL expõe ao mundo. Portanto, os tipos, consultas e mutações serão incluídos dentro do esquema a ser exposto 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 o RootMutation que criamos anteriormente para serem expostos ao mundo.

Como o GraphQL funciona com Nodejs e Expressjs

O 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 a JavaScript e também há outras implementações do GraphQL para simplificar a escrita e a codificação no GraphQL.

O GraphQL Apollo fornece uma implementação para Node.js e Express.js e facilita a introdução ao GraphQL.

Você aprenderá como criar e desenvolver seu primeiro aplicativo GraphQL na estrutura de back-end Nodes.js e Express.js usando o GraphQL Apollo na próxima seção.

Configurando GraphQL com Express.js

Construir um servidor GraphQL API com Express.js é simples de começar. Nesta seção, exploraremos como construir um servidor GraphQL.

Inicializar projeto com Express

Primeiro, você precisa instalar e configurar um novo projeto Express.js.

Crie uma pasta para 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, vamos estruturar nosso projeto conforme a imagem abaixo. Ele conterá diferentes módulos para os recursos do projeto, como usuários, todos, etc.

Uma lista de arquivos em graphql-todo.
Arquivos para graphql-todo .

Inicializar 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 Esquemas e Tipos

Em seguida, vamos criar um arquivo index.js dentro da pasta modules e adicionar o seguinte trecho 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;

Passo a passo do código

Vamos trabalhar com o trecho de código e dividi-lo:

Passo 1

Primeiro, importamos as bibliotecas necessárias e criamos tipos de consulta e mutação padrão. A consulta e a mutação definem apenas a versão da API GraphQL por enquanto. No entanto, estenderemos a consulta e a mutação para incluir outros esquemas à medida que prosseguirmos.

Uma interface de linha de comando mostrando o código "const" para importar GraphQL e outras extensões.
Importando GraphQL e extensões.
Passo 2:

Em seguida, criamos um novo tipo escalar para o tempo e nosso primeiro resolvedor para a consulta e mutação criada acima. Além disso, também geramos um esquema usando a função makeExecutableEchema .

O esquema gerado inclui todos os outros esquemas que importamos e também incluirá mais quando os criarmos e importarmos.

Lutando com problemas de tempo de inatividade e WordPress? Kinsta é a solução de hospedagem projetada para economizar seu tempo! Confira nossos recursos
Uma interface de linha de comando mostrando o código "const" para criar nosso tipo escalar e nosso primeiro resolvedor.
Criando um tipo escalar para o tempo, bem como nosso primeiro resolvedor.

O trecho de código acima mostra que importamos esquemas diferentes para a função makeExecutableEchema. Essa abordagem nos ajuda a estruturar o aplicativo para a complexidade. Em seguida, vamos criar os esquemas Todo e Usuário que importamos.

Criando esquema de tarefas

O esquema Todo mostra operações CRUD simples que os usuários do aplicativo podem executar. Abaixo está o esquema que implementa a operação 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 };

Passo a passo do código

Vamos trabalhar com o trecho de código e dividi-lo:

Passo 1:

Primeiro, criamos um esquema para o 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.

Uma interface de linha de comando mostrando o esquema para nosso script Todo, incluindo novas entradas.
Criando o esquema para o nosso Todo.
Passo 2:

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

Uma interface de linha de comando mostrando o código para criar um resolvedor para nosso Todo.
Criando um resolvedor.

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

Crie um arquivo create-user.js na pasta <code>./mutations</code> e adicione a lógica de negócios 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 trecho de código acima é uma maneira simplificada de criar um novo Todo em nosso banco de dados usando o Sequelize ORM. Você pode aprender mais sobre Sequelize e como configurá-lo com Node.js.

Você pode seguir a mesma etapa para criar muitos esquemas dependendo do seu aplicativo ou pode clonar o projeto completo do GitHub.

Em seguida, vamos configurar o servidor com Express.js e executar o aplicativo Todo recém-criado com GraphQL e Node.js

Configurando e executando o servidor

Por fim, configuraremos nosso servidor usando a biblioteca apollo-server-express que instalamos anteriormente e a configuramos.

O apollo-server-express é um wrapper simples do Apollo Server para Express.js. É recomendado porque foi desenvolvido para caber no desenvolvimento do Express.js.

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

Crie um arquivo server.js no diretório raiz e cole o 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`) );

No código acima, você criou com sucesso seu primeiro servidor CRUD GraphQL para Todos e Usuários. Você pode iniciar seu servidor de desenvolvimento e acessar o playground usando http://localhost:3000/graphql. Se tudo der certo, você deverá ser apresentado à tela abaixo:

Uma interface de desenvolvimento mostrando uma consulta simples em resposta.
A tela de verificação.

Resumo

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

Este guia elucidou o GraphQL e demonstrou como desenvolver sua primeira API GraphQL com Express.js.

Deixe-nos saber o que você constrói usando GraphQL.