<?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: Johannes Pfeiffer</title>
    <description>The latest articles on DEV Community by Johannes Pfeiffer (@johannespfeiffer).</description>
    <link>https://dev.to/johannespfeiffer</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%2F451219%2Fd6edacd8-9542-431d-a36f-cf7533bbd80c.jpeg</url>
      <title>DEV Community: Johannes Pfeiffer</title>
      <link>https://dev.to/johannespfeiffer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/johannespfeiffer"/>
    <language>en</language>
    <item>
      <title>Machine Learning: A classification problem in FinTech with Node.js and TensorFlow</title>
      <dc:creator>Johannes Pfeiffer</dc:creator>
      <pubDate>Mon, 24 Aug 2020 13:35:46 +0000</pubDate>
      <link>https://dev.to/kontist/machine-learning-a-classification-problem-in-fintech-with-node-js-and-tensorflow-1jg9</link>
      <guid>https://dev.to/kontist/machine-learning-a-classification-problem-in-fintech-with-node-js-and-tensorflow-1jg9</guid>
      <description>&lt;h1&gt;
  
  
  Machine Learning
&lt;/h1&gt;

&lt;p&gt;Solving a classification problem in FinTech with Node.js and TensorFlow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;At &lt;a href="https://kontist.com"&gt;Kontist&lt;/a&gt; we provide a banking app for freelancers. The user can select a category for each of their transactions. For example, ”Business expense,“ “Private,” ”Business income,“ ”Tax pay,“ and more. Based on this selection, we then do tax calculations for the freelancer to support his or her savings.&lt;/p&gt;

&lt;p&gt;In the current user interface flow, the user selects a category from a list every time a new transaction comes in. To improve the user experience, we would like to automate the category selection. The naïve approach is to create manual rules like, “If the sender of a transaction was used in a transaction before, then just use the same category.” Obviously, this has some shortcomings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Imagine the sender “Amazon.“ One transaction could be “Private“, but the next one could be a ”Business expense“ and our approach would fail.&lt;/li&gt;
&lt;li&gt;How should we categorize transactions from new and unknown senders?&lt;/li&gt;
&lt;li&gt;You could refine the rules by including more data. For example, we could not only look at the sender but also at the transaction amounts. But adding more manual rules to improve the accuracy would make the code complex and unwieldy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So instead the approach we took was to create a machine learning solution. First, we build a model and trained it with some existing transactions for which the category was known. Then we uses that model to make predictions about upcoming transactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to classification
&lt;/h2&gt;

&lt;p&gt;Classification is a task that assigns a label to some data based on what was learned from previous data. In our case, the labels are categories (“Business expense,” ”Private,” ”Business income,“ ”Tax pay,“ et cetera) and the data are the transactions.&lt;/p&gt;

&lt;p&gt;In general, the process looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define our model.&lt;/li&gt;
&lt;li&gt;Train the model with known data.&lt;/li&gt;
&lt;li&gt;Use the model to make predictions.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Choose the right inputs
&lt;/h3&gt;

&lt;p&gt;Not all properties of a transaction help us with the classification. For example, it is obvious that some random UUID properties cannot help the model in making predictions. In fact, we found that only a couple of properties have any influence on the prediction at all. These properties, that we do use as an input for the training and prediction, are called “input features.” On the other hand, the categories are called the “output labels.“&lt;/p&gt;

&lt;h3&gt;
  
  
  Layers and Neurons
&lt;/h3&gt;

&lt;p&gt;/neurons.png (Simplified overview)&lt;br&gt;
Looking at this image we can see that each input feature corresponds to one neuron at the left, and each output label corresponds to one neuron at the right.&lt;/p&gt;

&lt;p&gt;In between we have several neurons organized in multiple hidden layers. Neurons are connected from one layer to the next, each connection having a specific and custom weight. You could say the values (also called probabilities) of the output labels are just a sum of the neuron values multiplied by their weights. Put simply, training the model is a process of finding the correct weights for all connections between the neurons. &lt;/p&gt;

&lt;p&gt;/neurons-weights.png (Sample weights; 62% of input data is predicted to be in the business expense category.)&lt;/p&gt;
&lt;h2&gt;
  
  
  Our setup
&lt;/h2&gt;

&lt;p&gt;The backend is a Node.js and TypeScript environment. The transaction data comes from various sources, but we can access all of it via a PostgreSQL database.&lt;/p&gt;

&lt;p&gt;Luckily, there is already a JavaScript binding for TensorFlow (called TensorFlow.js).&lt;/p&gt;

&lt;p&gt;So, we can define a sequential model as described above. It consists of four layers. The first is the input layer, where we enter our features. This is implicitly added to the model. In addition, we have two hidden layers and a layer for the output labels.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;tf&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@tensorflow/tfjs-node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputFeaturesCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;categoriesCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sequential&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;units&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;inputFeaturesCount&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;categoriesCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;tf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dense&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;units&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;inputShape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;inputFeaturesCount&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;activation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;relu&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dense&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;units&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;tf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dense&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;units&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;categoriesCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;activation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;softmax&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;optimizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;adam&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;loss&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;categoricalCrossentropy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accuracy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Normalize everything
&lt;/h3&gt;

&lt;p&gt;Before we can start to train our model, it is time to normalize the data; the input features must be numerical values.&lt;/p&gt;

&lt;p&gt;For example, take the date of the booking, "2019-05-28 22:12." With the help of the &lt;a href="https://momentjs.com/"&gt;moment.js&lt;/a&gt; library, this can be extracted into three input features:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const dayOfMonth = +moment(bookingDate).format("D");
const dayOfWeek = +moment(bookingDate).format("d");
const hour = +moment(bookingDate).format("H");
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To avoid complications, we want all the values to be normalized between 0 and 1. Therefore, we divide all the values by their maximum value, adding an extra step.&lt;/p&gt;

&lt;p&gt;Another part of the preparation for training is to evenly distribute the data. In our case, we have a lot more training data that is marked as "Business Purpose" than "Private." TensorFlow offers a nice way to handle that; it allows the user to set a class weight for each label corresponding to the distribution in the training data set. Note that  these class weights are not to be confused with the actual weights of the connections between the neurons.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does the crowd say?
&lt;/h3&gt;

&lt;p&gt;Turns out that we have good input features which do not directly come from the transaction itself. We can have a look how the user in question or other users categorized transactions with the same IBAN in the past. This might give a strong indication of how to predict future transactions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Training
&lt;/h3&gt;

&lt;p&gt;Time to train our model. We take our training data, shuffle it, and split it into two parts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The actual training data (80%)&lt;/li&gt;
&lt;li&gt;Some validation data (20%)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;First, TensorFlow uses the training data to try to find good weight values for the connections between the neurons. Training is a process of determining weight values so that the sum of the neuron values multiplied by their weights of connections will create good output label values.&lt;/p&gt;

&lt;p&gt;The validation data will then be used to check if the training worked. We cannot use the training data to verify this; it would of course return perfect results since we just used it to create this model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputFeatureTensor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;labelTensor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="na"&gt;validationSplit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;callbacks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;tf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;callbacks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;earlyStopping&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;val_loss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;min&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;How does TensorFlow find these values? It iteratively applies a function to adjust the weights so that the discrepancy between the label results and the expected results is minimized. If the discrepancy is below a given threshold, training is complete.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making predictions
&lt;/h3&gt;

&lt;p&gt;We now have a model and can start making predictions. Our income data must be in the same format as our training data, meaning we must apply the same normalization.&lt;/p&gt;

&lt;p&gt;All that is left to do is call &lt;code&gt;model.predict&lt;/code&gt; which will return a list of the probabilities that the input matches each category. The one with the highest probability will be selected as the category of the transaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learnings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Native Binary
&lt;/h3&gt;

&lt;p&gt;Internally, TensorFlow is a binary that runs completely separately from Node.js providing bindings for it. The following sections explain two resulting considerations.&lt;/p&gt;

&lt;h4&gt;
  
  
  Dispose variables
&lt;/h4&gt;

&lt;p&gt;TensorFlow doesn't automatically cleanup memory after model operations like &lt;code&gt;model.fit&lt;/code&gt;, &lt;code&gt;model.predict&lt;/code&gt; etc. Therefore, we have to wrap these operations in &lt;code&gt;tf.engine()&lt;/code&gt; scope calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;tf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;startScope&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="nx"&gt;tf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;endScope&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;tf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;disposeVariables&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Running in parallel
&lt;/h4&gt;

&lt;p&gt;We do have multiple workers or processes. If they are interacting with the same TensorFlow.js instance it creates a complication. Possible solutions are to run the processes in sequence, block concurrent access, or separate them into their own instances.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitation of tools
&lt;/h3&gt;

&lt;p&gt;A lot of tools to optimize and research the models are written in Python instead of JavaScript. For example, one cannot just use "Tensorboard" to watch how your models behave. For further optimization of our machine learning code, we plan to investigate the further integration of external tools.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>machinelearning</category>
      <category>node</category>
    </item>
    <item>
      <title>Implementing GraphQL in an existing code base</title>
      <dc:creator>Johannes Pfeiffer</dc:creator>
      <pubDate>Mon, 24 Aug 2020 13:35:27 +0000</pubDate>
      <link>https://dev.to/kontist/implementing-graphql-in-an-existing-code-base-3mf3</link>
      <guid>https://dev.to/kontist/implementing-graphql-in-an-existing-code-base-3mf3</guid>
      <description>&lt;h1&gt;
  
  
  Implementing GraphQL in an existing code base
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;At &lt;a href="https://kontist.com" rel="noopener noreferrer"&gt;Kontist&lt;/a&gt;, we provide a mobile banking app for freelancers. As our app grew in popularity, more and more of our external partners wanted to integrate with our services. To add to our offering, we developed a browser-based web application.  Soon, our customers wanted an API as well. However, our clients had different requests for an API. Each client had its own idea about what data should be returned and how.  Previously, we only had a custom-tailored REST-API for our own mobile application, hardly able to fulfill all those needs.&lt;/p&gt;

&lt;p&gt;One problem was that the API endpoints did not return the data that was required by the client. Either it was not available at all or the client was required to do a lot of follow-up requests to find a simple value (also called underfetching). On the other extreme, it returned too much data. Much of this data was never used by the client at all, having a negative impact on the performance (so called overfetching). Both underfetching and overfetching symptoms are common problems for traditional REST-APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does GraphQL
&lt;/h2&gt;

&lt;p&gt;Several options exist to overcome these overfetching or underfetching. One popular solution is called GraphQL.&lt;/p&gt;

&lt;p&gt;The main idea of GraphQL is that &lt;em&gt;clients define the data structure.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Instead of enforcing a server defined data structure with multiple endpoints, each client can define what data it needs only via one endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example REST vs. GraphQL
&lt;/h3&gt;

&lt;p&gt;Let's say we want to pull up an overview of the last three account transactions.  In our UI, we only need the description and the amount of the transactions.&lt;/p&gt;

&lt;p&gt;In the REST-API we would need (at least) two requests:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get information about the accounts to which the user has access, then filter the response for the primary account.&lt;/li&gt;
&lt;li&gt;2.   Using the ID of the main account, pull all transaction data for this account. Then, drop all transactions except the three transactions we want to show. Drop all other data associated with those three transactions besides description and amount (e.g. we never show the booking date, the sender, IBAN, et cetera).&lt;/li&gt;
&lt;/ol&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Foi21mcor927bt45ocrm3.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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Foi21mcor927bt45ocrm3.png" alt="Traditional approach with REST-API"&gt;&lt;/a&gt;&lt;br&gt;
(Traditional approach with REST-API)&lt;/p&gt;

&lt;p&gt;In contrast, GraphQL could fetch this transaction data like this:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fj63jtvjjxch2ajr6hy7d.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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fj63jtvjjxch2ajr6hy7d.png" alt="Approach with GraphQL-API"&gt;&lt;/a&gt;&lt;br&gt;
(Approach with GraphQL-API)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Send one request with response structure defined in the body:
&lt;/li&gt;
&lt;/ol&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;viewer&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;mainAccount&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;transactions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&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;edges&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;node&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;description&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;amount&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the body we use the GraphQL query language; it defines how the response should look. The available fields and their types are defined by the server up front. This allows mistakes in the client code to be detected before runtime of the application.&lt;/p&gt;

&lt;p&gt;This approach clearly has some benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoid over- and underfetching&lt;/li&gt;
&lt;li&gt;Performance improvements because of
** less usage of network bandwidth
** no need to fetch unused data on the server
** no need to clear client data or pollute client memory &lt;/li&gt;
&lt;li&gt;  Integrated validation and type-checking&lt;/li&gt;
&lt;li&gt;  Documentation can be auto generated&lt;/li&gt;
&lt;li&gt;  Easier evolution of the API (without versioning)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;
&lt;h3&gt;
  
  
  API First
&lt;/h3&gt;

&lt;p&gt;Introducing the GraphQL-API was more complex than just adding a small component to our code base; we started using a different development model, called "API First.” Previously, we only added new features to our mobile application then adjusted the backend for it. Now, the feature will be used by different clients at launch (web application, mobile application, our external partners and users). API First means we carefully design the model in our API, which then can be used by our own and all other clients.&lt;/p&gt;
&lt;h3&gt;
  
  
  Reusing code
&lt;/h3&gt;

&lt;p&gt;When we started the GraphQL-API we already had a well-functioning system. It did not make sense to start from scratch; this would have cost resources and introduced new bugs. Our idea was to reuse the existing models where it was possible. We did this by introducing decorators (also called annotations) to the code of the models to expose certain properties via GraphQL. Alongside GraphQL, we introduced a new ORM version which allowed us to use decorators. Our models are now mostly clean POJOs with some decorators added.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DataType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Column&lt;/span&gt;
    &lt;span class="nx"&gt;iban&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we see a small excerpt of our account model class with the properties &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;iban&lt;/code&gt;. The &lt;code&gt;@Column&lt;/code&gt; decorator comes from our ORM and the &lt;code&gt;@Field&lt;/code&gt; decorator is used to expose this property in GraphQL. This example shows, that we do not expose the internal database ID property via GraphQL.&lt;/p&gt;

&lt;p&gt;Beside these decorators, we have &lt;code&gt;resolver&lt;/code&gt; classes which find the correct entities for a given field in the GraphQL-tree.&lt;/p&gt;

&lt;p&gt;All of this allowed us to introduce GraphQL very quickly without much overhead or boilerplate code. Instead, we could spend the time on more advanced topics like API-design, paging, sorting, and filtering. There was already a common standard for paging called connection pattern. We implemented our own solution for sorting and filtering which is now published as an open source library called &lt;a href="https://github.com/kontist/type-graphql-filter" rel="noopener noreferrer"&gt;type-graphql-filter&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common problems
&lt;/h2&gt;

&lt;p&gt;When dealing with GraphQL common problems are performance and security. These become a concern simply because you can do so much in just a single call. We implemented the following measures to mitigate these risks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Flat structure: We do not allow recursive structures in our queries. The hierarchy for a transaction is &lt;code&gt;user &amp;gt; account &amp;gt; transaction&lt;/code&gt;, but then this transaction entity has no property leading back to its account or user.&lt;/li&gt;
&lt;li&gt;Implementation of the data loaders standard: For performance intense properties, we cache the database result during the same request.&lt;/li&gt;
&lt;li&gt;Hard limit: To avoid misusage of our API, we implemented limits on request and response sizes as well as on the number of items that we return in one response.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The feedback from both our internal and external users of GraphQL-API was very positive. We have the ability to quickly develop new models. Currently, we are migrating all remaining REST-endpoints to GraphQL. Then we will shutdown the old legacy API.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>typescript</category>
      <category>decorators</category>
    </item>
  </channel>
</rss>
