José Navarro é um desenvolvedor full stack na FAMOCO em Bruxelas, Bélgica. Ele tem trabalhado nos últimos 3 anos como desenvolvedor web desenvolvedor com Node.js, Java, AngularJS e ReactJS, e tem grande interesse em desenvolvimento web e tecnologias móveis.

Vamos desenvolver um Graphql servidor em nodejs com express. Graphql é uma linguagem de consulta para APIs, foi desenvolvida pelo Facebook e lançada em 2015. Ela foi projetada para criar aplicativos de clientes fornecendo uma sintaxe e um sistema intuitivos e flexíveis para descrever seus requisitos de dados e interações. Uma das maiores diferenças em relação às APIs REST é que você tem apenas um ponto de extremidade de entrada para todos os recursos, em vez de um ponto de extremidade para cada recurso; e com o graphql você especifica os atributos que deseja para cada solicitação, em vez de receber o que quer que o serviço da API REST retorne, para que possamos ter certeza de que temos todos os dados de que precisamos e reduzir o tamanho de nossas solicitações.

O Facebook o utiliza há alguns anos em seu aplicativo móvel, por exemplo, no aplicativo para iOS, e a versão antiga do aplicativo móvel ainda funciona sem problemas porque o graphql não mudou, porque é um ponto de extremidade com o mesmo esquema, o que provavelmente não seria possível com as APIs REST, porque quando você lança uma nova versão da API, seu ponto de extremidade provavelmente vai mudar, de modo que os clientes precisam se adaptar ao novo ponto de extremidade e aos dados. Além disso Github abrir seu servidor graphql, para que os usuários possam consultar seus serviços usando graphql, em vez dos serviços de APIs REST...

Com o servidor, vamos consultar e criar Locais. Para armazenar os dados, usaremos o couchbase e as exibições espaciais para consultar os locais por sua localização geográfica. Escrevi um artigo anterior postagem sobre o node e o couchbase, portanto, vou pular a configuração do banco de dados que escrevi na postagem anterior.

Requisitos

Você precisa ter instalado em seu computador:

- nodejs

- servidor couchbase

Você pode encontrar o código na seção repositório do github.

Visão espacial

Antes de tudo, precisamos criar a visualização espacial. Vamos para a página de administração, no meu caso http://localhost:8091/ e faça login com meu usuário e senha. Em seguida, clique em Compartimentos de dados e criar um bucket, que chamei de graphql. Depois disso, clicamos em Vere, em seguida, clicamos em Criar visualização espacial do desenvolvimentoe digitamos os valores.

Create Spatial Development View

Eu usei lugar_por_localização em ambos Nome do documento de design e Ver nome. Agora clique em editare adicione o seguinte código


function (doc) {

Se (doc._type === 'Place' && doc.location) {

emit([{

"type": "Ponto",

"coordenadas": [doc.location.lon, doc.location.lat]

}], doc);

}

}
e clique em Salvar.

Aqui você também pode testar a visualização com os documentos no balde.

Local Modelo

Para nossos locais, vamos armazenar o nome do local e uma descrição como string.

Como queremos usar o SpatialView que Couchbase fornece, vamos armazenar a localização do local em um objeto chamado *location*, no qual armazenaremos a latitude e a longitude.

Além disso, definiremos por padrão a data de criação quando adicionarmos o local.


const PlaceModel = ottoman.model('Place', {

nome: 'string',

descrição: 'string',

localização: {

lat: 'number',

lon: 'number'

},

criado: {

tipo: 'Date',

padrão: Date.now

}

});
Para a consulta espacial, vamos definir uma função que executará uma consulta espacial. Para isso, vamos criar uma consulta espacial usando o pacote couchbase.


const queryByLocation = (bbox = [0, 0, 0, 0], next) => {

const query = couchbase.SpatialQuery.from('dev_place_by_location', 'place_by_location').bbox(bbox);

bucket.query(query, next);

}
Na função *from*, precisamos fornecer o *nome do documento de design* e o *nome da visualização*. Em seguida, na bbox (caixa delimitadora), precisamos fornecer a matriz de 4 pontos flutuantes **[ min Longitude , min Latitude , max Longitude , max Latitude ]**.

A última etapa é executar a consulta no bucket.

Servidor Graphql

Vamos usar um servidor express e o pacote express-graphql.

Importamos o esquema do nosso servidor graphql que definiremos mais tarde.


const express = require('express');

const graphqlHTTP = require('express-graphql');

const PORT = process.env.PORT || 5000;

const schema = require('./schemas');

const app = express();

app.use('/graphql', graphqlHTTP({

esquema: esquema,

graphiql: true,

}));

// iniciar o servidor

const server = app.listen(PORT, () => {

console.log(Servidor iniciado em ${ server.address().port });

});
No servidor graphql, usaremos a rota */graphql*. E vamos definir alguma opção, como graphiql, que nos fornecerá uma interface gráfica para executar consultas.

A última etapa é iniciar nosso servidor expresso.

Esquemas Graphql

As consultas e mutações do Graphql dependem dos esquemas que definimos. Portanto, temos de criar um esquema para o nosso objeto Place.

Primeiro, vamos definir um esquema para Localização.


const {

GraphQLObjectType,

GraphQLFloat,

GraphQLNonNull,

} = require('graphql');

const LocationSchema = new GraphQLObjectType({

nome: 'Location',

description: 'Geographical location',

campos: {

lat: {

type: new GraphQLNonNull(GraphQLFloat),

descrição: 'Latitude',

},

lon: {

type: new GraphQLNonNull(GraphQLFloat),

descrição: 'Longitude',

},

}

});

module.exports = LocationSchema;

Precisamos importar os tipos de graphql pacote. Em nosso esquema, podemos definir um nome e uma descrição. Esses campos são úteis para documentar nossas consultas, para que o usuário possa entender o que o campo significa.

Então temos que definir camposonde especificaremos os campos dentro de nosso esquema; nesse caso, definimos lat e solitário. Em cada campo, temos que especificar o tipo; nesse caso, esses campos são valores float e são obrigatórios, portanto, usamos GraphQLNonNull  e o tipo GraphQLFloat. Adicionamos uma descrição para que possamos saber o que eles significam.

Agora vamos definir o esquema Local.

Aqui, vamos importar os tipos de graphql e o esquema Localização que definimos.


const {

GraphQLObjectType,

GraphQLString,

GraphQLNonNull,

} = require('graphql');

const LocationSchema = require('./location');

const PlaceSchema = new GraphQLObjectType({

nome: 'Place',

description: 'Descrição do local',

campos: {

id: {

Tipo: GraphQLString,

resolve(place) {

return place._id;

}

},

nome: {

Tipo: GraphQLString,

},

descrição:{

Tipo: GraphQLString,

},

localização: {

Tipo: LocationSchema,

},

criado: {

Tipo: GraphQLString,

}

}

});

module.exports = PlaceSchema;

Estamos combinando os campos do modelo, portanto, não precisamos fornecer a função de resolução. Somente para o campo idporque o couchbase retorna o valor no campo _id.

Consulta Graphql

A consulta é a maneira pela qual recuperamos dados para o servidor.

O objeto de consulta também é um esquema, como os anteriores. Nesse caso, os campos são as consultas que permitimos que o usuário execute. Vamos definir três tipos de consulta.

Todos os lugares

Nessa consulta, vamos consultar todos os locais no banco de dados e vamos ordená-los pelo campo criado, para que retornemos primeiro os locais mais novos.

Como vamos retornar uma matriz de Places, atribuímos a tipo o tipo GraphQLList e fornecemos o Esquema de local


...

const PlaceSchema = require('./place');

const Place = require('../models/place');

allPlaces: {

type: new GraphQLList(PlaceSchema),

descrição: 'Query for all places',

resolve(root, args) {

return new Promise((resolve, reject) => {

Place.find({}, {

sort: {

criado: -1

},

}, (err, places) => {

se (err) {

reject(err);

}

resolver(lugares);

})

});

}

}

Também adicionamos uma descrição, esse campo é opcional.

O último parâmetro é uma função, ou seja, a função resolve, que especificará como recuperaremos os dados do nosso banco de dados. Como nossas chamadas para o banco de dados são assíncronas, retornaremos uma promessa que usará o modelo Place que definimos com o ottoman. Com o modelo, usamos find para consultar os documentos e passamos como primeiro parâmetro um objeto vazio, porque queremos consultar todos os documentos; o segundo parâmetro são as opções da nossa consulta; nesse caso, vamos ordenar pelo campo criado em ordem decrescente. Por fim, fornecemos a função de retorno de chamada que resolverá a promessa com os valores ou a rejeitará em caso de erro.

Locais

Nessa consulta, vamos consultar usando a visualização Spatial, portanto, temos que passar os pontos bbox no parâmetro da consulta.


...

const queryByLocation = require('../models/place').queryByLocation;

Locais: {

type: new GraphQLList(PlaceSchema),

descrição: 'Query for all places inside the boundary box',

args: {

minLon: {

type: new GraphQLNonNull(GraphQLFloat),

description: 'Min Longitude of the boundary box' (Longitude mínima da caixa de limite),

},

maxLon: {

type: new GraphQLNonNull(GraphQLFloat),

descrição: "Max Longitude of the boundary box" (Longitude máxima da caixa de limite),

},

minLat: {

type: new GraphQLNonNull(GraphQLFloat),

descrição: 'Latitude mínima da caixa de limite',

},

maxLat: {

type: new GraphQLNonNull(GraphQLFloat),

descrição: 'Latitude máxima da caixa de limite',

},

},

resolve(root, args) {

// bbox = [ min Longitude , min Latitude , max Longitude , max Latitude ]

const bbox = [

args.minLon,

args.minLat,

args.maxLon,

args.maxLat,

];

return new Promise((resolve, reject) => {

queryByLocation(bbox, (err, places) => {

se (err) {

reject(err);

}

resolve(places.map((place) => place.value));

})

});

}

}

Primeiro, importamos a função que definimos para realizar a consulta espacial.

Como na consulta anterior, definimos o tipo como uma matriz de locais e adicionamos uma descrição.

Nessa consulta, precisamos de algum parâmetro, então definimos os args, que se referem ao parâmetro; cada valor dentro de argumentos correspondem aos parâmetros; nesse caso, definimos 4, minLon, maxLon, minLat, maxLat e, para todos eles, vamos definir o tipo como obrigatório e floats.

Nesse caso, a função de resolução também é uma promessa. Primeiro, criamos a matriz bbox para passar a função queryByLocation. Em caso de erro, rejeitaremos a promessa com um erro; em caso de sucesso, precisaremos mapear o objeto do banco de dados, pois a visualização espacial retorna o ponto geográfico e o valor, e quando estivermos retornando o documento completo, isso mudará se definirmos uma visualização espacial diferente.

Local

A última consulta que vamos definir é a que consulta um local pelo seu ID.


Local: {

Tipo: PlaceSchema,

descrição: "Consulta de um local pelo ID do local",

args: {

id: {

type: new GraphQLNonNull(GraphQLString),

descrição: 'Place id',

}

},

resolve(root, args) {

return new Promise((resolve, reject) => {

Place.getById(args.id, (err, place) => {

se (err) {

reject(err);

}

resolver(lugar);

});

});

}
Nesse caso, o tipo é o Esquema de localnos args, só precisamos definir o id e o definimos como string e obrigatório.

A função resolve, mais uma vez, é uma promessa; nesse caso, usaremos a função queryById do modelo, e passamos o id do objeto args.

Mutação do Graphql

Com as mutações, podemos modificar os dados em nosso servidor. Assim como a consulta, os objetos de mutação são esquemas. Portanto, temos que definir os mesmos campos que os esquemas anteriores.

Quando realizamos a consulta de mutação, fornecemos os valores entre parênteses e, assim como as consultas, fornecemos os valores que queremos recuperar do objeto modificado.

Aqui, realizaremos a criação, a atualização e a exclusão de Locais.

createPlace

Nesta mutação, vamos criar um novo local.

No tipo do esquema, vamos defini-lo como Esquema de localporque vamos retornar o local criado.


createPlace: {

Tipo: PlaceSchema,

descrição: 'Create a place',

args: {

nome: {

type: new GraphQLNonNull(GraphQLString),

description: 'Nome do local',

},

descrição: {

type: new GraphQLNonNull(GraphQLString),

description: "Descrição do local",

},

latitude: {

type: new GraphQLNonNull(GraphQLFloat),

description: 'Latitude do local',

},

longitude: {

type: new GraphQLNonNull(GraphQLFloat),

description: 'Longitude do local',

}

},

resolve(source, args) {

return new Promise((resolve, reject) => {

const place = new Place({

name: args.name,

description: args.description,

localização: {

lat: args.latitude,

lon: args.longitude,

},

});

place.save((err) => {

se (err) {

reject(err);

}

resolver(lugar);

})

});

}

}
Como nas consultas, definimos os argumentos com os valores necessários para criar um novo local. Nesse caso, exigimos que o nome e a descrição sejam strings e que a latitude e a longitude sejam floats; todos os campos serão obrigatórios.

Na função resolver, vamos retornar uma promessa. Dentro da promessa, vamos criar o local com os valores da consulta dentro de argumentos. Em seguida, vamos realizar salvar no objeto do local. Por fim, no caso de um erro ao salvar o local, rejeitaremos a promessa com o erro ou resolveremos a promessa com os dados do local.

updatePlace

Como no createPlace mutação, o updatePlace A mutação é semelhante, as diferenças são que, nesse caso, todos os valores nos args não são obrigatórios e o campo id é uma string obrigatória; e na função resolve, primeiro vamos procurar o objeto pelo parâmetro idEm seguida, verificamos os campos fornecidos pelo usuário e atualizamos o local e, por fim, salvamos e retornamos o novo objeto


updatePlace: {

Tipo: PlaceSchema,

descrição: "Atualizar um local",

args: {

id: {

type: new GraphQLNonNull(GraphQLString),

descrição: 'Id do local',

},

nome: {

Tipo: GraphQLString,

description: 'Nome do local',

},

descrição: {

Tipo: GraphQLString,

description: "Descrição do local",

},

latitude: {

Tipo: GraphQLFloat,

description: 'Latitude do local',

},

longitude: {

Tipo: GraphQLFloat,

description: 'Longitude do local',

}

},

resolve(source, args) {

return new Promise((resolve, reject) => {

Place.getById(args.id, (err, place) => {

se (err) {

reject(err);

} else {

Se (args.name) {

place.name = args.name;

}

Se (args.description) {

place.name = args.name;

}

Se (args.latitude) {

place.location.lat = args.latitude;

}

Se (args.longitude) {

place.location.lon = args.longitude;

}

place.save((err) => {

se (err) {

reject(err);

}

resolver(lugar);

});

}

})

});

}

}

deletePlace

A última mutação é a exclusão, na qual definimos um tipo de Esquema de localporque vamos retornar o objeto que excluímos.

Nos args, só precisamos definir o id do local a ser excluído.

Na função de resolução, retornaremos uma Promise que procurará o local pelo ID e executará a remoção. Rejeitaremos a promessa caso o local não seja encontrado ou se houver um erro ao removê-lo; ou resolveremos a promessa com os dados do local caso o removamos com êxito.


deletePlace: {

Tipo: PlaceSchema,

descrição: 'Delete a place',

args: {

id: {

type: new GraphQLNonNull(GraphQLString),

descrição: 'Id do local',

},

},

resolve(source, args) {

return new Promise((resolve, reject) => {

Place.getById(args.id, (err, place) => {

se (err) {

reject(err);

} else {

place.remove((err) => {

se (err) {

reject(err);

}

resolver(lugar);

});

}

})

});

}

}

Teste

Para testar nosso aplicativo, vamos usar o Graphiql, que permitimos em nosso servidor. Para isso, precisamos acessar http://localhost:5000/graphql

graphiql

Nessa página, podemos realizar as consultas e mutações que definimos anteriormente.

Criar


mutação {

createPlace(

nome: "testplace"

descrição: "testdescription"

latitude: 1,36

longitude: 18,36

) {

id

}

}

mutation create

Atualização


mutação {

updatePlace(

id: “41133f98-18e8-4979-89e0-7af012b0e14f”

nome: "updateplace"

descrição: "updatedescription"

latitude: 2,36

longitude: 15,96

) {

id

nome

descrição

}

}

mutation update

Excluir


mutação {

deletePlace(id: "41133f98-18e8-4979-89e0-7af012b0e14f") {

id

}

}
mutation delete

Consultar tudo


consulta {

allPlaces {

id

nome

localização {

lat

solitário

}

}

}

query all

Consulta por caixa de limite


consulta {

Locais(

minLon: 3

maxLon: 5

minLat: 49

maxLat: 51

) {

nome

localização {

lat

solitário

}

}

}
query bbox

Consultar um local por id


mutação {

deletePlace(id: "41133f98-18e8-4979-89e0-7af012b0e14f") {

id

}

}


query id

Conclusão

O Graphql é uma boa linguagem de consulta que nos permite consultar apenas as informações que definimos, de modo que podemos evitar a busca excessiva ou insuficiente e podemos ter certeza de que sempre teremos os dados.

Em um servidor Graphql, os clientes usam apenas um único ponto de extremidade, o que oculta a complexidade e a lógica do back-end, de modo que o servidor pode se conectar a diferentes back-ends ou usar diferentes bancos de dados e, se eles mudarem, a lógica dos clientes não precisará mudar porque o ponto de extremidade é o mesmo.

Também vimos como realizar uma consulta geográfica em nossos dados com o couchbase.

Referências

Autor

Postado por Laura Czajkowski, gerente da comunidade de desenvolvedores, Couchbase

Laura Czajkowski é a Snr. Developer Community Manager da Couchbase, supervisionando a comunidade. Ela é responsável pelo nosso boletim informativo mensal para desenvolvedores.

Deixar uma resposta