Tenho ouvido muitos rumores sobre o trabalho com GraphQL e NoSQL, internamente no Couchbase e externamente. Como esse parece ser um assunto muito discutido, pensei em passar algum tempo aprendendo sobre ele para ver como poderia ser útil na criação de aplicativos da Web.
Vindo do mundo das APIs RESTful, o GraphQL é um conceito totalmente diferente para mim, embora tenha como objetivo resolver um problema semelhante de acesso e alteração de dados por meio de aplicativos voltados para o cliente. Felizmente, o Node.js e os bancos de dados funcionam bem juntos, portanto, no exemplo de banco de dados GraphQL abaixo, usaremos usando Node.js com Couchbase como nosso Banco de dados NoSQL camada.
No momento, não sou um especialista em GraphQL. Venho desenvolvendo APIs RESTful com estruturas como Express e Hapi há muitos anos. Dito isso, vou tentar explicar como aprendi sobre elas.
As APIs funcionam muito bem, mas quando se trata de obter as informações de que você precisa delas, as coisas podem ser feitas melhor. Por exemplo, e se você precisar de um monte de dados não relacionados ou pouco relacionados para o seu cliente? Você cria um endpoint enorme que retorna muitos dados ou faz uma solicitação para cada endpoint de API que possa conter seus dados? E se você precisar de apenas um fragmento dos dados retornados de um determinado endpoint?
É aqui que o GraphQL entra em ação. Por meio da linguagem de consulta, você pode especificar exatamente quais dados deseja que sejam retornados ao cliente em um determinado momento, sem precisar escrever um endpoint para tudo.
Criação de um novo aplicativo Node.js com o Express Framework
Usaremos o Node.js para criar uma interface para usar o Consultas GraphQL. Vamos começar criando um novo projeto, obtendo as dependências e inicializando nosso projeto em preparação para alguma lógica real.
Com o Node.js instalado, na linha de comando, execute:
1 2 3 4 5 6 |
npm inicial -y npm instalar couchbase --salvar npm instalar expresso --salvar npm instalar expresso-graphql --salvar npm instalar graphql --salvar npm instalar uuid --salvar |
Os comandos acima inicializarão um novo projeto criando um arquivo package.json e instalando as dependências necessárias. Das dependências, estamos instalando o Couchbase e um pacote para gerar cadeias de caracteres UUID que mais tarde representarão nossas chaves de documento. Os outros pacotes estão relacionados ao GraphQL e à vinculação ao Express Framework.
Crie um arquivo chamado app.js em seu projeto e inclua o seguinte código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
const Expresso = exigir("expresso"); const Couchbase = exigir("couchbase"); const ExpressGraphQL = exigir("express-graphql"); const Criar esquema = exigir("graphql").buildSchema; const UUID = exigir("uuid"); var esquema = Criar esquema(``); var solucionadores = { }; var aplicativo = Expresso(); aplicativo.uso("/graphql", ExpressGraphQL({ esquema: esquema, rootValue: solucionadores, gráfico: verdadeiro })); aplicativo.ouvir(3000, () => { console.registro("Ouvindo a :3000"); }); |
Ainda não vamos nos preocupar em preencher nosso esquema e configurar nosso banco de dados. No código acima, observe que importamos cada uma de nossas dependências e inicializamos o Express. Também definimos um único ponto de extremidade de API, que é como vamos fazer com que o Couchbase e o GraphQL interajam. Essencialmente, emitiremos todas as consultas GraphQL para esse único endpoint e ele responderá com os dados corretos com base em nosso esquema e resolvedores.
Definição de um esquema e resolvedores GraphQL
Agora que já temos a base do aplicativo, vamos definir um esquema e os resolvedores para realmente vincular os dados a possíveis consultas e tipos de dados.
Para este exemplo, vamos criar um aplicativo simples de blog. Por esse motivo, pode fazer sentido ter os seguintes modelos de esquema:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var esquema = Criar esquema(` tipo Conta { id: Cordas, primeiro nome: Cordas, sobrenome: Cordas, } tipo Blog { id: Cordas, conta: Cordas!, título: Cordas, conteúdo: Cordas } `); |
Sabemos que teremos contas e que teremos artigos de blog para uma determinada conta. O ponto de exclamação em qualquer tipo de dados significa que se trata de uma propriedade obrigatória.
Agora precisamos conectar nosso modelo ao Couchbase para que possamos consultar os dados ou até mesmo criar dados.
Consulta e mutação de dados do Couchbase Server, o banco de dados NoSQL
Vamos supor que você já tenha Couchbase em execução. Não há nenhum requisito especial de configuração do Couchbase para este exemplo específico.
A primeira etapa é conectar-se ao Couchbase Server a partir do aplicativo Node.js. Na seção app.js adicione as seguintes linhas:
1 2 3 |
var agrupamento = novo Couchbase.Aglomerado("couchbase://localhost"); agrupamento.autenticar("exemplo", "123456"); var balde = agrupamento.openBucket("exemplo"); |
As linhas acima pressupõem que o Node.js e o banco de dados - Couchbase, neste exemplo - estejam sendo executados no computador local. Vamos usar um Bucket chamado exemplo
e uma conta de usuário RBAC chamada exemplo
.
Os modelos de dados para o GraphQL já estão em vigor, mas ainda não definimos as possíveis consultas ou mutações. Vamos modificar o esquema em nosso aplicativo para que se pareça com o seguinte:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
var esquema = Criar esquema(` tipo Consulta { conta(id: Cordas!): Conta, contas: [Conta], blog(id: Cordas!): Blog, blogs(conta: Cordas!): [Blog] } tipo Conta { id: Cordas, primeiro nome: Cordas, sobrenome: Cordas, } tipo Blog { id: Cordas, conta: Cordas!, título: Cordas, conteúdo: Cordas } tipo Mutação { createAccount(primeiro nome: Cordas!, sobrenome: Cordas!): Conta criarBlog(conta: Cordas!, título: Cordas!, conteúdo: Cordas!): Blog } `); |
Tanto para as consultas quanto para as mutações, serão chamadas funções especiais de resolução que fazem o trabalho pesado. Como resultado, um Conta
ou um Blog
será devolvido.
Começando com as mutações, estamos esperando um createAccount
que espera dois parâmetros. Ela pode se parecer com o seguinte:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var solucionadores = { createAccount: (dados) => { var id = UUID.v4(); dados.tipo = "account" (conta); retorno novo Promessa((resolver, rejeitar) => { balde.inserir(id, dados, (erro, resultado) => { se(erro) { retorno rejeitar(erro); } resolver({ "id": id }); }); }); } }; |
Observe que nossa função resolver tem apenas um parâmetro, embora estivéssemos passando dois em nosso esquema. Todos os nossos parâmetros de dados residirão na seção dados
variável. A própria função espera que retornemos uma promessa.
Você notará que estamos retornando um id
e nada mais. Lembre-se, esperamos um Conta
a ser devolvido, mas nem todas as partes do Conta
precisa estar presente. No entanto, talvez seja uma prática melhor devolver tudo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var solucionadores = { criarBlog: (dados) => { var id = UUID.v4(); dados.tipo = "blog"; retorno novo Promessa((resolver, rejeitar) => { balde.inserir(id, dados, (erro, resultado) => { se(erro) { retorno rejeitar(erro); } resolver({ "id": id }); }); }); } }; |
O criarBlog
é praticamente a mesma que a função anterior, com a exceção de que os dados de entrada são diferentes e a função tipo
é definida como outra coisa. Todas as nossas funções estarão no mesmo solucionadores
objeto.
Com as mutações fora do caminho, os dados podem ser consultados. Pegue o conta
por exemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var solucionadores = { conta: (dados) => { var id = dados.id; retorno novo Promessa((resolver, rejeitar) => { balde.obter(id, (erro, resultado) => { se(erro) { retorno rejeitar(erro); } resolver(resultado.valor); }); }); } }; |
Nada realmente diferente está acontecendo entre essa função de consulta específica e a função de mutação. Estamos esperando um id
a ser passado e estamos usando-o para fazer uma pesquisa.
Para recuperar todas as contas, podemos usar uma consulta N1QL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var solucionadores = { contas: () => { var declaração = "SELECT META(account).id, account.* FROM `" + balde._nome + "` AS account WHERE account.type = 'account'"; var consulta = Couchbase.N1qlQuery.fromString(declaração); retorno novo Promessa((resolver, rejeitar) => { balde.consulta(consulta, (erro, resultado) => { se(erro) { retorno rejeitar(erro); } resolver(resultado); }); }); } }; |
Você consegue ver a tendência agora quando se trata de consultar e alterar dados? Para concluir o trabalho, vamos dar uma olhada em nossas duas funções finais, uma para obter um único blog e outra para obter todos os blogs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
var solucionadores = { blog: (dados) => { var id = dados.id; retorno novo Promessa((resolver, rejeitar) => { balde.obter(id, (erro, resultado) => { se(erro) { retorno rejeitar(erro); } resolver(resultado.valor); }); }); }, blogs: (dados) => { var declaração = "SELECT META(blog).id, blog.* FROM `" + balde._nome + "` AS blog WHERE blog.type = 'blog' AND blog.account = $account"; var consulta = Couchbase.N1qlQuery.fromString(declaração); retorno novo Promessa((resolver, rejeitar) => { balde.consulta(consulta, { "account" (conta): dados.conta }, (erro, resultado) => { se(erro) { retorno rejeitar(erro); } resolver(resultado); }); }); } }; |
Nesse momento, nosso aplicativo está completo. Pelo menos completo o suficiente para este exemplo específico. Agora, provavelmente, devemos testá-lo com algumas consultas GraphQL.
Execução de consultas GraphQL com a interface gráfica
Se estiver usando o Node.js e o pacote GraphQL para o Express, você terá uma boa interface gráfica para executar consultas. Navegue até http://localhost:3000/graphql em seu navegador da Web e você verá algo parecido com o seguinte:
Portanto, vamos criar uma consulta de mutação que inserirá dados para nós.
1 2 3 4 5 |
mutação Criar conta($primeiro nome: Cordas!, $sobrenome: Cordas!) { createAccount(primeiro nome: $primeiro nome, sobrenome: $sobrenome) { id } } |
A mutação acima receberá dois parâmetros obrigatórios e, em seguida, executará nosso createAccount
passando cada um desses parâmetros. Os parâmetros podem ser definidos na função Variáveis de consulta do GraphQL explorer. Os parâmetros seriam mais ou menos assim:
1 2 3 4 |
{ "firstname": "Erika", "lastname" (sobrenome): "Raboy" } |
Após uma mutação bem-sucedida, obteremos o id
que é o que solicitamos. Depois de preencher nosso banco de dados com dados, poderíamos fazer uma consulta GraphQL mais sofisticada, como a seguinte:
1 2 3 4 5 6 7 8 9 10 |
consulta GetAccountData($id: Cordas!) { conta(id: $id) { primeiro nome sobrenome } blogs(conta: $id) { título conteúdo } } |
Para a consulta acima, você passaria uma conta válida e ela retornaria as informações da conta, bem como as informações do blog que essa conta escreveu. Por meio das consultas GraphQL, você pode solicitar determinadas propriedades ou todas as propriedades, evitando que você faça várias solicitações.
Conclusão
Você acabou de ver como criar um banco de dados Node.js simples aproveitando o GraphQL em Couchbase. O fato de o GraphQL oferecer um meio de consultar os dados de que você precisa não elimina a necessidade de escrever consultas de qualidade que se comuniquem com o seu banco de dados. Lembre-se, o GraphQL é apenas uma linguagem de consulta voltada para o cliente, não uma linguagem de consulta de banco de dados de back-end. Em outras palavras, deixe o Couchbase fazer o trabalho pesado quando fizer sentido.
Se você quiser obter mais ajuda com o SDK do Node.js para o Couchbase, consulte o Portal do desenvolvedor do Couchbase.