{"id":2854,"date":"2017-02-28T08:41:20","date_gmt":"2017-02-28T16:41:20","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=2854"},"modified":"2025-06-13T21:40:21","modified_gmt":"2025-06-14T04:40:21","slug":"graphql-server-node-couchbase-ottoman-spatial-view","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/","title":{"rendered":"Graphql server with node and couchbase, ottoman and spatial view"},"content":{"rendered":"<p><em><a href=\"https:\/\/www.linkedin.com\/in\/jm8nav\"><span class=\"no-underline\">Jose Navarro<\/span><\/a> is a full stack developer at FAMOCO in Brussels, Belgium. He has been working for the last 3 years as a web <a href=\"https:\/\/github.com\/jmn8718\">developer<\/a> with Node.js, Java, AngularJS, and ReactJS, and has deep interest in web development and mobile technologies.<\/em><\/p>\n<p>We are going to develop a <u><a href=\"https:\/\/graphql.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">Graphql<\/a><\/u>\u00a0server in nodejs with express. Graphql is a query language for APIs, it was developed by facebook and it was released in 2015. It is designed to build client applications by providing an intuitive and flexible syntax and system for describing their data requirements and interactions. One of the biggest differences with REST APIs is that you only have one entry endpoint for all the resources, instead of one endpoint for every resource; And with graphql you specify what attributes you want for each request, instead of receive whatever the REST API service returns, so we can be sure that we have all the data that we need and reduce the size of our requests.<\/p>\n<p>Facebook has been using it for a few years in their mobile app, for example in the iOS app, and old version of their mobile app still works without problem because the graphql did not change, because it is one endpoint with the same schema, this probably would not be possible with REST APIs because when you release a new version of you API, your endpoint probably is going to change, so the clients needs to adapt to the new endpoint and data. Also <u><a href=\"https:\/\/developer.github.com\/early-access\/graphql\/\" target=\"_blank\" rel=\"noopener noreferrer\">Github<\/a><\/u>\u00a0open their graphql server, so the users can query their services using graphql, instead of the REST APIs services..<\/p>\n<p>With the server we are going to query and create <strong>Places<\/strong>. To store the data we are going to use couchbase and we are going to use Spatial views to query the Places by its geographical location. I wrote a previous <u><a href=\"https:\/\/www.couchbase.com\/blog\/develop-a-rest-api-with-couchbase-ottoman-nodejs-hapijs\/\" target=\"_blank\" rel=\"noopener noreferrer\">post<\/a><\/u>\u00a0about node and couchbase, so I will skip the configuration of the db that I wrote in the previous post.<\/p>\n<h2>Requirements<\/h2>\n<p>You need to have installed in your computer:<\/p>\n<p>&#8211; nodejs<\/p>\n<p>&#8211; couchbase server<\/p>\n<p>You can find the code in the <u><a href=\"https:\/\/github.com\/jmn8718\/node-couchbase-graphql.git\" target=\"_blank\" rel=\"noopener noreferrer\">github repo<\/a><\/u>.<\/p>\n<h2>Spatial View<\/h2>\n<p>First of all we need to create the spatial view. We go to the admin page, in my case <u><a href=\"https:\/\/localhost:8091\/\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/localhost:8091\/<\/a><\/u>\u00a0and log in with my user and password. Then click on <strong>Data Buckets<\/strong>\u00a0and create a bucket, i called it <em>graphql<\/em>. After that we click on <strong>View<\/strong>, then we click on <strong>Create Development Spatial View<\/strong>, and we type the values.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2017\/02\/create_view.png\" alt=\"Create Spatial Development View\" width=\"404\" height=\"301\" \/><\/p>\n<p>I used <em>place_by_location<\/em>\u00a0in both <strong>Design Document Name<\/strong>\u00a0and <strong>View Name<\/strong>. Now click on <strong>edit<\/strong>, and add the following code<\/p>\n<p><code><br \/>\nfunction\u00a0(doc)\u00a0{<\/code><\/p>\n<p>if\u00a0(doc._type ===\u00a0&#8216;Place&#8217;\u00a0&amp;&amp;\u00a0doc.location)\u00a0{<\/p>\n<p>emit([{<\/p>\n<p>&#8220;type&#8221;:\u00a0&#8220;Point&#8221;,<\/p>\n<p>&#8220;coordinates&#8221;:\u00a0[doc.location.lon,\u00a0doc.location.lat]<\/p>\n<p>}],\u00a0doc);<\/p>\n<p>}<\/p>\n<p>}<br \/>\nand click on <strong>Save<\/strong>.<\/p>\n<p>Here you can also test the view with the documents on the bucket.<\/p>\n<h2>Place Model<\/h2>\n<p>For our places, we are going to store the name of the place and a description as string.<\/p>\n<p>Since we want to use the <a href=\"https:\/\/developer.couchbase.com\/documentation\/server\/4.6\/views\/sv-writing-views.html\">SpatialView<\/a> that <a href=\"https:\/\/www.couchbase.com\/developers\/\">Couchbase<\/a> provides, we are going to store the location of the place in an object called *location* where we are going to store the latitude and longitude.<\/p>\n<p>Also we will set by default the created date when we add the place.<\/p>\n<p><code><br \/>\nconst\u00a0PlaceModel\u00a0=\u00a0ottoman.model('Place',\u00a0{<\/code><\/p>\n<p>name:\u00a0&#8216;string&#8217;,<\/p>\n<p>description:\u00a0&#8216;string&#8217;,<\/p>\n<p>location:\u00a0{<\/p>\n<p>lat:\u00a0&#8216;number&#8217;,<\/p>\n<p>lon:\u00a0&#8216;number&#8217;<\/p>\n<p>},<\/p>\n<p>created:\u00a0{<\/p>\n<p>type:\u00a0&#8216;Date&#8217;,<\/p>\n<p>default:\u00a0Date.now<\/p>\n<p>}<\/p>\n<p>});<br \/>\nFor the spatial query, we are going to define a function that will perform a spatial query. For that we are going to create a spatial query using the couchbase package.<\/p>\n<p><code><br \/>\nconst\u00a0queryByLocation =\u00a0(bbox =\u00a0[0,\u00a00,\u00a00,\u00a00],\u00a0next)\u00a0=&gt;\u00a0{<\/code><\/p>\n<p>const\u00a0query =\u00a0couchbase.SpatialQuery.from(&#8216;dev_place_by_location&#8217;,\u00a0&#8216;place_by_location&#8217;).bbox(bbox);<\/p>\n<p>bucket.query(query,\u00a0next);<\/p>\n<p>}<br \/>\nIn the *from* function, we have to provide the *design document name* and the *view name*. Then in the bbox(bounding box), we need to provide the array of 4 floats **[ min Longitude , min Latitude , max Longitude , max Latitude ]**.<\/p>\n<p>The last step is to perform the query in the bucket.<\/p>\n<h2>Graphql Server<\/h2>\n<p>We are going to use an express server and the package express-graphql.<\/p>\n<p>We import the schema of our graphql server that we are going to define later.<\/p>\n<p><code><br \/>\nconst\u00a0express =\u00a0require('express');<\/code><\/p>\n<p>const\u00a0graphqlHTTP =\u00a0require(&#8216;express-graphql&#8217;);<\/p>\n<p>const\u00a0PORT =\u00a0process.env.PORT ||\u00a05000;<\/p>\n<p>const\u00a0schema =\u00a0require(&#8216;.\/schemas&#8217;);<\/p>\n<p>const\u00a0app =\u00a0express();<\/p>\n<p>app.use(&#8216;\/graphql&#8217;,\u00a0graphqlHTTP({<\/p>\n<p>schema:\u00a0schema,<\/p>\n<p>graphiql:\u00a0true,<\/p>\n<p>}));<\/p>\n<p>\/\/ start server<\/p>\n<p>const\u00a0server =\u00a0app.listen(PORT,\u00a0()\u00a0=&gt;\u00a0{<\/p>\n<p>console.log(`Server started at ${ server.address().port }`);<\/p>\n<p>});<br \/>\nIn the graphql server, we are going to use the route *\/graphql*. And we are going to set some option, like graphiql, that will provide us a graphic interface to execute queries.<\/p>\n<p>The last step is start our express server.<\/p>\n<h2>Graphql Schemas<\/h2>\n<p>Graphql queries and mutations rely on the schemas that we define. So we have to create a schema for our Place object.<\/p>\n<p>First we are going to define a Schema for <strong>Location<\/strong>.<\/p>\n<p><code><br \/>\nconst\u00a0{<\/code><\/p>\n<p>GraphQLObjectType,<\/p>\n<p>GraphQLFloat,<\/p>\n<p>GraphQLNonNull,<\/p>\n<p>}\u00a0=\u00a0require(&#8216;graphql&#8217;);<\/p>\n<p>const\u00a0LocationSchema\u00a0=\u00a0new\u00a0GraphQLObjectType({<\/p>\n<p>name:\u00a0&#8216;Location&#8217;,<\/p>\n<p>description:\u00a0&#8216;Geographical location&#8217;,<\/p>\n<p>fields:\u00a0{<\/p>\n<p>lat:\u00a0{<\/p>\n<p>type:\u00a0new\u00a0GraphQLNonNull(GraphQLFloat),<\/p>\n<p>description:\u00a0&#8216;Latitude&#8217;,<\/p>\n<p>},<\/p>\n<p>lon:\u00a0{<\/p>\n<p>type:\u00a0new\u00a0GraphQLNonNull(GraphQLFloat),<\/p>\n<p>description:\u00a0&#8216;Longitude&#8217;,<\/p>\n<p>},<\/p>\n<p>}<\/p>\n<p>});<\/p>\n<p>module.exports =\u00a0LocationSchema;<\/p>\n<p>We need to import the types from <em>graphql<\/em>\u00a0package. In our schema we can define a name and a description, this fields are useful to document our queries, so the user can understand what that field means.<\/p>\n<p>Then we have to define <em>fields<\/em>, where we are going to specify the fields inside our schema, in this case, we have defined <em>lat<\/em>\u00a0and <em>lon<\/em>. In every field we have to specify the type, in this case these fields are float values, and they are required, so we use <em>GraphQLNonNull<\/em>\u00a0 and \u00a0the type <em>GraphQLFloat<\/em>. We add a description so we can now what they mean.<\/p>\n<p>Now we are going to define the schema <strong>Place<\/strong>.<\/p>\n<p>Here we are going to import the types from <em>graphql<\/em>\u00a0and the schema <em>Location<\/em>\u00a0that we have defined.<\/p>\n<p><code><br \/>\nconst\u00a0{<\/code><\/p>\n<p>GraphQLObjectType,<\/p>\n<p>GraphQLString,<\/p>\n<p>GraphQLNonNull,<\/p>\n<p>}\u00a0=\u00a0require(&#8216;graphql&#8217;);<\/p>\n<p>const\u00a0LocationSchema\u00a0=\u00a0require(&#8216;.\/location&#8217;);<\/p>\n<p>const\u00a0PlaceSchema\u00a0=\u00a0new\u00a0GraphQLObjectType({<\/p>\n<p>name:\u00a0&#8216;Place&#8217;,<\/p>\n<p>description:\u00a0&#8216;Place description&#8217;,<\/p>\n<p>fields:\u00a0{<\/p>\n<p>id:\u00a0{<\/p>\n<p>type:\u00a0GraphQLString,<\/p>\n<p>resolve(place)\u00a0{<\/p>\n<p>return\u00a0place._id;<\/p>\n<p>}<\/p>\n<p>},<\/p>\n<p>name:\u00a0{<\/p>\n<p>type:\u00a0GraphQLString,<\/p>\n<p>},<\/p>\n<p>description:{<\/p>\n<p>type:\u00a0GraphQLString,<\/p>\n<p>},<\/p>\n<p>location:\u00a0{<\/p>\n<p>type:\u00a0LocationSchema,<\/p>\n<p>},<\/p>\n<p>created:\u00a0{<\/p>\n<p>type:\u00a0GraphQLString,<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>});<\/p>\n<p>module.exports =\u00a0PlaceSchema;<\/p>\n<p>We are matching the fields from the model, so we do not have to provide the resolve function. Only for the field <em>id<\/em>, because couchbase return the value in the field <strong>_id<\/strong>.<\/p>\n<h2>Graphql Query<\/h2>\n<p>Query is the way that we retrieve data for the server.<\/p>\n<p>The query object is also a schema, like the previous ones. In this case the fields are the queries we allow the user to perform. We are going to define 3 types of query.<\/p>\n<h3>allPlaces<\/h3>\n<p>In this query we are going to query for all the places in the database, and we are going to order them by the created field, so we return first the newest places.<\/p>\n<p>Like we are going to return an array of Places, we assign to <strong>type<\/strong>\u00a0the type <em>GraphQLList<\/em>\u00a0and we provide the <em>Place schema<\/em><\/p>\n<p><code><br \/>\n...<\/code><\/p>\n<p>const\u00a0PlaceSchema\u00a0=\u00a0require(&#8216;.\/place&#8217;);<\/p>\n<p>const\u00a0Place\u00a0=\u00a0require(&#8216;..\/models\/place&#8217;);<\/p>\n<p>&#8230;<\/p>\n<p>allPlaces:\u00a0{<\/p>\n<p>type:\u00a0new\u00a0GraphQLList(PlaceSchema),<\/p>\n<p>description:\u00a0&#8216;Query for all places&#8217;,<\/p>\n<p>resolve(root,\u00a0args)\u00a0{<\/p>\n<p>return\u00a0new\u00a0Promise((resolve,\u00a0reject)\u00a0=&gt;\u00a0{<\/p>\n<p>Place.find({},\u00a0{<\/p>\n<p>sort:\u00a0{<\/p>\n<p>created:\u00a0-1<\/p>\n<p>},<\/p>\n<p>},\u00a0(err,\u00a0places)\u00a0=&gt;\u00a0{<\/p>\n<p>if\u00a0(err)\u00a0{<\/p>\n<p>reject(err);<\/p>\n<p>}<\/p>\n<p>resolve(places);<\/p>\n<p>})<\/p>\n<p>});<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>We also add a description, this field is optional.<\/p>\n<p>The last parameter is a function, that is the resolve function, that will specify how we are going to retrieve the data for our database. As our calls to the database are asynchronous, we are going to return a promise, that is going to use the Place model that we defined with ottoman. With the model we use find to query for documents, and we pass as the first parameter an empty object, because we want to query all the documents; The second parameter is the options of our query, in this case we are going to order by the field <em>created<\/em>\u00a0in descending order. Finally we provide the callback function that will resolve the promise with the values, or reject it in case of an error.<\/p>\n<h3>Places<\/h3>\n<p>In this query we are going to query using the Spatial view, so we have to pass the bbox points in the parameter of the query.<\/p>\n<p><code><br \/>\n...<\/code><\/p>\n<p>const\u00a0queryByLocation =\u00a0require(&#8216;..\/models\/place&#8217;).queryByLocation;<\/p>\n<p>&#8230;<\/p>\n<p>Places:\u00a0{<\/p>\n<p>type:\u00a0new\u00a0GraphQLList(PlaceSchema),<\/p>\n<p>description:\u00a0&#8216;Query for all places inside the boundary box&#8217;,<\/p>\n<p>args:\u00a0{<\/p>\n<p>minLon:\u00a0{<\/p>\n<p>type:\u00a0new\u00a0GraphQLNonNull(GraphQLFloat),<\/p>\n<p>description:\u00a0&#8216;Min Longitude of the boundary box&#8217;,<\/p>\n<p>},<\/p>\n<p>maxLon:\u00a0{<\/p>\n<p>type:\u00a0new\u00a0GraphQLNonNull(GraphQLFloat),<\/p>\n<p>description:\u00a0&#8216;Max Longitude of the boundary box&#8217;,<\/p>\n<p>},<\/p>\n<p>minLat:\u00a0{<\/p>\n<p>type:\u00a0new\u00a0GraphQLNonNull(GraphQLFloat),<\/p>\n<p>description:\u00a0&#8216;Min Latitude of the boundary box&#8217;,<\/p>\n<p>},<\/p>\n<p>maxLat:\u00a0{<\/p>\n<p>type:\u00a0new\u00a0GraphQLNonNull(GraphQLFloat),<\/p>\n<p>description:\u00a0&#8216;Max Latitude of the boundary box&#8217;,<\/p>\n<p>},<\/p>\n<p>},<\/p>\n<p>resolve(root,\u00a0args)\u00a0{<\/p>\n<p>\/\/ bbox = [ min Longitude , min Latitude , max Longitude , max Latitude ]<\/p>\n<p>const\u00a0bbox =\u00a0[<\/p>\n<p>args.minLon,<\/p>\n<p>args.minLat,<\/p>\n<p>args.maxLon,<\/p>\n<p>args.maxLat,<\/p>\n<p>];<\/p>\n<p>return\u00a0new\u00a0Promise((resolve,\u00a0reject)\u00a0=&gt;\u00a0{<\/p>\n<p>queryByLocation(bbox,\u00a0(err,\u00a0places)\u00a0=&gt;\u00a0{<\/p>\n<p>if\u00a0(err)\u00a0{<\/p>\n<p>reject(err);<\/p>\n<p>}<\/p>\n<p>resolve(places.map((place)\u00a0=&gt;\u00a0place.value));<\/p>\n<p>})<\/p>\n<p>});<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>First we import the function that we defined to perform the spatial query.<\/p>\n<p>As the previous query, we define the type as an array of places, and add a description.<\/p>\n<p>In this query, we need some parameter, so we define the args, that refers to the parameter; Each value inside <em>args<\/em>\u00a0correspond with the parameters, in this case we define 4, <strong>minLon, maxLon, minLat, maxLat<\/strong>\u00a0and for all of them we are going to define the type as required and floats.<\/p>\n<p>In this case, the resolve function is also a promise. First we build the bbox array to pass the function <em>queryByLocation<\/em>. In case of an error, we will reject the promise with an error; In case of success, we need to map the object from the db, because the spatial view returns the geopoint and value, where we are returning the full document, it will change if we define a different spatial view.<\/p>\n<h3>Place<\/h3>\n<p>The last query that we are going to define, is the one to query for one place by its id.<\/p>\n<p><code><br \/>\nPlace:\u00a0{<\/code><\/p>\n<p>type:\u00a0PlaceSchema,<\/p>\n<p>description:\u00a0&#8216;Query for a place by the place id&#8217;,<\/p>\n<p>args:\u00a0{<\/p>\n<p>id:\u00a0{<\/p>\n<p>type:\u00a0new\u00a0GraphQLNonNull(GraphQLString),<\/p>\n<p>description:\u00a0&#8216;Place id&#8217;,<\/p>\n<p>}<\/p>\n<p>},<\/p>\n<p>resolve(root,\u00a0args)\u00a0{<\/p>\n<p>return\u00a0new\u00a0Promise((resolve,\u00a0reject)\u00a0=&gt;\u00a0{<\/p>\n<p>Place.getById(args.id,\u00a0(err,\u00a0place)\u00a0=&gt;\u00a0{<\/p>\n<p>if\u00a0(err)\u00a0{<\/p>\n<p>reject(err);<\/p>\n<p>}<\/p>\n<p>resolve(place);<\/p>\n<p>});<\/p>\n<p>});<\/p>\n<p>}<br \/>\nIn this case, the type is the <em>Place schema<\/em>, in the args, we only need to define the <strong>id<\/strong>\u00a0and we set it to string and required.<\/p>\n<p>The resolve function, again is a promise, in this case we are going to use the function <em>queryById<\/em>\u00a0from the model, and we pass the <em>id<\/em>\u00a0value from the args object.<\/p>\n<h2>Graphql Mutation<\/h2>\n<p>With mutations we can modify the data in our server. As the query, mutations object are schemas. So we have to define the same fields as the previous schemas.<\/p>\n<p>When we perform the mutation query we provide the values between parenthesis, and like the queries, we provide the values we want to retrieve of the modified object.<\/p>\n<p>Here we are going to perform the creation, update and delete of Places.<\/p>\n<h3>createPlace<\/h3>\n<p>In this mutation we are going to create a new place.<\/p>\n<p>In the type of the schema we are going to define it as <em>Place Schema<\/em>, because we are going to return the created Place.<\/p>\n<p><code><br \/>\ncreatePlace:\u00a0{<\/code><\/p>\n<p>type:\u00a0PlaceSchema,<\/p>\n<p>description:\u00a0&#8216;Create a place&#8217;,<\/p>\n<p>args:\u00a0{<\/p>\n<p>name:\u00a0{<\/p>\n<p>type:\u00a0new\u00a0GraphQLNonNull(GraphQLString),<\/p>\n<p>description:\u00a0&#8216;Name of the place&#8217;,<\/p>\n<p>},<\/p>\n<p>description:\u00a0{<\/p>\n<p>type:\u00a0new\u00a0GraphQLNonNull(GraphQLString),<\/p>\n<p>description:\u00a0&#8216;Description of the place&#8217;,<\/p>\n<p>},<\/p>\n<p>latitude:\u00a0{<\/p>\n<p>type:\u00a0new\u00a0GraphQLNonNull(GraphQLFloat),<\/p>\n<p>description:\u00a0&#8216;Latitude of the place&#8217;,<\/p>\n<p>},<\/p>\n<p>longitude:\u00a0{<\/p>\n<p>type:\u00a0new\u00a0GraphQLNonNull(GraphQLFloat),<\/p>\n<p>description:\u00a0&#8216;Longitude of the place&#8217;,<\/p>\n<p>}<\/p>\n<p>},<\/p>\n<p>resolve(source,\u00a0args)\u00a0{<\/p>\n<p>return\u00a0new\u00a0Promise((resolve,\u00a0reject)\u00a0=&gt;\u00a0{<\/p>\n<p>const\u00a0place =\u00a0new\u00a0Place({<\/p>\n<p>name:\u00a0args.name,<\/p>\n<p>description:\u00a0args.description,<\/p>\n<p>location:\u00a0{<\/p>\n<p>lat:\u00a0args.latitude,<\/p>\n<p>lon:\u00a0args.longitude,<\/p>\n<p>},<\/p>\n<p>});<\/p>\n<p>place.save((err)\u00a0=&gt;\u00a0{<\/p>\n<p>if\u00a0(err)\u00a0{<\/p>\n<p>reject(err);<\/p>\n<p>}<\/p>\n<p>resolve(place);<\/p>\n<p>})<\/p>\n<p>});<\/p>\n<p>}<\/p>\n<p>}<br \/>\nLike the queries, we define the args with the values that we require to create a new Place. In this case we require name and description as strings, and latitude and longitude as floats, all fields are going to be required.<\/p>\n<p>In the resolve function, we are going to return a promise. Inside the promise we are going to create the place with the values of the query inside <em>args<\/em>. Then we are going to perform <em>save<\/em>\u00a0on the place object. Finally, in case of an error saving the place, we are going to reject the promise with the error, or we are going to resolve the promise with the place data.<\/p>\n<h3>updatePlace<\/h3>\n<p>As in the <em>createPlace<\/em>\u00a0mutation, the <em>updatePlace<\/em>\u00a0mutation is similar, the differences are that in this case, all the values in the args are not required, and the id field is a required string; And in the resolve function, first we are going to look for the object by the <em>id<\/em>, then we check the fields provided by the user and update the place, and finally we save it, and return the new object<\/p>\n<p><code><br \/>\nupdatePlace:\u00a0{<\/code><\/p>\n<p>type:\u00a0PlaceSchema,<\/p>\n<p>description:\u00a0&#8216;Update a place&#8217;,<\/p>\n<p>args:\u00a0{<\/p>\n<p>id:\u00a0{<\/p>\n<p>type:\u00a0new\u00a0GraphQLNonNull(GraphQLString),<\/p>\n<p>description:\u00a0&#8216;Id of the place&#8217;,<\/p>\n<p>},<\/p>\n<p>name:\u00a0{<\/p>\n<p>type:\u00a0GraphQLString,<\/p>\n<p>description:\u00a0&#8216;Name of the place&#8217;,<\/p>\n<p>},<\/p>\n<p>description:\u00a0{<\/p>\n<p>type:\u00a0GraphQLString,<\/p>\n<p>description:\u00a0&#8216;Description of the place&#8217;,<\/p>\n<p>},<\/p>\n<p>latitude:\u00a0{<\/p>\n<p>type:\u00a0GraphQLFloat,<\/p>\n<p>description:\u00a0&#8216;Latitude of the place&#8217;,<\/p>\n<p>},<\/p>\n<p>longitude:\u00a0{<\/p>\n<p>type:\u00a0GraphQLFloat,<\/p>\n<p>description:\u00a0&#8216;Longitude of the place&#8217;,<\/p>\n<p>}<\/p>\n<p>},<\/p>\n<p>resolve(source,\u00a0args)\u00a0{<\/p>\n<p>return\u00a0new\u00a0Promise((resolve,\u00a0reject)\u00a0=&gt;\u00a0{<\/p>\n<p>Place.getById(args.id,\u00a0(err,\u00a0place)\u00a0=&gt;\u00a0{<\/p>\n<p>if\u00a0(err)\u00a0{<\/p>\n<p>reject(err);<\/p>\n<p>}\u00a0else\u00a0{<\/p>\n<p>if\u00a0(args.name)\u00a0{<\/p>\n<p>place.name =\u00a0args.name;<\/p>\n<p>}<\/p>\n<p>if\u00a0(args.description)\u00a0{<\/p>\n<p>place.name =\u00a0args.name;<\/p>\n<p>}<\/p>\n<p>if\u00a0(args.latitude)\u00a0{<\/p>\n<p>place.location.lat =\u00a0args.latitude;<\/p>\n<p>}<\/p>\n<p>if\u00a0(args.longitude)\u00a0{<\/p>\n<p>place.location.lon =\u00a0args.longitude;<\/p>\n<p>}<\/p>\n<p>place.save((err)\u00a0=&gt;\u00a0{<\/p>\n<p>if\u00a0(err)\u00a0{<\/p>\n<p>reject(err);<\/p>\n<p>}<\/p>\n<p>resolve(place);<\/p>\n<p>});<\/p>\n<p>}<\/p>\n<p>})<\/p>\n<p>});<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<h3>deletePlace<\/h3>\n<p>The last mutation is the delete, here we define a type of <em>Place Schema<\/em>, because we are going to return the object we delete.<\/p>\n<p>In the args, we only need to define the <em>id<\/em>\u00a0of the place to delete.<\/p>\n<p>In the resolve function, we are going to return a Promise that is going to search for the place by id, and the perform the remove. We will reject the promise in case the place is not found or if there is an error while removing it; Or we are going to resolve the promise with the place data in case we remove it successfully.<\/p>\n<p><code><br \/>\ndeletePlace:\u00a0{<\/code><\/p>\n<p>type:\u00a0PlaceSchema,<\/p>\n<p>description:\u00a0&#8216;Delete a place&#8217;,<\/p>\n<p>args:\u00a0{<\/p>\n<p>id:\u00a0{<\/p>\n<p>type:\u00a0new\u00a0GraphQLNonNull(GraphQLString),<\/p>\n<p>description:\u00a0&#8216;Id of the place&#8217;,<\/p>\n<p>},<\/p>\n<p>},<\/p>\n<p>resolve(source,\u00a0args)\u00a0{<\/p>\n<p>return\u00a0new\u00a0Promise((resolve,\u00a0reject)\u00a0=&gt;\u00a0{<\/p>\n<p>Place.getById(args.id,\u00a0(err,\u00a0place)\u00a0=&gt;\u00a0{<\/p>\n<p>if\u00a0(err)\u00a0{<\/p>\n<p>reject(err);<\/p>\n<p>}\u00a0else\u00a0{<\/p>\n<p>place.remove((err)\u00a0=&gt;\u00a0{<\/p>\n<p>if\u00a0(err)\u00a0{<\/p>\n<p>reject(err);<\/p>\n<p>}<\/p>\n<p>resolve(place);<\/p>\n<p>});<\/p>\n<p>}<\/p>\n<p>})<\/p>\n<p>});<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<h2>Test<\/h2>\n<p>To test our app, we are going to use Graphiql, that we allow in our server, for that we have to visit https:\/\/localhost:5000\/graphql<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2017\/02\/graphiql.png\" alt=\"graphiql\" \/><\/p>\n<p>In this page, we can perform the queries and mutations that we defined previously.<\/p>\n<h3>Create<\/h3>\n<p><code><br \/>\nmutation {<\/code><\/p>\n<p>createPlace(<\/p>\n<p>name:\u00a0&#8220;testplace&#8221;<\/p>\n<p>description:\u00a0&#8220;testdescription&#8221;<\/p>\n<p>latitude:\u00a01.36<\/p>\n<p>longitude:\u00a018.36<\/p>\n<p>)\u00a0{<\/p>\n<p>id<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2017\/02\/mutation_create.png\" alt=\"mutation create\" \/><\/p>\n<h3>Update<\/h3>\n<p><code><br \/>\nmutation {<\/code><\/p>\n<p>updatePlace(<\/p>\n<p>id:\u00a0&#8220;41133f98-18e8-4979-89e0-7af012b0e14f&#8221;<\/p>\n<p>name:\u00a0&#8220;updateplace&#8221;<\/p>\n<p>description:\u00a0&#8220;updatedescription&#8221;<\/p>\n<p>latitude:\u00a02.36<\/p>\n<p>longitude:\u00a015.96<\/p>\n<p>)\u00a0{<\/p>\n<p>id<\/p>\n<p>name<\/p>\n<p>description<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2017\/02\/mutation_update.png\" alt=\"mutation update\" \/><\/p>\n<h3>Delete<\/h3>\n<p><code><br \/>\nmutation {<\/code><\/p>\n<p>deletePlace(id:\u00a0&#8220;41133f98-18e8-4979-89e0-7af012b0e14f&#8221;)\u00a0{<\/p>\n<p>id<\/p>\n<p>}<\/p>\n<p>}<br \/>\n<img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2017\/02\/mutation_delete.png\" alt=\"mutation delete\" \/><\/p>\n<h3>Query All<\/h3>\n<p><code><br \/>\nquery {<\/code><\/p>\n<p>allPlaces {<\/p>\n<p>id<\/p>\n<p>name<\/p>\n<p>location {<\/p>\n<p>lat<\/p>\n<p>lon<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2017\/02\/query_all.png\" alt=\"query all\" \/><\/p>\n<h3>Query by boundary box<\/h3>\n<p><code><br \/>\nquery {<\/code><\/p>\n<p>Places(<\/p>\n<p>minLon:\u00a03<\/p>\n<p>maxLon:\u00a05<\/p>\n<p>minLat:\u00a049<\/p>\n<p>maxLat:\u00a051<\/p>\n<p>)\u00a0{<\/p>\n<p>name<\/p>\n<p>location {<\/p>\n<p>lat<\/p>\n<p>lon<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>}<br \/>\n<img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2017\/02\/query_bbox.png\" alt=\"query bbox\" \/><\/p>\n<h3>Query a place by id<\/h3>\n<p><code><br \/>\n```<\/code><\/p>\n<p>mutation {<\/p>\n<p>deletePlace(id:\u00a0&#8220;41133f98-18e8-4979-89e0-7af012b0e14f&#8221;)\u00a0{<\/p>\n<p>id<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&#8220;`<br \/>\n<img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2017\/02\/query_id.png\" alt=\"query id\" \/><\/p>\n<h2>Conclusion<\/h2>\n<p>Graphql is a good query language that allow us to query only for the information that we define, so we can avoid underfetching or overfetching, and we can be sure that we always have the data.<\/p>\n<p>In a Graphql server, the clients only use one single endpoint, so it hides the complexity and logics of the backend, so the server can connect to different backends, or use different databases, and if they change, the clients logic do not have to change because the endpoint is the same.<\/p>\n<p>Also we have seen how to perform geographical query in our data with couchbase.<\/p>\n<h2>References<\/h2>\n<ul>\n<li><u><a href=\"https:\/\/github.com\/jmn8718\/node-couchbase-graphql.git\" target=\"_blank\" rel=\"noopener noreferrer\">Code<\/a><\/u><\/li>\n<li><u><a href=\"https:\/\/graphql.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">Graphql<\/a><\/u><\/li>\n<li><u><a href=\"https:\/\/developer.github.com\/early-access\/graphql\/\" target=\"_blank\" rel=\"noopener noreferrer\">Github graphql<\/a><\/u><\/li>\n<li><u><a href=\"https:\/\/developer.couchbase.com\/documentation\/server\/4.6\/views\/sv-writing-views.html\" target=\"_blank\" rel=\"noopener noreferrer\">Couchbase Spatial View<\/a><\/u><\/li>\n<li><u><a href=\"https:\/\/wiki.openstreetmap.org\/wiki\/Bounding_Box\" target=\"_blank\" rel=\"noopener noreferrer\">Boundary box<\/a><\/u><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p> Graphql is a query language for APIs, it was developed by facebook and it was released in 2015. It is designed to build client applications by providing an intuitive and flexible syntax and system for describing their data requirements and interactions. O<\/p>\n","protected":false},"author":53,"featured_media":13873,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1822,10128],"tags":[1726],"ppma_author":[9026],"class_list":["post-2854","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-node-js","category-ottoman","tag-graph-database"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.9 (Yoast SEO v25.9) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Graphql server in nodejs with express - The Couchbase Blog<\/title>\n<meta name=\"description\" content=\"Learn how to perform a geographical query in our data with couchbase.and how we can avoid underfetching or overfetching using Graphql.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Graphql server with node and couchbase, ottoman and spatial view\" \/>\n<meta property=\"og:description\" content=\"Learn how to perform a geographical query in our data with couchbase.and how we can avoid underfetching or overfetching using Graphql.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2017-02-28T16:41:20+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-14T04:40:21+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2017\/02\/create_view.png\" \/>\n<meta name=\"author\" content=\"Laura Czajkowski, Developer Community Manager, Couchbase\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Laura Czajkowski, Developer Community Manager, Couchbase\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"14 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/\"},\"author\":{\"name\":\"Laura Czajkowski, Developer Community Manager, Couchbase\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/5f1a0ece4e644bc8c037686fbc8f3220\"},\"headline\":\"Graphql server with node and couchbase, ottoman and spatial view\",\"datePublished\":\"2017-02-28T16:41:20+00:00\",\"dateModified\":\"2025-06-14T04:40:21+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/\"},\"wordCount\":2619,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"keywords\":[\"graph database\"],\"articleSection\":[\"Node.js\",\"Ottoman.js ODM\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/\",\"name\":\"Graphql server in nodejs with express - The Couchbase Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"datePublished\":\"2017-02-28T16:41:20+00:00\",\"dateModified\":\"2025-06-14T04:40:21+00:00\",\"description\":\"Learn how to perform a geographical query in our data with couchbase.and how we can avoid underfetching or overfetching using Graphql.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"width\":1800,\"height\":630},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Graphql server with node and couchbase, ottoman and spatial view\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/5f1a0ece4e644bc8c037686fbc8f3220\",\"name\":\"Laura Czajkowski, Developer Community Manager, Couchbase\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/9deb07d5daaa00220534c31768bc4409\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/bc8eebaf25cbe39bc12fd7b1ef92550becc3953ab877a3f0285a59ec2d30b754?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/bc8eebaf25cbe39bc12fd7b1ef92550becc3953ab877a3f0285a59ec2d30b754?s=96&d=mm&r=g\",\"caption\":\"Laura Czajkowski, Developer Community Manager, Couchbase\"},\"description\":\"Laura Czajkowski is the Snr. Developer Community Manager at Couchbase overseeing the community. She\u2019s responsible for our monthly developer newsletter.\",\"url\":\"https:\/\/www.couchbase.com\/blog\/author\/laura-czajkowski\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Graphql server in nodejs with express - The Couchbase Blog","description":"Learn how to perform a geographical query in our data with couchbase.and how we can avoid underfetching or overfetching using Graphql.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/","og_locale":"en_US","og_type":"article","og_title":"Graphql server with node and couchbase, ottoman and spatial view","og_description":"Learn how to perform a geographical query in our data with couchbase.and how we can avoid underfetching or overfetching using Graphql.","og_url":"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/","og_site_name":"The Couchbase Blog","article_published_time":"2017-02-28T16:41:20+00:00","article_modified_time":"2025-06-14T04:40:21+00:00","og_image":[{"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2017\/02\/create_view.png","type":"","width":"","height":""}],"author":"Laura Czajkowski, Developer Community Manager, Couchbase","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Laura Czajkowski, Developer Community Manager, Couchbase","Est. reading time":"14 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/"},"author":{"name":"Laura Czajkowski, Developer Community Manager, Couchbase","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/5f1a0ece4e644bc8c037686fbc8f3220"},"headline":"Graphql server with node and couchbase, ottoman and spatial view","datePublished":"2017-02-28T16:41:20+00:00","dateModified":"2025-06-14T04:40:21+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/"},"wordCount":2619,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","keywords":["graph database"],"articleSection":["Node.js","Ottoman.js ODM"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/","url":"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/","name":"Graphql server in nodejs with express - The Couchbase Blog","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","datePublished":"2017-02-28T16:41:20+00:00","dateModified":"2025-06-14T04:40:21+00:00","description":"Learn how to perform a geographical query in our data with couchbase.and how we can avoid underfetching or overfetching using Graphql.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","width":1800,"height":630},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/graphql-server-node-couchbase-ottoman-spatial-view\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Graphql server with node and couchbase, ottoman and spatial view"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"The Couchbase Blog","description":"Couchbase, the NoSQL Database","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"The Couchbase Blog","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/5f1a0ece4e644bc8c037686fbc8f3220","name":"Laura Czajkowski, Developer Community Manager, Couchbase","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/9deb07d5daaa00220534c31768bc4409","url":"https:\/\/secure.gravatar.com\/avatar\/bc8eebaf25cbe39bc12fd7b1ef92550becc3953ab877a3f0285a59ec2d30b754?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/bc8eebaf25cbe39bc12fd7b1ef92550becc3953ab877a3f0285a59ec2d30b754?s=96&d=mm&r=g","caption":"Laura Czajkowski, Developer Community Manager, Couchbase"},"description":"Laura Czajkowski is the Snr. Developer Community Manager at Couchbase overseeing the community. She\u2019s responsible for our monthly developer newsletter.","url":"https:\/\/www.couchbase.com\/blog\/author\/laura-czajkowski\/"}]}},"authors":[{"term_id":9026,"user_id":53,"is_guest":0,"slug":"laura-czajkowski","display_name":"Laura Czajkowski, Developer Community Manager, Couchbase","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/bc8eebaf25cbe39bc12fd7b1ef92550becc3953ab877a3f0285a59ec2d30b754?s=96&d=mm&r=g","author_category":"","last_name":"Czajkowski","first_name":"Laura","job_title":"","user_url":"","description":"Laura Czajkowski is the Snr. Developer Community Manager at Couchbase overseeing the community. She\u2019s responsible for our monthly developer newsletter."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/2854","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/users\/53"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/comments?post=2854"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/2854\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media\/13873"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media?parent=2854"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/categories?post=2854"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/tags?post=2854"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=2854"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}