TUTORIAIS & NOTÍCIAS

Introdução à criação de APIs GraphQL

9 min read

Esses tipos de dados interconectados são exatamente o desafio que o GraphQL inicialmente se propôs a resolver no desenvolvimento da API. Ao escrever uma API do GraphQL, somos capazes de conectar dados de forma eficiente, o que reduz a complexidade e o número de solicitações, enquanto nos permite atender precisamente ao cliente os dados de que ele precisa.

Neste artigo, vamos criar uma API GraphQL no Node.js, usando o pacote Apollo Server. Para isso, vamos explorar tópicos fundamentais do GraphQL, escreveremos um esquema GraphQL, desenvolvemoso código para resolver nossas funções de esquema e acessar nossa API usando a interface do usuário do GraphQL Playground.

O que é o GraphQL?

GraphQL é uma linguagem de consulta de código aberto e manipulação de dados para APIs. Foi desenvolvido com o objetivo de fornecer terminais únicos para dados, permitindo que os aplicativos solicitem exatamente os dados necessários. Isso tem o benefício de não apenas simplificar nosso código de interface do usuário, mas também melhorar o desempenho, limitando a quantidade de dados que precisam ser enviados.

Exmplo: APIs REST Client, GraphQL e gRPC Client

O que estamos desenvolvendo

Para acompanhar este tutorial, você precisará do Node v8.x ou posterior e familiaridade com o trabalho com a linha de comando.

Vamos criar um aplicativo API para destaques de livros, permitindo armazenar passagens memoráveis das coisas que lemos. Os usuários da API poderão executar operações "CRUD" (criar, ler, atualizar, excluir) com seus destaques:

  • Crie um novo destaque
  • Leia um destaque individual, bem como uma lista de destaques
  • Atualizar o conteúdo de um destaque
  • Excluir um destaque

Primeiros passos

Para começar, primeiro crie um novo diretório para o nosso projeto, inicialize um novo projeto de nó e instale as dependências necessárias:

# make the new directory
mkdir highlights-api
# change into the directory
cd highlights-api
# initiate a new node project
npm init -y
# install the project dependencies
npm install apollo-server graphql
# install the development dependencies
npm install nodemon --save-dev

Antes de prosseguir, vamos detalhar nossas dependências:

  • apollo-server é uma biblioteca que nos permite trabalhar com o GraphQL em nosso aplicativo Node. Vamos usá-lo como uma biblioteca independente, mas a equipe da Apollo também criou middleware para trabalhar com aplicações web nó existente no Expresso , hapi , Fastify e Koa

  • graphql inclui a linguagem GraphQL e é uma dependência necessária de pares apollo-server.

  • nodemon é uma biblioteca útil que acompanhará nosso projeto em busca de alterações e reiniciará automaticamente nosso servidor.

Com nossos pacotes instalados, vamos criar o arquivo raiz do aplicativo, chamado index.js. Por enquanto, enviaremos console.log() uma mensagem neste arquivo:

console.log(Ola Devs!!);
"scripts": {
  "start": "nodemon index.js"
},

Agora, podemos iniciar nosso aplicativo digitando npm start o aplicativo do terminal. Se tudo estiver funcionando corretamente, você verá "Ola Devs!!" registrado no seu terminal.

Tipos de esquema GraphQL

Um esquema é uma representação escrita de nossos dados e interações. Ao exigir um esquema, o GraphQL impõe um plano estrito para nossa API. Isso ocorre porque a API pode retornar apenas dados e executar interações definidas no esquema. O componente fundamental dos esquemas do GraphQL são os tipos de objetos. O GraphQL contém cinco tipos internos:

  • String: uma string com codificação de caracteres UTF-8
  • Booleano: um valor verdadeiro ou falso
  • Int: um número inteiro de 32 bits
  • Float: um valor de ponto flutuante
  • ID: um identificador exclusivo
const { gql } = require('apollo-server');

const typeDefs = gql`
  # The schema will go here
`;

module.exports = typeDefs;

Para escrever nosso esquema, primeiro definimos o tipo. Vamos considerar como podemos definir um esquema para nosso aplicativo de destaques. Para começar, criaríamos um novo tipo com o nome de Devs:

const typeDefs = gql`
  type Devs {
  }
`;

Cada destaque terá um ID exclusivo, algum conteúdo, um título e um autor. O Highlight esquema será mais ou menos assim:

const typeDefs = gql`
  type Highlight {
    id: ID
    content: String
    title: String
    author: String
  }
`;

Podemos fazer alguns desses campos obrigatórios adicionando um ponto de exclamação:

const typeDefs = gql`
  type Devs {
    id: ID!
    content: String!
    title: String
    author: String
  }
`;

Embora definimos um tipo de objeto para nossos destaques, também precisamos fornecer uma descrição de como um cliente buscará esses dados. Isso é chamado de query. Vamos abordar mais as consultas em breve, mas, por enquanto, vamos descrever em nosso esquema as maneiras pelas quais alguém recuperará os destaques. Ao solicitar todos os nossos destaques, os dados serão retornados como uma matriz (representada como [Devs]) e quando quisermos recuperar um único destaque, precisaremos passar um ID como parâmetro.

const typeDefs = gql`
  type Highlights {
    id: ID!
    content: String!
    title: String
    author: String
  }
  type Query {
    highlights: [Highlight]!
    highlight(id: ID!): Highlight
  }
`;

Agora, no index.js arquivo, podemos importar nossas definições de tipo e configurar o Apollo Server:

const {ApolloServer } = require('apollo-server');
const typeDefs = require('./schema');

const server = new ApolloServer({ typeDefs });

server.listen().then(({ url }) => {
     console.log(`Devs server ready at ${url}`);
});

Se mantemos o processo do nó em execução, o aplicativo será atualizado e reiniciado automaticamente, mas, caso contrário, digitar npm start no diretório do projeto na janela do terminal iniciará o servidor. Se observar para o terminal, vamos ver que nodemon está assistindo nossos arquivos e o servidor está sendo executado em uma porta local:

[nodemon] 2.0.2
[nodemon] to restart at any time, enter `rs`
[nodemon] watching dir(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node index.js`
Highlight server ready at http://localhost:4000/

Visitar o URL no navegador iniciará o aplicativo GraphQL Playground, que fornece uma interface de usuário para interagir com nossa API.

Resolvedores GraphQL

Embora temos desenvolvido nosso projeto com um esquema inicial e uma configuração do Apollo Server, ainda não podemos interagir com nossa API. Para isso, vamos apresentar os resolvedores realizam exatamente a ação que seu nome implica; eles resolvem os dados que o usuário da API solicitou. Foi escrito esses resolvedores primeiro definindo-os em nosso esquema e implementando a lógica em nosso código JavaScript. Nossa API conterá dois tipos de resolvedores: consultas e mutações.

Vamos primeiro adicionar alguns dados para interagir. Em um aplicativo, normalmente são dados que estamos recuperando e gravando de um banco de dados, mas, para o nosso exemplo, vamos usar uma matriz de objetos. No index.js arquivo, adicione o seguinte:

let Highlight = [
  {
    id: '1',
    content: 'Um dia eu vou encontrar as palavras certas, e elas serão simples.',
    title: 'Devs',
    author: 'Palamar'
  },
  {
    id: '2',
    content: 'Nos limites de uma situação, há humor, há graça e tudo mais.',
    title: 'Teste',
    author: 'Palamar'
  }
]

Consultas

Uma consulta solicita dados específicos de uma API, no formato desejado. A consulta retornará um objeto, contendo os dados que o usuário da API solicitou. Uma consulta nunca modifica os dados; apenas acessa. Já escrevemos duas consultas em nosso esquema. O primeiro retorna uma matriz de destaques e o segundo retorna um destaque específico. O próximo passo é escrever os resolvedores que retornarão os dados.

No index.js arquivo, podemos adicionar um objeto resolvedores, que pode conter nossas consultas:

const resolvers = {
  Query: {
    Devs: () => highlights,
    Devs: (parent, args) => {
      return highlight.find(devs => devs.id === args.id);
    }
  }
};

A Highlight consulta retorna toda a matriz de dados de destaques. A devs consulta aceita dois parâmetros: parente args. O parent é o primeiro parâmetro de qualquer consulta GraqhQL no Apollo Server e fornece uma maneira de acessar o contexto da consulta. O args parâmetro nos permite acessar os argumentos fornecidos pelo usuário. Nesse caso, os usuários da API fornecerão um argumento de id para acessar um destaque específico.

Podemos então atualizar nossa configuração do servidor Apollo para incluir os resolvedores:

const server = new ApolloServer({ typeDefs, resolvers });

Com nossos resolvedores de consultas escritos e o Apollo Server atualizado, agora podemos consultar a API usando o GraphQL Playground. Para acessar o GraphQL Playground, visite http://localhost:4000 no seu navegador da web.

Uma consulta está formatada da seguinte forma:

query {
  queryName {
      field
      field
    }
}

Com isso em mente, podemos escrever uma consulta que solicite o ID, conteúdo, título e autor para cada um dos nossos destaques:

query {
  highlights {
    id
    content
    title
    author
  }
}

Digamos que tivemos uma página em nossa interface do usuário que lista apenas os títulos e autores de nossos textos destacados. Não precisamos recuperar o conteúdo de cada um desses destaques. Em vez disso, poderíamos escrever uma consulta que solicita apenas os dados de que precisamos:

query {
  devs {
    title
    author
  }
}

Também criamos um resolvedor para consultar uma nota individual, incluindo um parâmetro de ID em nossa consulta. Podemos fazer o seguinte:

query {
  devs(id: "1") {
    content
  }
}

Mutações

Usamos uma mutação quando queremos modificar os dados em nossa API. Em nosso exemplo de destaque, queremos escrever uma mutação para criar um novo destaque, um para atualizar um destaque existente e um terceiro para excluir um destaque. Semelhante a uma consulta, também é esperado que uma mutação retorne um resultado na forma de um objeto, geralmente o resultado final da ação executada.

O primeiro passo para atualizar qualquer coisa no GraphQL é escrever o esquema. Podemos incluir mutações em nosso esquema, adicionando um tipo de mutação ao nosso schema.js arquivo:

type Mutation {
  newDevs (content: String! title: String author: String): Devs!
  updateHighlight(id: ID! content: String!): Devs!
  deleteDevs(id: ID!): Devs!
}

Nossa newHighlight mutação pegará o valor necessário do conteúdo junto com opcional titlee authorvalores e retornará a Highlight. A updateHighlightmutação exigirá que um destaque ide contentser passado como valores de argumento e irá retornar a atualizados Highlight. Finalmente, a deleteHighligh mutação t aceitará um argumento de ID e retornará o destaque excluído.

Com o esquema atualizado para incluir mutações, agora podemos atualizar o arquivo resolvers em nosso index.js arquivo para executar essas ações. Cada mutação atualizará nossa devs matriz de dados.

const resolvers = {
  Query: {
    highlights: () => highlights,
    highlight: (parent, args) => {
      return highlights.find(highlight => highlight.id === args.id);
    }
  },
  Mutation: {
    newHighlight: (parent, args) => {
      const highlight = {
        id: String(highlights.length + 1),
        title: args.title || '',
        author: args.author || '',
        content: args.content
      };
      highlights.push(highlight);
      return highlight;
    },
    updateHighlight: (parent, args) => {
      const index = highlights.findIndex(highlight => highlight.id === args.id);
      const highlight = {
        id: args.id,
        content: args.content,
        author: highlights[index].author,
        title: highlights[index].title
      };
      highlights[index] = highlight;
      return highlight;
    },
    deleteHighlight: (parent, args) => {
      const deletedHighlight = highlights.find(
        highlight => highlight.id === args.id
      );
      highlights = highlights.filter(highlight => highlight.id !== args.id);
      return deletedHighlight;
    }
  }
};

Com o esquema atualizado para incluir mutações, agora podemos atualizar o arquivo resolvers em nosso index.js arquivo para executar essas ações. Cada mutação atualizará nossa highlights matriz de dados.

const resolvers = {
  Query: {
    highlights: () => highlights,
    highlight: (parent, args) => {
      return highlights.find(highlight => highlight.id === args.id);
    }
  },
  Mutation: {
    newHighlight: (parent, args) => {
      const highlight = {
        id: String(highlights.length + 1),
        title: args.title || '',
        author: args.author || '',
        content: args.content
      };
      highlights.push(highlight);
      return highlight;
    },
    updateHighlight: (parent, args) => {
      const index = highlights.findIndex(highlight => highlight.id === args.id);
      const highlight = {
        id: args.id,
        content: args.content,
        author: highlights[index].author,
        title: highlights[index].title
      };
      highlights[index] = highlight;
      return highlight;
    },
    deleteHighlight: (parent, args) => {
      const deletedHighlight = highlights.find(
        highlight => highlight.id === args.id
      );
      highlights = highlights.filter(highlight => highlight.id !== args.id);
      return deletedHighlight;
    }
  }
};

Com essas mutações escritas, podemos usar o GraphQL Playground para praticar a mutação dos dados. A estrutura de uma mutação é quase idêntica à de uma consulta, especificando o nome da mutação, passando os valores do argumento e solicitando dados específicos em troca. Vamos começar adicionando um novo destaque:

mutation {
  newHighlight(author: "Palamar" title: "JS Everywhere" content: "GraphQL is awesome") {
    id
    author
    title
    content
  }
}

Podemos então escrever mutações para atualizar um destaque:

mutation {
  updateHighlight(id: "3" content: "GraphQL is rad") {
    id
    content
  }
}

E para excluir um destaque:

mutation {
  deleteHighlight(id: "3") {
    id
  }
}

Conclusão

Parabéns! Agora você criou com êxito uma API GraphQL, usando o Apollo Server, e pode executar consultas e mutações GraphQL em um objeto de dados na memória. Estabelecemos uma base sólida para explorar o mundo do desenvolvimento da API GraphQL.