<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: yoav</title>
    <description>The latest articles on DEV Community by yoav (@yoav).</description>
    <link>https://dev.to/yoav</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F64304%2F8337fc53-944b-443e-abd8-18c0732c0bf5.png</url>
      <title>DEV Community: yoav</title>
      <link>https://dev.to/yoav</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yoav"/>
    <language>en</language>
    <item>
      <title>GraphQL-to-MongoDB, or how I learned to stop worrying and love generated query APIs</title>
      <dc:creator>yoav</dc:creator>
      <pubDate>Mon, 26 Mar 2018 13:11:08 +0000</pubDate>
      <link>https://dev.to/yoav/graphql-to-mongodb-or-how-i-learned-to-stop-worrying-and-love-generated-query-apis-47pk</link>
      <guid>https://dev.to/yoav/graphql-to-mongodb-or-how-i-learned-to-stop-worrying-and-love-generated-query-apis-47pk</guid>
      <description>&lt;p&gt;In this post we’ll have a look at leveraging GraphQL types to expose MongoDB capabilities in NodeJs. We will examine &lt;a href="https://www.npmjs.com/package/graphql-to-mongodb" rel="noopener noreferrer"&gt;graphql-to-mongodb&lt;/a&gt;, the unobtrusive solution we came up with for our service, and its rationales.&lt;/p&gt;

&lt;h2&gt;
  
  
  The advent of the GraphQL MongoDB stack
&lt;/h2&gt;

&lt;p&gt;Relative newcomer GraphQL and established MongoDB are two technologies that appear well suited to one another. MongoDB is a document-oriented DB with a flexible query language. GraphQL is a service API and a query language at the same time. It works by defining a hierarchical, typed, and parameterized schema. Both technologies take and receive arguments in a hierarchical data structure. Taken together, the two compatible technologies can make for a streamlined interface between your client and data.&lt;/p&gt;

&lt;p&gt;First off, if you feel that your knowledge of either technology could use a refresher, you’re welcome to read more about &lt;a href="http://graphql.org/learn/" rel="noopener noreferrer"&gt;GraphQL&lt;/a&gt; or &lt;a href="https://docs.mongodb.com/manual/reference/method/db.collection.find/" rel="noopener noreferrer"&gt;MongoDB&lt;/a&gt; before continuing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Growing pains
&lt;/h2&gt;

&lt;p&gt;Implementing a way to expose MongoDB querying on a GraphQL backend is not a trivial task; we learned that when we tried to join the two in our latest NodeJs service. It’s easy enough to start by adding a single query field, using a single comparison operator, one at a time. As the query complexity of your clients increases, however, you can easily find yourself maintaining a disorganized mess of filtering code.&lt;/p&gt;

&lt;p&gt;A developer might be tempted to simply accept a generic JSON type as input, passing a client's input directly to MongoDB, but we saw that kind of solution to be less than satisfactory. Not only does that approach miss the entire point of GraphQL, it also gives up control over how the client may communicate with the DB.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our ideal API
&lt;/h2&gt;

&lt;p&gt;Upon recognizing that the issue was less than simple, we set out on a search for a solution that suited our needs, which were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An interface to MongoDB’s powerful querying capabilities&lt;/li&gt;
&lt;li&gt;Simple implementation&lt;/li&gt;
&lt;li&gt;Unobtrusive integration&lt;/li&gt;
&lt;li&gt;Explicitness and consistency to the GraphQL type schema&lt;/li&gt;
&lt;li&gt;No vulnerabilities to NoSQL injections&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unfortunately, our search yielded less than fruitful results.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you want something done right…
&lt;/h2&gt;

&lt;p&gt;As is often the case, we couldn’t find a mature and well-documented third-party solution that met our needs, prompting us to design one ourselves. Subsequently leading us to come up with an answer in the form of the package &lt;strong&gt;graphql-to-mongodb&lt;/strong&gt;, publicly available on both &lt;a href="https://github.com/Soluto/graphql-to-mongodb" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/graphql-to-mongodb" rel="noopener noreferrer"&gt;npm&lt;/a&gt;. Fundamentally, the package works by generating query arguments for your GraphQL schema at run-time, based on your existing GraphQL types. It parses the sent requests into MongoDB query params.&lt;/p&gt;

&lt;p&gt;Let's explore how it checks off the needs we identified earlier:&lt;/p&gt;

&lt;h3&gt;
  
  
  MongoDB for your client
&lt;/h3&gt;

&lt;p&gt;The package boosts your GraphQL API with the bulk of MongoDB’s most commonly used query operators. With it, a client can comb through the underlying data in a multitude of ways without requiring additional changes to your API for every new query.&lt;/p&gt;

&lt;p&gt;An example GraphQL query sent to a service using the package, showcasing filtering, sorting, and pagination:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
                &lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EQ&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; 
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DESC&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;pagination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
            &lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;center&gt;Queries 50 people, oldest first, over the age of 18, and whose first name is John&lt;/center&gt;

&lt;p&gt;All of that, and more, for a very small development overhead in your service.&lt;/p&gt;
&lt;h3&gt;
  
  
  Simplexity
&lt;/h3&gt;

&lt;p&gt;As with many packages, it strives to give you the biggest bang for your buck, hiding the complexities of the solution behind a simple integration. The exposed GraphQL field will be based on your underlying GraphQL type describing the data structure schema.&lt;/p&gt;

&lt;center&gt;Given a simple GraphQL type&lt;/center&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;When implementing the package, for the most common use case, all you need to do is to build a field in the GraphQL schema with a wrapped resolve function (getMongoDbQueryResolver) and generated arguments (getGraphQLQueryArgs).&lt;/p&gt;

&lt;center&gt;We’ll add the following field to our schema&lt;/center&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;That’s it!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the price of two function calls, you’ve just added all of the functionality described in the previous section to your API.&lt;/p&gt;

&lt;p&gt;The additional arguments supplied by the wrapper - filter, projection, and options - can be passed directly to MongoDB! To get an idea of what the package does, take a look at these arguments, produced from the previous section’s query:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  It’s just middleware
&lt;/h3&gt;

&lt;p&gt;It’s clearly visible that the package behaves like a middleware. This feature allows for the development of modules independent of MongoDB in the GraphQL service. &lt;/p&gt;

&lt;p&gt;Fields built using the package’s function can be easily extended. It’s simple enough to merge additional arguments into the generated args object, and to add handling in the resolver.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Resolving fields within your GraphQL type is also supported, though it requires a minor overhead in defining the field. One of the added benefits of the package is a minimization of throughput by projecting from MongoDB only the fields requested by the user. For resolved fields, what that means is that their dependencies might not always be queried from the DB. To resolve that issue, the package allows you to define a resolve field’s dependencies to ensure that when that field is queried, its dependencies will always be retrieved as well.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Alternatively, if throughput is of no concern, the projection argument supplied by the resolve wrapper can simply be discarded and replaced by an empty object.&lt;/p&gt;

&lt;h3&gt;
  
  
  Well-defined…
&lt;/h3&gt;

&lt;p&gt;Because the functionality of the package is based solely on the GraphQL types of your implementation, the exposed API of the service is both explicit and consistent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.solutotlv.com%2Fwp-content%2Fuploads%2F2018%2F01%2FCapture.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.solutotlv.com%2Fwp-content%2Fuploads%2F2018%2F01%2FCapture.png" alt="A description of the types generated by graphql-to-mongodb from the examined code sample, as viewed in graphiQL"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;Because of course there's mutation functionality...&lt;/center&gt;

&lt;p&gt;Only the fields defined in the original GraphQL type (to the far left above) are exposed as arguments in the schema fields. Likewise, generated input and insert types provided as an additional functionality of the package are derived directly from your original type and grant mutation capabilities on its fields to your API.&lt;/p&gt;

&lt;h3&gt;
  
  
  ...and safe
&lt;/h3&gt;

&lt;p&gt;The explicit nature of the API provides it with a measure of security. GraphQL provides out-of-the-box input validation, ensuring that all arguments match the types defined by your schema. With every one of your fields being unambiguously defined and consistently processed, would-be attackers are left with no wiggle room to exploit, or human errors to target.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give it a try
&lt;/h2&gt;

&lt;p&gt;Next time you’re designing a new service, or just considering an overhaul of an existing one, reflect on the benefits and principles of the package. If you want to give your NodeJs GraphQL service a whole lot of the power of the MongoDb database you have standing behind it with very little hassle, mayhaps you’ll consider adding &lt;a href="https://www.npmjs.com/package/graphql-to-mongodb" rel="noopener noreferrer"&gt;graphql-to-mongodb&lt;/a&gt; to your implementation.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.solutotlv.com/graphql-to-mongodb-or-how-i-learned-to-stop-worrying-and-love-generated-query-apis/?utm_source=devto" rel="noopener noreferrer"&gt;blog.solutotlv.com&lt;/a&gt; on February 5, 2018.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>mongodb</category>
      <category>server</category>
      <category>node</category>
    </item>
  </channel>
</rss>
