PostgreSQL e GraphQL

Exemplo Básico de Como usar o GraphQL com PostgreSQL


Autor: Réulison Silva Publicado em: Outubro 15, 2019

O GraphQL é apenas uma linguagem de consulta para sua API. Eu acho que o GraphQL é uma boa alternativa ao REST. O GraphQL facilita muito a programação no front-end, pois permite o uso de um único endpoint, o que reduz o tempo de resposta evitando Over-Fetching ou Under-fetching.

O Over-Fetching (excesso de consultas) ocorre quando campos adicionais são declarados em um fragmento, mas na verdade não são usados no modelo. Isso provavelmente acontecerá quando um código executado.

O Under-fetching (consulta insuficiente) ocorre quando os campos não são declarados em um fragmento, mas são usados em um modelo. Esses dados ausentes provavelmente aparecerão como NoFieldError ou valor null.

Nos dois casos, são problemas de desempenho: você deve estar usando mais banda do que deveria ou está fazendo mais solicitações HTTP do que deveria.

Em um mundo perfeito, esses problemas nunca surgiriam; você só tem os endpoints certos para fornecer os dados certos aos seus produtos.

Esses problemas geralmente aparecem quando você dimensiona e interage com seus produtos. Os dados que você usa nas suas páginas geralmente mudam e os custos para manter um endpoint separado com apenas os dados corretos para cada componente se tornam excessivos.

Portanto, você acaba com um compromisso entre não ter muitos endpoints e fazer com que eles se ajustem às necessidades de cada componente. Isso levará ao Over-Fetching em alguns casos e ao Under-fetching em outros (será necessário chamar um segundo endpoint).

Portanto, o GraphQL corrige esse problema porque permite solicitar quais dados você deseja para o servidor. Você especifica o que precisa e deseja obter desses dados, em uma única requisição ao servidor.

No entanto, esses benefícios têm um custo, um schema query e um resolver.

Aqui vou explicar como conectar o GraphQL ao PostgreSQL

Afinal, é bem simples, a única coisa que você precisa fazer é implementar adequadamente os métodos de resolução de query e mutation.

PostgreSQL Database

CREATE TABLE people (
    id              serial PRIMARY KEY,
    firstname       varchar(40) NOT NULL,
    lastname        varchar(40) NOT NULL
);

CREATE TABLE emails (
    id          serial PRIMARY KEY,
    email       varchar(256) NOT NULL,
    person      integer REFERENCES people (id),
    “primaryboolean DEFAULT false
);

A primeira tabela representa as pessoas e a segunda representa os e-mails das pessoas.

GraphQL Schema

Query {
  person(id: ID): Person
}

type Person {
  id: ID
  firstname: String
  lastname: String
  emails: [Email!]!
}

type Email {
  id: ID
  email: String
  primary: Boolean
}

Query

SELECT * FROM "emails" WHERE person=person.id;

Quero listar todos os emails de uma única pessoa.

O primeiro passo é pegar o ID da pessoa e buscar os e-mails vinculados a essa pessoa.

Configurando o Express Server

const express = require('express');
const expressGraphQL = require('express-graphql');
const schema = require('./schema');
const app = express();
app.use('/graphql', expressGraphQL({
   schema,
   graphiql: true
}))
app.listen(4000, () => {
   console.log('Listening...')
})

Primeiro, você deve instalar o express e express-graphql.

$ npm install express
npm install --save express-graphql

Você verá como definir a configuração que eu estou requisitando na parte superior.

Então, eu tenho o Graphiql definido como true. O Graphiql é um IDE do GraphQL no qual posso verificar facilmente minhas consultas. Quando o GraphiQL faz uma solicitação, o GraphQL acessa meu banco de dados e diz: “Estou procurando por essa pessoa específica.”

Se você se perguntou, nossa solicitação será assíncrona.

Configurando o GraphQL Schema

const graphql = require('graphql');
const connectionString = 'myURI';
const pgp = require('pg-promise')();
const db = {}
db.conn = pgp(connectionString);
const {
   GraphQLObjectType,
   GraphQLID,
   GraphQLString,
   GraphQLBoolean,
   GraphQLList,
   GraphQLSchema
} = graphql;
const PersonType = new GraphQLObjectType({
   name: 'Person',
   fields: () => ({
      id: { type: GraphQLID },
      firstname: { type: GraphQLString },
      lastname: { type: GraphQLString },
      emails: {
         type: new GraphQLList(EmailType),
         resolve(parentValue, args) {
            const query = `SELECT * FROM "emails" WHERE
            person=${parentValue.id}`;
            return db.conn.many(query)
               .then(data => {
                  return data;
               })
               .catch(err => {
                  return 'The error is', err;
               });
         }
      }
   })
})
const EmailType = new GraphQLObjectType({
   name: 'Email',
   fields: {
      id: { type: GraphQLID },
      email: { type: GraphQLString },
      primary: { type: GraphQLBoolean }
   }
})
const RootQuery = new GraphQLObjectType({
   name: 'RootQueryType',
   fields: {
      person: {
      type: PersonType,
      args: { id: { type: GraphQLID } },
      resolve(parentValue, args) {
         const query = `SELECT * FROM "people" WHERE id=${args.id}`;
         return db.conn.one(query)
            .then(data => {
               return data;
            })
            .catch(err => {
                return 'The error is', err;
            });
      }
   },
   emails: {
      type: EmailType,
      args: { id: { type: GraphQLID } },
      resolve(parentValue, args) {
         const query = `SELECT * FROM "emails" WHERE id=${args.id}`;
         return db.conn.one(query)
            .then(data => {
               return data;
            })
            .catch(err => {
               return 'The error is', err;
            });
        }
      }
   }
})
module.exports = new GraphQLSchema({
   query: RootQuery
})

Estou usando o pg-promise para conectar ao meu banco de dados. Então, eu estou definindo todos os tipos que vou usar.

Você pode ver uma diferença entre os campos no EmailType e no PersonType. Os campos de email são um objeto simples, mas o PersonType é agrupado com um arrow function.

Por que isso?

Se deixarmos como um objeto simples e quisermos executar esse código, apresentará o seguinte erro: "EmailType is not defined". Isso ocorre porque o Javascript não sabe sobre o EmailType ao declarar PersonType, porque EmailType é declarado após PersonType.

Podemos mover o EmailType para antes do PersonType e ele funcionará no EmailType, então teremos outra propriedade conectada ao PersonType.

A maneira como as tags de fechamentos funcionam dentro do javascript - a função é definida, mas não é executada até depois que todo esse arquivo foi executado.

Esse não é um problema do GraphQL, é um problema de como o javascript trata closures (fechamentos) e closure scopes (escopos de fechamento). Saiba mais.

Inicie seu código com:

node server.js

Visite:

http://localhost:4000/graphql

Adicione a consulta de cliente:

query {
  person(id: "1") {
    id
    firstname
    lastname
    emails {
      id
      email
      verified
    }
  }
}

Execute a consulta e veja os resultados.

Espero que este exemplo básico te ajude a entender melhor o GraphQL.

Aviso na coleta