<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[NSoft - Medium]]></title>
        <description><![CDATA[Engaging stories about software development from the brilliant team of people at NSoft. - Medium]]></description>
        <link>https://medium.com/nsoft?source=rss----c3fc05ce3056---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>NSoft - Medium</title>
            <link>https://medium.com/nsoft?source=rss----c3fc05ce3056---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 24 May 2026 02:28:57 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/nsoft" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Provably Fair — How do I apply it?]]></title>
            <link>https://medium.com/nsoft/provably-fair-how-do-i-apply-it-518c21f57ff1?source=rss----c3fc05ce3056---4</link>
            <guid isPermaLink="false">https://medium.com/p/518c21f57ff1</guid>
            <category><![CDATA[cryptography]]></category>
            <category><![CDATA[node]]></category>
            <category><![CDATA[provably-fair]]></category>
            <dc:creator><![CDATA[Jadranko Dragoje]]></dc:creator>
            <pubDate>Wed, 18 Jan 2023 13:24:55 GMT</pubDate>
            <atom:updated>2023-01-18T13:24:55.295Z</atom:updated>
            <content:encoded><![CDATA[<h3>Provably Fair — How do I apply it?</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IDRO4ZIwzncpN9JOplJoQA.png" /></figure><p>There has been a lot of discussion about the fairness of pseudo-random number generator (PRNG) algorithms in gambling and about fairness in general. While we are confident in the fairness of our algorithms, some providers have gone a step further by providing a way for players to verify their fairness using simple cryptographic calculations. How does this process work?</p><p>Provably Fair uses a simple process where the server announces a hashed seed for the upcoming round, which is publicly available to all players. The server also assigns a default player seed that the player can choose to change at any time before the round starts. The player creates a bet, and before the round is generated, the server combines the server seed and the player seed to create a game hash that contains the result. After the round is resolved, the server reveals the unhashed server seed, which is used to obtain the result from the game hash.</p><p>The same process is used for round-based games in which multiple players are playing the same round. In this case, the player seeds are typically taken from the first three players who make a bet. If there are less than three players in the round, their seeds are used instead.</p><p>Here is the example from one crash cash game:</p><ul><li>Server announces hashed server seed for the next round:<br><em>64a436bfafb564dc9831530c975c73ca5d43cb29a34fc3bc93e0b1dd4d69274e</em></li><li>Player sets seed to some value:<br><em>x8QBX5DPsyIHf066TRht</em></li><li>Player creates a bet and starts round</li><li>Algorithm uses combined server and player seed to generate result and announces game hash at the end of a round:<br><em>50aa38ce0e390e4f7ece4138878e46132e99fbe3887fca2a6c3f6c43ca2dc5611a15e5dc5de7b9ea9db79c9bfc48763e8d6df138ff24c377f4704c23d41bf911</em></li><li>Player can use online tool to check game hash and view result (in this case result is decimal number):<br><em>11.97</em></li></ul><h3>Generating server seed</h3><p>To generate server seed we need to use SHA256 algorithm.</p><pre>const crypto = require(&#39;crypto&#39;);<br><br>function generateServerSeed() {<br>  const seed = crypto.randomBytes(32).toString(&#39;hex&#39;);<br>  const seedHashRaw = crypto.createHash(&#39;sha256&#39;).update(seed);<br>  const seedHash = seedHashRaw.digest(&#39;hex&#39;);<br><br>  return {<br>    seed,<br>    seedHash,<br>  };<br>}</pre><p>Server seed hash needs to be publicly available, usually in game rules or special provably fair section of the game settings. It can also be stored on blockchain. Do not show server seed, only server seed hash. New server seed is generated before each round and announced to players.</p><h3>Generating result</h3><p>To generate result we need to use server seed combined with player seed that player provided during bet placement. For example, if we wish to generate result for Roulette game we can use this example:</p><pre>function generateResult(serverSeed, playerSeed) {<br>  const gameSeed = `${serverSeed}${playerSeed}`;<br>  const gameHash = crypto.createHash(&#39;sha512&#39;).update(gameSeed).digest(&#39;hex&#39;);<br>  const gameHashHmac = crypto.createHmac(&#39;sha256&#39;, gameHash).update(gameSeed).digest(&#39;hex&#39;);<br><br>  const resultNumber = parseInt(gameHashHmac.substring(0, 13), 16);<br>  const result = resultNumber % 37;<br><br>  return {<br>    result,<br>    serverSeed,<br>    playerSeed,<br>    gameHash,<br>    gameHashHmac,<br>  };<br>}</pre><p>The generation of the result in Roulette is determined by a game logic. To generate a random number between 0 and 36, we use the modulo operator. The result number, which is used to determine the outcome of the game, is derived from a game hash created using a combination of the server seed and the player seed, and is then converted to an integer.</p><p>For other games, like crash games, we would use different formula to get result. Here is the example:</p><pre>function generateResult(serverSeed, playerSeed) {<br>  const gameHashSeed = `${serverSeed}${playerSeed}`;<br>  const gameHash = crypto.createHash(&#39;sha512&#39;).update(gameHashSeed).digest(&#39;hex&#39;);<br>  const gameHashHmac = crypto.createHmac(&#39;sha256&#39;, gameHash).update(gameHashSeed).digest(&#39;hex&#39;);<br>  <br>  const resultMaximum = 2 ** 52;<br>  const resultNumber = parseInt(gameHashHmac.substring(0, 13), 16);<br>  const resultRandom = resultNumber % 33 === 0 ? 100 : ((100 * resultMaximum) - resultNumber) / (resultMaximum - resultNumber);<br>  const result = Math.floor(resultRandom) / 100;<br><br>  return {<br>    result,<br>    serverSeed,<br>    playerSeed,<br>    gameHash,<br>    gameHashHmac,<br>  };<br>}</pre><p>In this example, the result is a decimal number, unlike in Roulette where the result is an integer.</p><h3>Validating</h3><p>Upon the completion of a round, the player receives the game hash. This hash can be used to verify the validity of the seed that was made public before the start of the round. To confirm the game hash, a validation function can be employed:</p><pre>function isValidHash(serverSeed, playerSeed, gameHash) {<br>  const gameHashSeed = `${serverSeed}${playerSeed}`;<br>  const gameHashCheck = crypto.createHash(&#39;sha512&#39;).update(gameHashSeed).digest(&#39;hex&#39;);<br><br>  return gameHash === gameHashCheck;<br>}</pre><p>A player can also utilise the same process to determine the result from the game hash, thereby validating the entire process. It is uncertain whether game providers make public the logic for determining the result from the game hash. From my research, I have not been able to locate this information on official websites. Perhaps this could be something that could be added to each game, allowing players to independently verify this part of the process also.</p><h3>Conclusion</h3><p>The implementation of a provably fair algorithm is certainly noteworthy from a technical and certification standpoint, however, I am uncertain about its actual utilisation by players. This approach requires a certain level of technical understanding which the typical player may lack. Furthermore, for games like slots, the algorithm can be even more complicated as the outcome of a spin is not a singular number but rather a matrix of numbers determining the positioning of symbols on the reels. Even if a player is able to use the formula to confirm the result from the game hash, they would still need to check its validity against the paytable of the slot game, which can be quite complex.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=518c21f57ff1" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nsoft/provably-fair-how-do-i-apply-it-518c21f57ff1">Provably Fair — How do I apply it?</a> was originally published in <a href="https://medium.com/nsoft">NSoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Short Recap]]></title>
            <link>https://medium.com/nsoft/the-short-recap-20aed14a6e8d?source=rss----c3fc05ce3056---4</link>
            <guid isPermaLink="false">https://medium.com/p/20aed14a6e8d</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[games]]></category>
            <category><![CDATA[it-industry]]></category>
            <category><![CDATA[lottery]]></category>
            <category><![CDATA[casino]]></category>
            <dc:creator><![CDATA[Jadranko Dragoje]]></dc:creator>
            <pubDate>Wed, 18 Jan 2023 13:24:40 GMT</pubDate>
            <atom:updated>2023-01-18T13:24:40.811Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0ai2Wt41k6w1hUKqcRXcVw.png" /></figure><p>The IT industry has been hit hard by the looming recession, with many companies being forced to freeze hiring or lay off employees in order to stay afloat. This is a problem that has been building for some time, as the industry has been plagued by over-hiring in recent years.</p><p>Prior to the pandemic and especially after the first wave of the alpha variant, the IT industry was booming. Companies were scrambling to hire as many workers as possible in order to keep up with the rapidly-evolving technology landscape. This led to a glut of IT workers, with many companies offering high salaries and generous benefits in order to attract the best and brightest, and also all the others.</p><p>However, the recession has brought the IT industry to a grinding halt. With businesses forced to shut down or operate at reduced capacity, the demand for IT services has plummeted. This has left many companies, especially the Big Tech, with a surplus of employees, forcing them to make difficult decisions about their staffing levels.</p><p>One of the biggest problems facing the IT industry is the abundance of inexperienced workers. Many companies were so eager to hire during the boom that they were willing to take on workers with little or no experience in the field. This has left these companies with a workforce that is ill-equipped to handle the challenges of the current economic climate.</p><p>In order to weather the storm, many IT companies are being forced to freeze hiring or lay off employees. This is a painful decision, but one that many companies feel is necessary in order to stay afloat. It remains to be seen how the industry will recover, but it is clear that difficult times lie ahead.</p><p>One shining bright light is the final breakthrough of AI services. The field of artificial intelligence has made great strides in recent years, and two new developments in particular are worth noting. The first is ChatGPT, a state-of-the-art language model that is capable of generating human-like responses to text-based queries. The second is Midjourney, a tool that uses machine learning to generate amazing images. Overall, the breakthroughs of ChatGPT and Midjourney are exciting developments in the field of AI, and have the potential to greatly improve a wide range of industries. It will be interesting to see how these tools are used in the future, and what other innovations the field has in store.</p><p>Fortunately, NSoft has been able to avoid bloat in its workforce, mostly because our middle management wished to maintain lean and efficient development teams. Our Games department has started the year with 39 employees and ended with 44. We kept our focus on improving stability of our virtual games, as well as adding additional features to our lottery and casino products. We are currently maintaining and developing over 30 products, from round based draw games like Lucky Six to casino slots like Temple of Horus.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UoqekFo7MBCRKITSgpidyg.png" /><figcaption>Schema of <strong>Games</strong> department</figcaption></figure><p>The coming year will be a challenging one for us, as we will need to not only add new features to our existing products, but also research the potential use of AI in our products. This will require a lot of hard work and dedication, but we are confident that we are up to the task. We are excited about the opportunities that AI presents, and are looking forward to exploring its potential in our products.</p><p>In addition to AI, there are many other new features being developed for casino games. These include advanced graphics and animations, new game mechanics, and innovative bonus features. All of these innovations are designed to enhance the gameplay experience and make casino games even more enjoyable for players.</p><p>Overall, the world of casino game development is constantly evolving, with new and exciting features being developed all the time. This is great news for players, who can look forward to an ever-improving gaming experience as the industry continues to innovate.</p><p>Note: This blog post has been generated with help of the <a href="https://chat.openai.com/chat">ChatGPT</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=20aed14a6e8d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nsoft/the-short-recap-20aed14a6e8d">The Short Recap</a> was originally published in <a href="https://medium.com/nsoft">NSoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building and running a Node.JS, TypeScript, PostgreSQL app with Docker]]></title>
            <link>https://medium.com/nsoft/building-and-running-nodejs-typescript-postgresql-application-with-docker-3878240a2f73?source=rss----c3fc05ce3056---4</link>
            <guid isPermaLink="false">https://medium.com/p/3878240a2f73</guid>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[nodejs]]></category>
            <dc:creator><![CDATA[Haris Zujo]]></dc:creator>
            <pubDate>Thu, 03 Sep 2020 08:20:27 GMT</pubDate>
            <atom:updated>2020-09-03T08:20:27.803Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nSuwUaZxQyBsN3-eUwudSg.png" /></figure><p>We see more and more companies using JS as their tech stack for both backend and frontend. It makes sense because the development cycle reduces in time, cost, and increases in efficiency, and gives a unique stack for different parts of the system.</p><p>Today I’ll show you how you to build a small web app using Node easily. JS enhanced with TypeScript, PostgreSQL as our database, and how to dockerize it.</p><p>This article starts with the basic setup of our Node.JS app, Express server, and PostgreSQL configuration. The Docker part is described at the end of the story, so if you want to see the Docker setup for this app only, scroll to the bottom.</p><h4>Creating a Node.JS application</h4><p>I assume you have installed Node.JS before. Creating every Node.JS application starts with a simple command npm init If you add -y , it will fill all input for you and create a <strong>package.json</strong> file necessary for managing our application dependencies required for it to run.</p><p>Since we said we’re going to use TypeScript, we have to set up our project to work with TypeScript right away.</p><p>Next, we’ll install TypeScript and tslint in our project by typing the following:</p><pre>npm install typescript tslint --save-dev</pre><p>This command will install TypeScript in our dev dependencies. After we have installed TypeScript, we’ll edit our <strong>package.json file</strong> and add tsc command for accessing TypeScript commands. We’ll be using this command for starting and bundling our application.</p><p>After we’ve installed these packages, we’ll run the following command to initialize our <strong>tsconfig.json </strong>file where compiler options for our project are stored.</p><pre>tsc --init</pre><p>Since we’ll be using Express, it’s important to install a package that helps TypeScript identify express types by typing:</p><pre>npm i @types/express @types/node --save-dev<br></pre><p><strong>This way, TypeScript will be able to recognize Express classes and global Node types. </strong>For example, after you install the types package, you’ll be able to import Request and Response types from Express directly.</p><p>If we initialize <strong>tsconfig.json</strong> with the first command, we’ll get a file like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KozYc-n0qiNS5eM273B3zw.png" /></figure><p>This is our <strong>tsconfig.json</strong> file with plenty of compiler options you can customize.</p><p>We’ll replace it with the following:</p><pre>{<br> &quot;compilerOptions&quot;: {<br> &quot;module&quot;: &quot;commonjs&quot;,<br> &quot;esModuleInterop&quot;: true,<br> &quot;target&quot;: &quot;es6&quot;,<br> &quot;moduleResolution&quot;: &quot;node&quot;,<br> &quot;sourceMap&quot;: true,<br> &quot;outDir&quot;: &quot;dist/src&quot;<br>},</pre><pre> &quot;lib&quot;: [&quot;es2015&quot;]<br>}</pre><p>We are mostly interested in our outDir property where we specify the output directory for our <strong>transpiled</strong> TS code into JS.</p><h4>Creating and starting our server using TypeScript</h4><p>It is always a good idea to separate your scripts for defining your server and start your application using that server configuration.</p><p>For now, our project structure looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/273/1*1-lZgQRMsTv6BYOD8z3HWQ.png" /></figure><p>I’ve created dbconfig directory with database configuration that we’ll use when initializing thePostgres connection.</p><p>In the <strong>server.ts</strong> file lies our server configuration looking like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/fbdbb891f03c4e57f6eb3ae085faaa3c/href">https://medium.com/media/fbdbb891f03c4e57f6eb3ae085faaa3c/href</a></iframe><p>We’ll explain the configuration as we go. Firstly, we initialize our Express application, move to server specific configurations such as configuring<strong> </strong><a href="https://www.npmjs.com/package/body-parser"><strong>body-parser</strong></a> to handle our incoming data. In the end, there is a public method we’ll use to start our server in our <strong>app.ts</strong> file. We also set up our routing to use separated router configuration for getting all todos.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4ad4e0ec2f175444308fc4a7f42c4e61/href">https://medium.com/media/4ad4e0ec2f175444308fc4a7f42c4e61/href</a></iframe><p>Here we call our “start” method and log in case of success or failure.</p><h4>Postgres configuration</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/62144a0f222dfd80a182b47edbd4ed42/href">https://medium.com/media/62144a0f222dfd80a182b47edbd4ed42/href</a></iframe><p>Pooling is a process of creating connections and caching them to reuse them. There is no resource waste as it would be if you create a new connection every time.</p><p>Below is the explanation of the mentioned configuration properties :</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/891/1*oVmGxjejFgUNx73ll8WxHg.png" /></figure><p>I am using <a href="https://www.pgadmin.org/"><strong>pgAdmin4</strong></a> for accessing my database cluster and managing my databases.</p><h4>TodosController</h4><p>We’ll create a TodosController and route for fetching all todos from the database.</p><p>A controller would look like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/50e8dd613bd97e0e7560f64a929ac12b/href">https://medium.com/media/50e8dd613bd97e0e7560f64a929ac12b/href</a></iframe><ul><li>Importing pool from our database configuration class</li><li>Initializing new connection and creating an instance of pg client</li><li>Sending our raw query to our client’s <strong>query </strong>method — async</li><li><strong>Releasing our connection —THIS IS IMPORTANT</strong>!</li><li>Returning fetched data</li></ul><p>Always release your connection when using the pool. That way, the client will return to the pool of available connections.</p><h4>Todos router</h4><p>We’ll set up our router in a separate file stored in <strong>src/routers</strong> and give it a route to the <strong>GET</strong> method defined upper in our TodosController.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/18a24e8486303bdee95c5dd33df55c44/href">https://medium.com/media/18a24e8486303bdee95c5dd33df55c44/href</a></iframe><p>Now, all we have to do is register this router in our <strong>server.ts</strong> where we set up our server.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/839/1*E2LxBX_LlLCJsCQpd5OU1Q.png" /></figure><p>We import our todos router and we say to our Express server that anytime someone hits “/todos” in the URL, pass the instance <strong>todosRouter </strong>to handle all the requests with that route.</p><h3>Project structure</h3><p>So far, we have the following project’s structure:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/472/1*Lu0zzk04xoCI9WUboP6QKg.png" /></figure><h3><strong>Building our application</strong></h3><p>Now that we have all set up, we can test our endpoint for fetching all todos from the database.</p><p>First, we’ll run the following command from our terminal</p><pre>npm run build</pre><p>Using this command, we will look up our <strong>tsconfig.json</strong> and see the output dir we specified so that it knows the output directory for our transpiled files.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/435/1*RUpjnMYvVbc3N-3-Ped1pQ.png" /></figure><p>Make sure your main<strong> </strong>in the <strong>package.json</strong> points to the same directory as the <strong>output dir</strong> you specified for the TypeScript since it will be our main point of entry for our application.</p><pre>&quot;main&quot;: &quot;dist/src/app.js&quot;,</pre><p>Our scripts section in <strong>package.json</strong> should be like this:</p><pre>&quot;scripts&quot;: {</pre><pre>&quot;build&quot;: &quot;tsc&quot;,</pre><pre>&quot;start&quot;: &quot;tsc &amp;&amp; node dist/app.js&quot;,</pre><pre>&quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;&amp; exit 1&quot;</pre><pre>},</pre><p>Now, if you check your directories tree, you’ll see a new directory <strong>dist </strong>just as we specified in the <strong>tsconfig.json.</strong></p><h3>Testing our application</h3><p>To start your application type and check your terminal:</p><pre>npm start</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/943/1*YQIcDdzCJD8o48BRgPLqjQ.png" /></figure><p>We have it up and running, and since we previously set up our Express server, database configuration, and router, let’s test our endpoint for todos.</p><p>Now, if we go to the browser and visit:</p><pre><a href="http://localhost:4000/todos">http://localhost:4000/todos</a></pre><p>we should get a result from our database</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/689/1*d29dM0nJKw6EmZzkT27JjA.png" /></figure><p>Such a piece of good advice!</p><h3>Dockerizing our application</h3><p>Since containers are so popular and almost a must when you’re building, testing, and deploying your applications, we’re gonna make our application run inside a Docker container.</p><p>To achieve that, we need to build an image from our application. Images are basically our packed applications containing everything for the application to run.</p><p>Containers are running instances of our images.</p><p>You can read more about containers <a href="https://www.docker.com/resources/what-container"><strong>here</strong></a>.</p><h3>Docker compose</h3><p>We’ll create a file called docker-compose.yml where we can setup configuration for all of the services our application will be using.</p><p>We need a service for our web application running in Node.JS and our database service, which is PostgreSQL.</p><p>We use docker-compose to run multiple containers for our application.</p><p>Our docker-compose file would look like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2f0f5758174cfd57f05b75e749464160/href">https://medium.com/media/2f0f5758174cfd57f05b75e749464160/href</a></iframe><p>If you take a look at our <strong>Postgres</strong> container in our docker-compose configuration, you can see that we are using a 10.4 Postgres image to build our container, expose the “5432” port on our local machine and map it to the container’s 5432 port. Meaning, if I want to access my Postgres instance running inside my container, I would use <strong>localhost:5432</strong> port along with the defined username and password.</p><p>We also run our <strong>pgAdmin on port 80</strong>,<strong> </strong>which is our database management service where we can access our Postgres databases, create clusters, and do all kinds of operations.</p><h4>Initializing our database schema on the container startup</h4><p>It would be great to initialize the database with schema, so we don’t have to manually create it.</p><pre>- ./src/migrations/dbinit.sql:/docker-entrypoint-initdb.d/dbinit.sql</pre><p>This piece of code in our docker-compose file will trigger our dbinit.sql file from our project and use it to execute whatever SQL we wrote inside of it.</p><h3>Dockerfile</h3><p>Our Dockerfile would look like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/99bb470fb1661d18e37a7b9f997d5e0c/href">https://medium.com/media/99bb470fb1661d18e37a7b9f997d5e0c/href</a></iframe><p>What this will do is create an image of our application with our virtual directories structure to hold everything that we already have on our machine.</p><p>We list which version of Node do we want for our image created our working directory, then copied everything from our src folder to our virtual one.</p><p>Here we used <strong>-alpine </strong>image. Alpine comes from Linux and represents a minified version of Linux that has enough resources to run your applications. It is beneficial when creating Docker images because the output size will be a lot smaller.</p><h3>The container might be ready but our database might not!!!</h3><blockquote>What is that part with that URL? It looks like we’re also downloading and running something when building our image, some kind of a waiting process.</blockquote><p>Yes, your container may be ready for communication, but the process you’re running inside of it may not. In this case, it’s our Postgres database. It takes a bit more time for it to start than our application. Hence, we are going to download a <strong>shell script that will delay our database connection call</strong> until it is ready to accept connections.</p><h4>Our final project structure</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/361/1*ipZwL67al7G75KrbxzL4uw.png" /></figure><h3>Running our application services with Docker</h3><p>Now when we have our Docker configurations all ready, we can set them up and running in a matter of seconds.<br>We can achieve this by typing the following command in the root of our project where our Docker configuration files are:</p><pre>docker-compose up --build -d</pre><p>The -d says to run the container in the <strong>background, </strong>and the — build builds the image before starting containers. It is important because you often want to have access to the command prompt while the container is running.</p><p>Now, we should have our app image built and containers up and running. To check your running containers, execute the following:</p><pre>docker ps</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/491/1*9WeM9Vi4m7J2YZd-reUbDA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/505/1*8iLM5VrvOLv__J407aVArg.png" /></figure><p>We see that our containers are up and running, and we can see on which port each and every one of them has been exposed and mapped.</p><blockquote>It would be nice to see what is going on inside our containers, some kind of logs or something.</blockquote><p>To see the output of your containers during their execution time, just execute:</p><pre>docker logs {container}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/757/1*fzNR91RllhrDyxvmpwX5eQ.png" /></figure><p>This is the output of our Node app — the same output as if we’d execute npm start. It’s the output of our application starting, but now running in the container.</p><h3>PgAdmin</h3><p>We also see that our db management is running on port 80. If we go to “localhost:8080”, we’ll get our database management login page where we can log into our system.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/586/1*IfIG4MKHTuwlxdqwD8Kusg.png" /></figure><p>We’ll use our credentials from the pgadmin container’s environment variables, and now we have access to our database server.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/396/1*_Om3FegW6vv72_-eXR03_w.png" /></figure><p>You see that we already have our <strong>todo-db</strong>, and the <strong>todos</strong> <strong>table</strong> created.</p><h3>Testing our /todos route</h3><p>Okay, we have our database services and application up and running inside docker containers, so now we can test our todos endpoint.</p><p>I’ll hit a simple CURL request towards our TodosController with the JSON formatting pipe.</p><pre>curl &#39;http://localhost:4000/todos&#39; | json_pp</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1021/1*btoB0EJkmYTieh23CXuekQ.png" /></figure><p>And there we have our data.</p><h3>Conclusion</h3><p>The point of using Docker for your development is that you can put together a bunch of cool services, set up a testing environment, work with different versions of tools and see how your app behaves and a lot of other stuff. Just edit your docker-compose file, put some new services, and play around.</p><p>Thank you for reading. I hope you learned something useful!</p><p><strong>GitHub repo: </strong><a href="https://github.com/CyberZujo/todo-app"><strong>https://github.com/CyberZujo/todo-app</strong></a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3878240a2f73" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nsoft/building-and-running-nodejs-typescript-postgresql-application-with-docker-3878240a2f73">Building and running a Node.JS, TypeScript, PostgreSQL app with Docker</a> was originally published in <a href="https://medium.com/nsoft">NSoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using AJV for schema validation with NodeJS]]></title>
            <link>https://medium.com/nsoft/using-ajv-for-schema-validation-with-nodejs-1dfef0a372f8?source=rss----c3fc05ce3056---4</link>
            <guid isPermaLink="false">https://medium.com/p/1dfef0a372f8</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[ajv]]></category>
            <category><![CDATA[json]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[nsoft]]></category>
            <dc:creator><![CDATA[Haris Zujo]]></dc:creator>
            <pubDate>Tue, 21 Jul 2020 08:06:56 GMT</pubDate>
            <atom:updated>2020-07-21T08:27:37.839Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/735/1*ersNOleZRpdgPmxZaoGspg.png" /></figure><p>If you’re a web developer, especially backend developer, who develops APIs, among the gazillion things you need to consider is validating user input and preventing security leaks from a possible malicious user. Validating requests that hit our API is simply a <strong>MUST </strong>if you want to have a secure web application.</p><p>Manually checking payload properties and validating them can be of good use when you’re dealing with a type of pre-defined data with few properties that won’t subdue any major changes. However, dealing with bigger objects and their properties can be painful and slow if you’re using standard manual methods of validation instead of something like AJV.</p><p>AJV stands for Another JSON Schema Validator and represents the fastest validator for JSON schemas around. You can easily find it on npm and in the official <a href="https://github.com/ajv-validator/ajv"><strong>github repo</strong></a>.</p><p>Let’s go through a simple example where we’ll see how to apply AJV within our NodeJS app.</p><p>Suppose you have an entity with the following structure (Typescript):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/277/1*dCWYuK7te6_VJJV-GKeReA.png" /></figure><p>As you can see, there are plenty of things to be validated here. I’ll ignore the fact that there are database-level constraints I’ve previously defined. Validating the user’s input before it reaches any service that communicates with the database is something you always want to do.</p><h3>Installing AJV</h3><p>This is an easy step since AJV is available as an npm package. We will just do the following:<br><strong>npm i ajv</strong></p><h3>Defining our JSON Schema</h3><p>I won’t go into the JSON Schema details. You can find more info <a href="https://json-schema.org/understanding-json-schema/index.html"><strong>here</strong></a><strong>. </strong>I’ll explain defined properties as we go.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8VshSYRrsqgegNiJA-EJBw.png" /></figure><p>Here we have defined our JSON schema with our properties and set some rules for each one of them.</p><p>There are plenty of other attributes you can find in the official documentation. We have set our rules for required properties, and each property has its own constraints defined, and that is what’s important.</p><p>Be cautious when defining rules here if your model will be persisted into the database and make sure rules here don’t break the rules defined on the database level.</p><h3><strong>Using ajv to validate against the defined schema</strong></h3><pre>import tutorSchema from &#39;./schema/tutor-schema.json&#39;;</pre><pre>import ajv, { ErrorObject } from &#39;ajv&#39;;</pre><p>We’ll import our ajv library and the ErrorObject for accessing validation error properties if you want to do some parsing, as well as our defined schema.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3e3bf5b94208901818c9edbb188b8393/href">https://medium.com/media/3e3bf5b94208901818c9edbb188b8393/href</a></iframe><ol><li>We’re initializing our ajv with the following options of <strong>allErrors</strong> and <strong>async</strong> for asynchronous usage.<br><strong>allErrors </strong>option is very important. If not provided, the program will end after the first validation first occurs.</li><li>The next step is to compile our schema using the <strong>compile</strong> method that will create a <strong>validate</strong> function for our schema. This is a faster way of validating by compiling our schema.</li><li>We use our validate method to validate against our model that represents our user input.</li><li>We check if there are some errors and can do some parsing</li></ol><p>Here I’ve created a small parser method for parsing our errors (this is just for testing purposes and my predefined properties, you should use some of the npm libraries for humanizing ajv errors).</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4f5b880bfe25a2cd144a720c03332a39/href">https://medium.com/media/4f5b880bfe25a2cd144a720c03332a39/href</a></iframe><p>If I send some invalid payload towards my API, I’ll get a response like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/59efb1193b75f4059fa83afdd8d7eedb/href">https://medium.com/media/59efb1193b75f4059fa83afdd8d7eedb/href</a></iframe><p>Ajv does not format our error messages. We can directly access <strong>ErrorObject</strong> and do some parsing instead, or use another library such as <strong>better-ajv-errors</strong> to humanize our error messages. In the end, it all should be readable to end users without any generic data.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1dfef0a372f8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nsoft/using-ajv-for-schema-validation-with-nodejs-1dfef0a372f8">Using AJV for schema validation with NodeJS</a> was originally published in <a href="https://medium.com/nsoft">NSoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating Serverless Workboard with Netlify Lambda — Part Two]]></title>
            <link>https://medium.com/nsoft/creating-serverless-workboard-with-netlify-lambda-part-two-f466a1affffc?source=rss----c3fc05ce3056---4</link>
            <guid isPermaLink="false">https://medium.com/p/f466a1affffc</guid>
            <category><![CDATA[api]]></category>
            <category><![CDATA[mongodb]]></category>
            <category><![CDATA[serverless]]></category>
            <category><![CDATA[netlify]]></category>
            <dc:creator><![CDATA[Jadranko Dragoje]]></dc:creator>
            <pubDate>Mon, 03 Feb 2020 09:46:45 GMT</pubDate>
            <atom:updated>2020-02-05T13:49:14.901Z</atom:updated>
            <content:encoded><![CDATA[<h3>Creating Serverless Workboard with Netlify Lambda — Part Two</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vy7LrUtFsnStSiypjPQnyA.jpeg" /></figure><p>In <a href="https://medium.com/@dragoje.jadranko/creating-serverless-workboard-with-netlify-lambda-part-one-9421d253739">Part One</a>, we have explained what serverless is, how to use Netlify Lambda, and created a simple example of functions. In this article, using lambda functions, we will create an API needed to run a simple workboard.</p><p>To have a working API for client application to consume, we need to have these functionalities:</p><ul><li>View and search boards</li><li>View, create, update and delete board</li><li>View, create, update and delete columns</li><li>View, create, update and delete tasks</li></ul><h3>Extract Database Connection Handling</h3><p>Since connecting to the database will be a common task, we will extract this functionality to utility function and use it in lambda functions.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2526052bc4fb793533a0e76bf8ffd5d5/href">https://medium.com/media/2526052bc4fb793533a0e76bf8ffd5d5/href</a></iframe><h3>Create Boards Function</h3><p>This function will provide a client application with a list of boards and the ability to search them by name. We will create a new function boards.js for this purpose.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/004e915f27bbe7999261c98ab3c684a8/href">https://medium.com/media/004e915f27bbe7999261c98ab3c684a8/href</a></iframe><p>Now, we have a GET /boards route that returns response data consumable by a client application. We can use this data to build a workboard list.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2a277740b24883b83ba941f5e89076c1/href">https://medium.com/media/2a277740b24883b83ba941f5e89076c1/href</a></iframe><p>We can paginate data using query parameters: limit and offset, and filter data using a query parameter name.</p><h3>Modify Board Function</h3><p>To get a single board detail, we will modify board function. We need to find the board by an internal identifier which we will pass using a query parameter. Since we will find the document by internal _id , we need to use ObjectId.createFromHexString method which we will add to db utility file.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8297acde6eec16c3d096710acac9319b/href">https://medium.com/media/8297acde6eec16c3d096710acac9319b/href</a></iframe><p>Since we need to support adding, editing and deleting actions, we will update the board function to implement POST request with different actions.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/eb14384428b464cdd460486e584aef4b/href">https://medium.com/media/eb14384428b464cdd460486e584aef4b/href</a></iframe><h3>Implement Board Columns Manipulation</h3><p>Since each board can have board columns, we will support manipulation of columns inside board documents. One board can have zero or more columns.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/190b7433780c47ebcb4308099eab14e6/href">https://medium.com/media/190b7433780c47ebcb4308099eab14e6/href</a></iframe><h3>Creating function for tasks</h3><p>Task is created inside a column of the board meaning that it is necessary to send the column and board inside the payload of task creation and retrieve action. Since each task has id, for delete and update actions, it is enough to pass id. We will also create a new task collection inside the Mongo database. In this example, we will add just a few properties for task document and expand them once UI is built.</p><p>Since all tasks need to be shown inside the board column, paging is not necessary, but we will keep it to limit tasks in the column to some reasonable value.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/159ae99b5968bf83fbe1a6d67e679451/href">https://medium.com/media/159ae99b5968bf83fbe1a6d67e679451/href</a></iframe><p>Since functions need to be kept small, I did not use any utility functions like lodash for now. It is visible that we could use thedefaultTo method of library to create query parameters fallback. We will check for possible optimisations with tree-shaking in further articles.</p><h3>Conclusion</h3><p>Lambda functions can be used to create API in single-page applications. Every function is a single path in the API URL. We differentiate actions using HTTP verbs GET and POST and using custom action property inside the payload as we did in this case.</p><p>State of the application in this stage can be viewed on apiLambda branch of the repository: <a href="https://github.com/manico/workboard-lambda/tree/apiLambda">https://github.com/manico/workboard-lambda/tree/apiLambda</a></p><p>In <strong>Part Three</strong>, we will create a visual representation of the workboard using functions as an API and upgrade some parts of the API.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f466a1affffc" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nsoft/creating-serverless-workboard-with-netlify-lambda-part-two-f466a1affffc">Creating Serverless Workboard with Netlify Lambda — Part Two</a> was originally published in <a href="https://medium.com/nsoft">NSoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating Serverless Workboard with Netlify Lambda— Part One]]></title>
            <link>https://medium.com/nsoft/creating-serverless-workboard-with-netlify-lambda-part-one-9421d253739?source=rss----c3fc05ce3056---4</link>
            <guid isPermaLink="false">https://medium.com/p/9421d253739</guid>
            <dc:creator><![CDATA[Jadranko Dragoje]]></dc:creator>
            <pubDate>Mon, 06 Jan 2020 10:31:17 GMT</pubDate>
            <atom:updated>2020-02-03T06:56:06.358Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zfCw7KV6XzXHrRtS6zqJlg.jpeg" /></figure><p>Infrastructure running our applications has gone through many changes in recent years — from using dedicated machines to run even simple applications, virtual machines reducing the cost of infrastructure to containers that simplified deployment and maintenance. To provide more development time and simplify application development, large companies like Amazon, Google, and Microsoft now offer an entire ecosystem for all applications allowing you not to think in terms of servers, but instead, you think of <strong>infrastructure as part of the workflow of your application.</strong></p><p>Term <em>serverless</em> has been described differently during the past few years. To make it clear, I will use term <em>serverless </em>to describe an application that runs in third-party stateless event-driven compute containers. In this architecture, a unit of application logic is a single function executed in these containers — which is where we get term <strong>FaaS</strong> (Function as a Service). The benefit of this approach is that <strong>the application developer is only focused on application, not on infrastructure.</strong> Infrastructure is in total control of third-party, scales automatically and charges per function execution (idle time is free of charge).</p><p>The downside is that we relinquish control over infrastructure to third-party making the debugging and monitoring process more difficult as we depend on how detailed are these services and how good is local development tooling provided by the third-party. Luckily, this is getting more robust every day.</p><p>The other downside is usually vendor lock-in, although functions should be easily deployable to different providers. From the business perspective, additional risk is an unpredictable expense that can increase significantly depending on the usage of functions.</p><p>Currently, most popular providers are:</p><ul><li><a href="https://aws.amazon.com/lambda/">AWS Lambda</a></li><li><a href="https://cloud.google.com/functions/">Google Cloud Functions</a></li><li><a href="https://azure.microsoft.com/en-us/services/functions/">Azure Functions</a></li></ul><h3>Netlify Lambda</h3><p>In this example, we will use <a href="https://docs.netlify.com/functions/overview/">Netlify Lambda</a> to create functions needed to power client application that manages simple workboard application.</p><p>In the traditional approach, to run this application, we would require:</p><ul><li>Client application that contains client logic</li><li>Service application that contains backend logic</li><li>Server that runs the client application</li><li>Scalable infrastructure for service application</li><li>CDN service for security and speed optimisation</li><li>Database cluster</li><li>Preferably caching layer for service application</li></ul><p>Using the serverless Netlify approach, we will try to reduce this to:</p><ul><li>Client application that contains client logic</li><li>Functions that execute application logic and communicate with the database</li><li>Netlify infrastructure</li><li>Managed database service</li></ul><p>Netlify infrastructure provides automatic atomic deployment of client application and functions from a git repository, offers powerful CDN out of the box as well as other features like branch deploys, split testing, client application pre-rendering, automatic deployment and packaging of functions to AWS, DNS management, proxy and redirect rules, unlimited snapshots and rollbacks, cache invalidation, etc.</p><p>Netlify uses AWS lambda behind the scenes, but enhances developer experience by versioning, building and deploying functions along with the client application. It also implements a custom API gateway that handles service discovery and the ability to rollback deploy. This process is totally seamless and the developer only needs to worry about the application logic inside functions.</p><h3>Project Setup</h3><p>We will start this project by creating a client application in <a href="https://vuejs.org/">Vue</a> (popular JavaScript framework) which will be the base of the project. The entire project will be contained in a single repository.</p><p>To create a new Vue application, we will use <a href="https://cli.vuejs.org/">vue-cli</a> command:</p><pre>vue create workboard-lambda</pre><p>From the CLI wizard, choose features: Babel, PWA, Router (with history mode), Vuex, CSS pre-processors (SASS with Dart), Linter (ESLint with Airbnb config), Unit Testing (Jest). Choose to save the configuration to dedicated config files.</p><p>To keep the initial setup clean, we will remove About.vue from files and router and rename HelloWorld.vue component to WorkboardCard.vue and remove entire generated content. The initial setup can be checked on branch initialSetup inside <a href="https://github.com/manico/workboard-lambda/tree/initialSetup">git repository</a>.</p><p>We will immediately connect the git repository with Netlify to have this initial project built. Go to <a href="https://app.netlify.com/">Netlify Application</a> and click New Site from Git button. Follow instructions to authorise Netlify with GitHub and choose git repository to connect to. Once connected, enter build command and deploy the site.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vK63dL-qdwB66hhSr3omXA.png" /><figcaption>Settings for site deploy</figcaption></figure><p>After the build, the site is available at a given domain. If you wish, you can set up a custom domain in <em>Domain Settings</em> section.</p><p>Although it is possible to develop functions without additional tools, we will use <a href="https://github.com/netlify/cli/blob/master/docs/netlify-dev.md">Netlify Dev</a> (part of Netlify CLI) for local development.</p><blockquote>Netlify Dev brings the power of Netlify’s Edge Logic layer, serverless functions and add-on ecosystem to your local machine. It runs Netlify’s production routing engine in a local dev server to make all redirects, proxy rules, function routes or add-on routes available locally and injects the correct environment variables from your site environment, installed add-ons or your netlify.toml file into your build and function environment.</blockquote><p>To use Netlify Dev, first install Netlify CLI as a global package:</p><pre>npm install -g netlify-cli</pre><p>Then log in to Netlify:</p><pre>netlify login</pre><p>You can now connect to your remote Netlify site meaning that Netlify Dev will pull all settings locally, so your local environment is in sync with remote. Consequently, local development is much easier and without changes when it comes to pushing to production. Use link command to link to your remote site:</p><pre>netlify link</pre><p>Choose from a list of sites to connect. Once linked, Netlify Dev creates .netlify folder inside your project with state.json file that contains site identification. From now we can run all cli commands inside the project directory.</p><p>To start the project locally, use netlify dev command. It will detect your project and run internal development environment command (in our case this is vue-cli-service serve command). It also starts Netlify local server that serves as a proxy endpoint for the client application, functions, add-ons and redirects. By default, Netlify chooses a random free port to run the dev server. You can override this with port parameter:</p><pre>netlify dev -p 8888</pre><p>Local server is now ready on <a href="http://localhost:8888">http://localhost:8888</a>.</p><h3>Creating Simple Function</h3><p>To create a function and serve it locally, it is required to create netlify.toml configuration file in the root directory of the project.</p><pre>[build]<br>  command = &quot;npm run build&quot;<br>  functions = &quot;lambda&quot;<br>  publish = &quot;dist&quot;</pre><p>Using build block inside configuration we are defining build command, functions directory and publish directory. Now we can create functions.</p><p>Netlify Dev offers commands for creating a function out of templates for easy learning. To create function this way use command:</p><pre>netlify functions:create</pre><p>Choose hello-world template and name your function echo. Command will create sample function that you can run using command:</p><pre>netlify functions:invoke echo</pre><p>Alternatively, you can visit <a href="http://localhost:8888/.netlify/functions/echo">http://localhost:8888/.netlify/functions/echo</a> in your browser to get JSON result with hello message.</p><p>Since we wish to use the same JavaScript style (like using module syntax in Vue), we will not create functions directly in functions directory, but inside src/lambda directory where Netlify dev will perform a compilation of source code and output it to root lambda directory. For this, we can use any build tool we wish, but as a convenience, we will use <a href="https://github.com/netlify/netlify-lambda">netlify-lambda</a>. First, install it as a project dependency:</p><pre>npm install netlify-lambda --save</pre><p>Add build script to package scripts:</p><pre>&quot;build-lambda&quot;: &quot;netlify-lambda build ./src/lambda&quot;</pre><p>Now, when we execute npm run build-lambda, the tool will compile source functions to production-ready functions. Netlify Dev detects this script and recompiles function when the source changes.</p><p>Once we push changes to git, Netlify will start build and deploy process. When the process finishes, site and functions are available on the deployed site with the ability to revert to an older version in <em>Deploys</em> section of Netlify Application.</p><p>Project in this stage can be checked at setupLambda branch of the <a href="https://github.com/manico/workboard-lambda/tree/setupLambda">git repository</a>. To execute remote function request URL ${baseSiteUrl}/.netlify/functions/echo.</p><h3>Anatomy of a JavaScript Serverless Function</h3><p>Serverless JavaScript function needs to export thehandler method. Name of the JavaScript file determines the name of the function. As demonstrated, every function can be invoked by requesting URL ${baseSiteUrl}/.netlify/functions/${functionName}. Function can also be <a href="https://docs.netlify.com/functions/trigger-on-events/#available-triggers">triggered by internal Netlify events</a>. We can use async to omit the last callback parameter recommended to return success or error:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/931bb0569412230d083d7c5d61cfce5d/href">https://medium.com/media/931bb0569412230d083d7c5d61cfce5d/href</a></iframe><p>Netlify provides two parameters in thehandler function. Event is an object that contains information about the request. It contains properties: path, headers, httpMethod, queryStringParameters, body which is JSON string representation of request payload and isBase64Encoded flag that indicates the encoding of payload. Inside context, Netlify provides information about the context in which function is invoked like clientContext in which we have identity and user information stored. Identity usage with functions will be shown in future parts of this article series.</p><p>To inspect theevent, we will add it to the echo function.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b7eabcadfa20a04e5c26ec2ac6158d1c/href">https://medium.com/media/b7eabcadfa20a04e5c26ec2ac6158d1c/href</a></iframe><p>Once executed, this is the response:</p><p>${baseSiteUrl}/.netlify/functions/echo?testQuery=true:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5c8152746041077cfa1f5b0c1cd09516/href">https://medium.com/media/5c8152746041077cfa1f5b0c1cd09516/href</a></iframe><p>In the response, data from headers is removed due to security reasons and for brevity. It contains standard headers along with the custom ones.</p><h3>Connecting to Database Inside Function</h3><p>To test getting data from the database we will connect to MongoDB named workboard and pull board collection documents to show in the output. You can create a free MongoDB database on <a href="https://cloud.mongodb.com/">Atlas</a>. Create a database workboard and collection board that several documents with single property (along with _id) called name. Example of document:</p><pre>{<br>  &quot;_id&quot;: {<br>    &quot;$oid&quot;: &quot;5e0b43311c9d44000063556e&quot;<br>  },<br>  &quot;name&quot;: &quot;Netlify Dev Release&quot;<br>}</pre><p>Create new board.js function inside src/lambda directory:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/15c43a5c00ca246e65ab15dedb35b08b/href">https://medium.com/media/15c43a5c00ca246e65ab15dedb35b08b/href</a></iframe><p>It is visible from the code that we are using environment variables. Where do they come from? Environment variables come from netlify.toml configuration file or environment variables set on Netlify Application project <em>Deploy Settings</em>. Since this information is secret, we are setting it inside the Netlify Application project settings. Once netlify dev is started locally, remote environment variables are pulled locally so we can run functions without changing anything.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/958/1*Yg0dZNy30oPd_PUtCHaI3Q.png" /><figcaption>Example of environment variable injection</figcaption></figure><p>If we inspect bundled (compiled) board function we can see that it is pretty large, nearly <strong>0.5MB</strong>. This is because <strong>it contains bundled dependencies</strong>, in this case, MongoDB node driver.</p><p>Netlify Application also offers functions view where we can check the execution log of each function. This is the example log where we can see that once started from a <em>cold state</em>, the function does not have initialisation duration added to execution time. It also shows memory usage per function execution.</p><pre>2:34:24: Duration: 698.72ms Memory: 80MB Init Duration: 186.89ms<br>2:34:26: Duration: 631.54ms Memory: 81MB<br>2:35:03: Duration: 645.15ms Memory: 83MB<br>2:35:17: Duration: 634.43ms Memory: 83MB<br>2:35:18: Duration: 658.80ms Memory: 83MB</pre><p>In this stage, project can be checked at setupDatabase branch of the <a href="https://github.com/manico/workboard-lambda/tree/setupDatabase">git repository</a>.</p><h3>Optimising Function Deploy and Execution</h3><p>The first thing before going further into the development of functions is to check for possible optimisations. It is best to follow <a href="https://docs.aws.amazon.com/lambda/latest/dg//best-practices.html">AWS instructions</a> on best practices to work with lambda functions.</p><p>In the current code, we are instancing MongoClient on each execution. We need to move this outside of the function handler so that the connection is cached and reused during the function lifecycle.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e272da9bd5d3b9b7d17fe8bc009b84d8/href">https://medium.com/media/e272da9bd5d3b9b7d17fe8bc009b84d8/href</a></iframe><p>When we check the function execution log now, we get quite different results:</p><pre>3:31:13: Duration: 704.53ms Memory: 80MB Init Duration: 193.64ms<br>3:31:17: Duration: 90.32ms Memory: 80MB<br>3:31:19: Duration: 89.91ms Memory: 80MB<br>3:31:20: Duration: 90.34ms Memory: 81MB<br>3:31:22: Duration: 95.28ms Memory: 82MB</pre><p>Duration time is decreased by almost 85% after the initialisation!</p><p>Another small optimisation we advise in this scenario is to set callbackWaitsForEmptyEventLoop property of context object to false. I have not seen any significant duration changes when this is set. The explanation is in <a href="https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html#nodejs-prog-model-context-properties">AWS documentation</a> and <a href="https://docs.atlas.mongodb.com/best-practices-connecting-to-aws-lambda/">MongoDB best practices</a>:</p><blockquote>By default, the callback waits until the runtime event loop is empty before freezing the process and returning the results to the caller. Setting this property to false requests that AWS Lambda freeze the process soon after the callback is invoked, even if there are events in the event loop. AWS Lambda will freeze the process, any state data, and the events in the event loop. Any remaining events in the event loop are processed when the Lambda function is next invoked if AWS Lambda chooses to use the frozen process.</blockquote><p>Other best practices can be found <a href="https://docs.aws.amazon.com/lambda/latest/dg//best-practices.html">here</a>.</p><p>In the <a href="https://medium.com/@dragoje.jadranko/creating-serverless-workboard-with-netlify-lambda-part-two-f466a1affffc"><strong>Part Two</strong></a><strong> </strong>we will create entire API for our application using Netlify functions.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9421d253739" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nsoft/creating-serverless-workboard-with-netlify-lambda-part-one-9421d253739">Creating Serverless Workboard with Netlify Lambda— Part One</a> was originally published in <a href="https://medium.com/nsoft">NSoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Grame — a New Way of Online UX]]></title>
            <link>https://medium.com/nsoft/grame-a-new-way-of-online-ux-be8a6afe2154?source=rss----c3fc05ce3056---4</link>
            <guid isPermaLink="false">https://medium.com/p/be8a6afe2154</guid>
            <category><![CDATA[seven]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[chameleon]]></category>
            <category><![CDATA[nsoft]]></category>
            <category><![CDATA[grame]]></category>
            <dc:creator><![CDATA[Jadranko Dragoje]]></dc:creator>
            <pubDate>Fri, 23 Aug 2019 12:20:06 GMT</pubDate>
            <atom:updated>2019-08-23T14:09:49.957Z</atom:updated>
            <content:encoded><![CDATA[<h3>Grame — a New Way of Online UX</h3><p>In NSoft, we produce and integrate games inside our online solution as well as third-party websites. The integration process on a third-party website involves several steps where third-party developers are involved to make things work, too. This process can be complex because it often requires custom layout development to suit customer <em>look and feel</em>, message exchange arrangement between a third-party website and game iframe, etc.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oi8wUju3xOW207f-wxjCpw.png" /><figcaption>Grame running three games</figcaption></figure><p>To speed up the entire process and offer customers unique experience we decided to create a new system for running our virtual and other games.<strong> We named it Grame.</strong> Simply put, Grame is a system for<strong> integrating</strong> and <strong>running multiple products</strong> inside Seven (our betting platform) or <strong>a</strong> <strong>third-party website.</strong></p><h3>Solve integration problems</h3><p>Using this system, a third-party developer will just have to integrate a snippet of code and update a single placeholder on their side. The entire integration process and message exchange are handled inside Grame system integration script. Moreover, there is no custom design and development since Grame offers a unique and standard look and feel. Customisation can be done using our<a href="https://medium.com/nsoft/how-vue-helped-us-create-chameleon-builder-2dd91e29f34e"> Chameleon Builder</a> where customer can choose which modules to use inside Grame and set some basic color scheme.</p><h3>Multiple games at the same time</h3><p>With Grame, a user can run multiple games at the same time in the same view. This offers faster betting because it removes the need to switch between the games. Without the game switching and with the ability to run <strong>up to four games at the same time</strong>, a punter can play multiple tickets in a short period of time. <strong>More tickets mean more revenue.</strong></p><p>This system is flexible enough to accept all our games, but also third-party ones like Casino games. The user can play <strong>LuckySix and Slot game at the same time</strong>!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Q4wM3_CccGY2hsH6qvqPPg.png" /><figcaption>Grame Lobby</figcaption></figure><h3>Features and Customisation</h3><p>Main features of the Grame system are:</p><ul><li>Fast and easy integration</li><li>Pay in one ticket or play multi tickets</li><li>Focus on the game</li><li>Play multiple games</li><li>Build using Chameleon</li><li>Run in full screen</li><li>Games lobby inside Grame</li></ul><p>Grame system will be available on web, mobile and terminal channels with the same standard UI and UX.</p><p>Grame is configured and built using Chameleon Builder. Configurable options per customer are:</p><ul><li>Full or integrated version</li><li>Show or hide header</li><li>Change color palette, show or hide customer logo, show or hide balance</li><li>Use multiple games or just single game</li><li>Support favorites, game help or game settings</li></ul><h3>Showcase</h3><p>You can check out our showcase videos for web and mobile:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FAUwNkXSy6zA%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DAUwNkXSy6zA&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FAUwNkXSy6zA%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/c763eb83d12d77b9abb89711d315d75b/href">https://medium.com/media/c763eb83d12d77b9abb89711d315d75b/href</a></iframe><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2F0KBsOMxI-dQ%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D0KBsOMxI-dQ&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2F0KBsOMxI-dQ%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/49d6690c2c1f28060fdc86560d07f924/href">https://medium.com/media/49d6690c2c1f28060fdc86560d07f924/href</a></iframe><p>We would like to thank our Design Team for the idea and for pushing it into development.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=be8a6afe2154" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nsoft/grame-a-new-way-of-online-ux-be8a6afe2154">Grame — a New Way of Online UX</a> was originally published in <a href="https://medium.com/nsoft">NSoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Kubernetes Logging Stack]]></title>
            <link>https://medium.com/nsoft/kubernetes-logging-stack-7e5100562a32?source=rss----c3fc05ce3056---4</link>
            <guid isPermaLink="false">https://medium.com/p/7e5100562a32</guid>
            <category><![CDATA[logging]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[nsoft]]></category>
            <category><![CDATA[fluentbit]]></category>
            <category><![CDATA[elasticsearch]]></category>
            <dc:creator><![CDATA[Vlado Đerek]]></dc:creator>
            <pubDate>Fri, 21 Jun 2019 11:17:07 GMT</pubDate>
            <atom:updated>2019-07-01T09:12:41.136Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TfOATM7dx7l1DTzvgDRWDA.jpeg" /><figcaption>Mandatory log picture, get it? LOGGING, GET IT?!</figcaption></figure><p>A big part of the new cloud narrative is the observability which encompasses monitoring, logging and tracing with a single purpose of gaining visibility into the performance of your apps and infrastructure.</p><p>How to handle logging in Kubernetes has long been a topic of discussion here and there isn’t a simple answer. I personally have searched for “best practices” and failed. It’s all up to you. Act according to your needs and circumstances.</p><p>At first, we used our Graylog centralized setup which was already serving our existing infrastructure and just deployed Fluentd into our cluster. The issue here was that we had a bunch of logs streaming to our Graylog in high volume. This included Ambassador API Gateway, Istio, Prometheus and all other infrastructure apps. You can only imagine the volume of such streams.</p><p>In order to rationalize the process, our decision was to deploy a local stack into the cluster which will collect all the logs from the cluster (including Kubernetes API events) and store them there. All other application logs can still be forwarded to Graylog if they are critical for storage.</p><p>While searching for a complete solution I ran into many guides on how to deploy an ELK stack for logging but something was always missing. This provides a simple and robust way to deploy such logging stack by using a single helm chart (which is just a meta chart that combines other charts).</p><h3>Components Used</h3><p>The first part of the stack is Elasticsearch which is a distributed search and analytics engine used worldwide mostly with Elastic Stack (Elasticsearch, Logstash and Kibana — ELK) for open source logging purposes. You can read more at <a href="https://www.elastic.co/solutions/logging">Elasticsearch logging</a>.</p><p>Here I am using a helm chart provided by <a href="https://github.com/elastic/helm-charts/tree/master/elasticsearch">Elastic</a> for deploying Elasticsearch. There is also a chart in stable/charts but it is scheduled for depreciation, info <a href="https://github.com/helm/charts/tree/master/stable/elasticsearch#pre-deprecation-notice">here</a>. Similar to Elasticsearch, there is a new chart from Elastic for <a href="https://github.com/elastic/helm-charts/tree/master/kibana">Kibana</a> which is going to be used for this.</p><p>There is an elastic operator that is being developed by Elastic which may be used in the future but right now it is still in alpha. It is capable of deploying Elasticsearch and Kibana using Kubernetes CRDs and promises to provide a way to dynamically scale storage which is not possible with the chart. I will update when that matures a little. More info on the operator <a href="https://www.elastic.co/blog/introducing-elastic-cloud-on-kubernetes-the-elasticsearch-operator-and-beyond">here</a>.</p><p>Next component used is <a href="https://github.com/helm/charts/tree/master/stable/elasticsearch-curator">elasticsearch-curator</a>, designed do rotate older indices in order to save space (logs can take a lot of space and retention policy varies from company to company. Our Graylog setup is used for long term storage and this logging stack deploy is more of a short term storage). It also has an option of archiving older logs to S3 but that is not the scope of this article.</p><p>In order to read logs from the container stdout and ship them to Elasticsearch nodes, <a href="https://github.com/helm/charts/tree/master/stable/fluent-bit">fluent-bit</a> is used. Additionally, to read Kubernetes API logs we use <a href="https://github.com/helm/charts/tree/master/stable/metricbeat">metricbeat</a>.</p><h3>Metachart</h3><p>It’s called a metachart because it doesn’t have any templating implemented and it is being used just to enable deploying the whole stack with a single chart that has others in its requirements. The chart is located <a href="https://github.com/volatilemolotov/k8s-logging">here</a>.</p><p>The idea is that by using requirements one can still use the chart values for each component of the stack enabling deployment customization according to needs using requirement aliases as shown:</p><pre>dependencies:<br> — name: elasticsearch<br> version: 7.1.1<br> repository: <a href="https://helm.elastic.co">https://helm.elastic.co</a><br> alias: elasticsearch<br> — name: elasticsearch-curator<br> version: 1.5.0<br> repository: <a href="https://kubernetes-charts.storage.googleapis.com">https://kubernetes-charts.storage.googleapis.com</a><br> alias: curator<br> — name: kibana<br> version: 7.1.1<br> repository: <a href="https://helm.elastic.co">https://helm.elastic.co</a><br> alias: kibana<br> — name: fluent-bit<br> version: 2.0.5 <br> repository: <a href="https://kubernetes-charts.storage.googleapis.com">https://kubernetes-charts.storage.googleapis.com</a><br> alias: fluentbit<br> — name: metricbeat<br> version: 1.6.4<br> repository: <a href="https://kubernetes-charts.storage.googleapis.com">https://kubernetes-charts.storage.googleapis.com</a><br> alias: metricbeat</pre><p>Where versions are defined, the chart can be updated using:</p><pre>helm update deps</pre><p>to fetch the latest versions (if explicitly set in requirements.yaml file) from upstream charts. This saves time from maintaining forks.</p><p>One can also use values from upstream charts in valuefile by nesting them under the defined alias for each requirement:</p><pre>elasticsearch:<br> clusterName: “logger-elasticsearch”<br> nodeGroup: “master&quot;</pre><p>which enables you to use all the options that the chart maintainers created.</p><h3>Deploying the stack</h3><p>The logging stack is deployed by cloning the chart repo:</p><pre>git clone <a href="mailto:git@github.com">git@github.com</a>:volatilemolotov/k8s-logging.git</pre><p>and entering the chart directory:</p><pre>cd k8s-logging/logger</pre><p>The next step is to edit the valuefile in order to set up the stack. The provided default values are more than enough to run the stack. If you choose to edit you can read about options on the respected chart repos:</p><p><a href="https://github.com/elastic/helm-charts/tree/master/elasticsearch#configuration">Elasticsearch</a></p><p><a href="https://github.com/elastic/helm-charts/tree/master/kibana#configuration">Kibana</a></p><p><a href="https://github.com/elastic/helm-charts/tree/master/elasticsearch#configuration">Elasticsearch-curator</a></p><p><a href="https://github.com/elastic/helm-charts/tree/master/elasticsearch#configuration">fluent-bit</a></p><p><a href="https://github.com/helm/charts/tree/master/stable/metricbeat">Metricbeat</a></p><p>One thing to note is that you have to change the valuefile for the elasticsearch-curator, because the image used for curator is no longer maintained and does not work with Elasticsearch 7.1.1.<br>You can use the following <a href="http://quay.io/volatilemolotov/curator:v5.7.6,">image</a>. I have just forked the elasticsearch-curator git and set up an automated build on Quay.io. Also, remember to change the command used.</p><pre>curator:<br> cronjob:<br> schedule: “0 1 * * *”<br> serviceAccount:<br> create: true<br> image:<br> <strong>repository: quay.io/volatilemolotov/curator<br> tag: v5.7.6</strong><br> pullPolicy: IfNotPresent<br> hooks:<br> install: false<br> upgrade: false<br> dryrun: false<br><strong> command: [“/curator/curator”]</strong><br> configMaps:<br> ---</pre><p>Next step is to install the chart:</p><pre><strong>helm</strong> upgrade — install logger . — namespace logging -f values.yaml</pre><p>and wait until all the components are running.</p><p>After that port-forward into the Kibana pod and go to localhost:5601.</p><p>When starting Kibana for the first time, index patterns are required to be set up. This can be done by going to the Discover panel where you are presented with a list of indices. Create two : <strong>kubernetes_cluster* </strong>and<strong> kubernetes_events*</strong>. Select <strong>@timestamp</strong> for <strong>Time Filter field</strong> name for both.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7kQIQafMSQ3sshMIuwQTxA.png" /><figcaption>Index patterns adding screen</figcaption></figure><h3>Storage Caveat</h3><p>Given that the Elasticsearch chart uses StatefulSet for deployment, it is not possible to change the size of the Persistent Volume Claim. In order to do this you need to manually edit the claim (all three of them if deploying from standard values) and wait until the resize is finished. Use:</p><pre><strong>kubectl</strong> get pvc logger-elasticsearch-master-logger-elasticsearch-master-0 -o yaml</pre><p>to check the status of the PVC. When the resize action is done, you will get the following message:</p><pre>message: Waiting for user to (re-)start a pod to finish file system resize of <br> volume on node.</pre><p>After this message, you need to reset the pod which is using that disk. Make sure you reset the pods one by one.</p><p>If you wish to contribute, you can join us in our <a href="https://github.com/nsftx">open source projects</a>, or you can<a href="https://goo.gl/vhAP3i"> apply for a job</a> at NSoft.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7e5100562a32" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nsoft/kubernetes-logging-stack-7e5100562a32">Kubernetes Logging Stack</a> was originally published in <a href="https://medium.com/nsoft">NSoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Data Analytics at NSoft]]></title>
            <link>https://medium.com/nsoft/data-analytics-at-nsoft-4f9de9bc5592?source=rss----c3fc05ce3056---4</link>
            <guid isPermaLink="false">https://medium.com/p/4f9de9bc5592</guid>
            <category><![CDATA[data-visualization]]></category>
            <category><![CDATA[nsoft]]></category>
            <category><![CDATA[real-time-analytics]]></category>
            <category><![CDATA[big-data]]></category>
            <category><![CDATA[analytics]]></category>
            <dc:creator><![CDATA[Filip Ćorluka]]></dc:creator>
            <pubDate>Wed, 29 May 2019 13:02:37 GMT</pubDate>
            <atom:updated>2019-05-30T11:33:54.072Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zolf8xCoZ-f4ZOWjaoTFKQ.jpeg" /></figure><p><strong>Introducing NSoft’s BAS and NDS</strong></p><p>The formation of <strong>BAS (Business Analytics Specialists) team</strong> started 2 years ago with the arrival of a small BI team to NSoft in order to face new challenges in a completely new industry.</p><p>Considering our business background and experience in <strong>Data warehouse (DWH)</strong> development and maintenance, reporting and data provisioning, as well as the previous, almost decade long, cooperation and teamwork, we were always well aware of the importance of data and its impact on business performance. But what we have encountered at NSoft has solidified our thinking that the sole data provisioning is the thing of the past — the present data provisioning is being performed by automated systems without human interaction. That was the starting point of something that consequently evolved into a completely internally developed full-fledged <strong>Data Analytics Solution </strong>backed by <strong>BAS</strong> team and data engineers in <strong>NDS (NSoft Data Service) team</strong>. Implemented, with the best practices in mind, we are currently operating <strong>Data warehouse (DWH)</strong> capable of nearly real-time processing and ingesting millions of data points per second which then feed into our analytics layer. It resulted with a clear new goal to strive for — <strong>Data Analytics</strong>.</p><p><strong>What is a Data Analyst?</strong></p><p>Data Analyst by definition is <strong>someone who is able to successfully translate large quantities and a variety of data into plain language understandable to others.</strong> But the role of the data analyst isn’t strictly tied to data “translation” and understanding. It’s more of a role that incorporates generating clear and advisory actions that have an immediate business impact. Within the context of NSoft, upon initial observation of the entire business process, we have soon realized the requirements for a data analyst and how one could contribute. This included familiarization with vast quantities of data, from numerous and various sources, combining, preparing and finally analyzing and extracting the value. What seems like a list of cursory terms is actually enormous effort that involves preparation, analysis, development, and usage of a system that enables BAS to govern valuable data and generate value for NSoft and it’s partners alike.</p><p><strong>Importance of Turnkey</strong></p><p>Striving towards common growth is the simple definition of what the <strong>Turnkey </strong>is. Coming to NSoft and adopting the <strong>Turnkey</strong> concept has made us completely change our mindset regarding the usage of product and user generated data. For NSoft the turnkey product solutions represent symbiosis with our partners with the common goal of mutual growth. Taking it all into consideration, through time, we have realized our mission statement has shifted: <strong><em>“We will not be providing data, we will provide the value!”</em></strong></p><p><strong>Data Analytics as a product aid</strong></p><p>One of the ways through which data analytics provides value is by improving the existing product portfolio wherever possible. By providing services of statistical analysis, testing product logic, identifying user behavior as well as positive user feedback, we aim to implement our findings and insights into bettering each of our products.</p><p>Based on our analyses and observations, the numerous improvements, tweaks, upgrades have been made to our products which have led to greater recognizability of our products and far better player retention.</p><p>All products from NSoft’s rich portfolio receive the same treatment from our Analytics team in order to provide the best possible product to our partners and the best gaming experience to our players.</p><p>All of that combined sums to a sentence that best describes our aspirations:</p><ul><li><strong>Through data analysis, we aim to improve existing products and help to create new ones, to provide our partners with the better business results, give the players the best possible playing experience and maintain the highest standards our company has set up and insists on.</strong></li></ul><p><strong>Data Analytics as a partner aid</strong></p><p>Everything previously mentioned has resulted in the development of workflows capable of consuming and digesting this enormous amount of information which enables us to provide our partners with momentary information regarding anything of interest.</p><p>Additionally, by identifying the reasons for user churn we can provide our partners with actionable insights. <strong>Actionable insight is more than an information with context, it’s a prescription for a clear course of action. </strong>The final goal of those actions is an increase in partner’s revenue, as well as an increase in confidence in our information provisioning.</p><p>Our team has streamlined dashboard generation into an assortment of standardized report packages offered to all of our partners and in-house teams and individuals alike.</p><p><em>Today NSoft’s BAS team aims to improve existing products and help create new ones, to provide our partners with the better business results and give the users the best possible playing experience.</em></p><p><strong>The technology that made it all possible</strong></p><p>The technology stack involved was designed to be robust, easily scalable, adaptive and highly available and it required an enormous effort.</p><p>In our DWH implementation, we’ve chosen a <strong>streaming solution instead of ETL</strong> processes. As a starting point we should mention in-house operational data stores used for streaming sources:</p><p>Currently, from NoSQL databases, we have <strong>MongoDB</strong> as a source, and from SQL databases we’re currently using <strong>MySQL</strong> and <strong>PostgreSQL</strong>. Additionally, due to our partners’ specifics and requirements we are supporting various <strong>CSV</strong>, <strong>Google Sheets</strong>, <strong>SQLite</strong>, <strong>MS Excel</strong> data enrichments which can be blended at source or anywhere in the streaming pipeline seamlessly.</p><p>For <strong>data streaming Apache Kafka</strong> is used as a backbone and it’s accompanied by <strong>ZooKeeper</strong>, <strong>Kafka Connect</strong>, <strong>Kafka Streams</strong>, <strong>Kafka Schema Registry</strong> (as we’re using AVRO for primary streaming format) and <strong>Confluent kSQL</strong>.</p><p>Through streaming, pipeline data is enriched and transformed to ensure resulting data form compliance and quality and minimize data cleansing overhead.</p><p>Due to high-level data processing in the pipeline we’ve also decided to implement a <strong>caching layer using Redis</strong>.</p><p><strong>For resulting DWH solution we’ve opted for Vertica, a leading Enterprise SQL Column Store Database solution,</strong> as our long-term store and analytics back-end, as well as opting for <strong>Elasticsearch for our near real-time data insights</strong>.</p><p>The<strong> Tableau</strong> is our primary information presentation driver, coupled with <strong>Superset, Metabase</strong> and <strong>Google Data Studio</strong>. Tableau was versatile enough to meet all the requirements we have had from an information distribution platform. <strong>Grafana </strong>and <strong>Kibana </strong>were our primary choices for visualization of near real-time data.</p><p><strong>Programming languages in use are Java as a primary, Python as a secondary</strong> but as it usually happens, you need to expand your palette to deliver a good product so <strong>Bash</strong>, <strong>NodeJS,</strong> and <strong>PHP</strong> happen to be regular visitors of our team.</p><p>Monitoring needs are being effectively covered with <strong>Sensu</strong> for the systems part and <strong>Prometheus</strong> for service monitoring — ranging from availability checks to performance metrics.</p><p>Most of <strong>our stack is powered by Docker</strong> and due to stack size it was impossible to keep things smooth without orchestration solution, that’s where we decided to use <strong>Kubernetes with Rancher on-top</strong> for easier management.</p><p>Current CI/CD pipeline is custom implemented using Helm based <strong>Jenkins and Kubernetes</strong> publishing. It’s also worth mentioning that our K8S setup is expanded with <strong>Istio service mesh</strong>, <strong>MetalLB</strong>, <strong>fluentd</strong>, <strong>Rook</strong> and many other improvements compared to the standard issue of Kubernetes setup.</p><p>The entire setup is currently configured on bare-metal dedicated server machines and is horizontally and vertically scalable, as well as highly-available. The configuration of such stack led to high proficiency in DevOps department and faced us with some interesting challenges.</p><p>If you wish to be a part of our company and learn more about data science, <a href="http://bit.ly/2VXuEuX">find out more about us</a> and <a href="http://bit.ly/2VY4iZy">apply for a job</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4f9de9bc5592" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nsoft/data-analytics-at-nsoft-4f9de9bc5592">Data Analytics at NSoft</a> was originally published in <a href="https://medium.com/nsoft">NSoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Product Design team at NSoft]]></title>
            <link>https://medium.com/nsoft/product-design-team-at-nsoft-69b6a72522fb?source=rss----c3fc05ce3056---4</link>
            <guid isPermaLink="false">https://medium.com/p/69b6a72522fb</guid>
            <category><![CDATA[design-thinking]]></category>
            <category><![CDATA[ui-design]]></category>
            <category><![CDATA[design]]></category>
            <category><![CDATA[product-design]]></category>
            <category><![CDATA[nsoft]]></category>
            <dc:creator><![CDATA[Josip Vrbic]]></dc:creator>
            <pubDate>Wed, 20 Mar 2019 13:55:23 GMT</pubDate>
            <atom:updated>2019-03-20T13:55:22.952Z</atom:updated>
            <content:encoded><![CDATA[<h3>The world through NSoft designers’ eyes</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*Ii5fv10oYYMijuJtK79a6g.jpeg" /><figcaption>Product Design team</figcaption></figure><p>Here at <a href="https://goo.gl/Z2MHwC">NSoft</a>, we have an in house design team with a total of 9 designers. We tend to hire ‘T’ shaped designers meaning that most of us have general knowledge across many different areas of design, but everyone is really, really good in one thing. UI and UX designers, Interaction Designers, Graphic Designers, and Illustrators are working together to bring the best possible product to our customers.</p><p>When people think about design, they mostly refer to products’ appearance. Currently, we do have one of the best looking products on the market. However, now we work more on evolving our Design System <em>(standardized set of rules and elements that unify look and feel of all our products),</em> product updates and market/user research, so we can bring best possible products to our customers as well as a great experience for their users.</p><p><strong>We don’t just design products, but also we suggest what might be appealing to the market.</strong></p><blockquote><strong>We want to deliver something beautiful with both functional design and the best possible user experience.</strong></blockquote><p>I’ve been working in NSoft for 7 years now, so I could say I grew up with the organization. I’ve participated in shaping our Product Design team into a unique group of people.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8AJ4nAA1ZnO_eQIQdz9SRQ.png" /><figcaption>The new project we are working on. We didn&#39;t want to have the same boring dashboard every morning we come to work. So we refreshed it with illustration (we change illustration depending on the time of year, events etc.) and AI message bot.</figcaption></figure><p><strong>It’s important to say that we were lucky enough to have more than just a talented team, but leaders who believe in the power of design.</strong> We could say we’ve deserved it by bringing good ideas to the table and making great products. The Designers from other companies struggle to bring their vision and ideas to top management, regardless of their work.</p><h3><strong>Process</strong></h3><p>Everything might look easy when you read and learn about the different processes of product development, especially when some of them go through 5 phases and others go through 12 phases. Some companies first do the research, others start designing their product and make changes on the go while some define problems they want to solve and then proceed to another phase.</p><blockquote><strong>What works for one company, most likely, does not work for another.</strong></blockquote><p>To make a good product you have to understand your clients’ needs, who makes the requests and what are the capabilities of your team.</p><p>We use different approaches depending on the type of request:</p><p>If a product is completely new, we like to start with a few concept designs which can help everybody in the team to understand what we are actually making.</p><p>If we need to update the existing features, we use iterative design — we do research and based on the analyzed results, we make changes.</p><p>We realized that <strong>Design sprints are a </strong>good way of making new innovative products. We usually use these sprints in the early phase of planning a new product. The idea is to describe and let everyone understand what is the problem we want to solve. Afterward, everyone in the team works independently on their solution for one day before coming together again to share their work. We go through all solutions, discuss them and decide which is the best one.</p><h3><strong>Collaboration</strong></h3><p>We truly believe in the strength of collaboration, not only within the Design team but within the company. When making or updating our products, we have brainstorming meeting(s) that involve members from different teams — Development, Product Management, Business, etc.</p><p>What I’ve learned through my work is that there are Backend and Frontend Developers with a great sense of product and UX. Despite the thought that Designers have the best and most creative ideas, if you give chance to other people to participate in the process of product design you could be surprised with results. Anyone can have and suggest ideas and every idea is valued equally.</p><blockquote><strong>Leave your silo, explore, talk and develop great products with the help of people around you.</strong></blockquote><p>Robert Pressman in his book “Software Engineering: A Practitioner’s Approach” said: <strong><em>“For every dollar spent to resolve a problem during product design, $10 would be spent on the same problem during development, and multiply to $100 or more if the problem had to be solved after the product’s release.”</em></strong> Solving the problem at the start is the most cost-effective and you can do it only if you include people from different areas.</p><p>Our company and team have grown rapidly in a few years, and it was hard to motivate people to work through collaboration. The truth is<strong> nobody should believe that their ideas are the best ones.</strong></p><h3><strong>Biggest challenge</strong></h3><p>If you know any passionate designers, you can agree — designers are, let&#39;s say, unique. They all have cutting edge ideas, their own style, and unique mindset.</p><p>Now, imagine 9 people with different ideas and try to make them work with the same design elements under the same vision. You have to make them feel that the product they are building is actually theirs because if they do not feel that, it will not be made with passion.</p><h3><strong>How we help our clients?</strong></h3><p>Today, if you have a bad product or service, users go to other providers. There are numerous companies in the same business, so you have to continuously evolve to keep up with the trends and customers’ needs.</p><p>The primary goal of any business is to increase its sales, make sure the business grows and build brand awareness.</p><p>Product Design plays an essential role in achieving this goal by improving user experience and customer satisfaction. The result is an increased number of product users.</p><p>Each customer has a Customer Lifetime value and this value depends upon the customer’s willingness to keep using your product. If you have a great UX design that does not frustrate the customers, they will more likely dismiss the small mistakes.</p><p>Do you know how many users don’t use your product because they had trouble registering? How many of them don’t know how to use your product at first sight? How long does it take to do an action and can you make users do it more easily? Conversion rates depend highly on the user’s ability to use the application easily.</p><p>We get to know our users by doing research on what they expect from the product. We try to iterate regularly on existing products and improve the experience for partners and their users.</p><blockquote><strong><em>If you have the best product today, it doesn’t mean you should stop working on it.</em></strong></blockquote><p>It is necessary to research and always stay on alert. That’s the reason why we are always on top of new technologies, and among the first ones in our industry offering concepts for our products that include virtual and augmented reality, artificial intelligence, face recognition etc. These products don’t necessarily bring us money, but they prove that we don’t work in a bubble and that we are ready for the future.</p><h3><strong>Wrap Up</strong></h3><p>Design became an integral part of our vision and additional value driving company strategy. NSoft will keep creating a culture, environment, and process that attracts the best people for our team and company.</p><p>If you wish to be a part of our Design or any other team, <a href="https://goo.gl/Lh2fe5">apply for a job</a> and join us at NSoft in the creation of innovative products.</p><p>Cheers.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=69b6a72522fb" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nsoft/product-design-team-at-nsoft-69b6a72522fb">Product Design team at NSoft</a> was originally published in <a href="https://medium.com/nsoft">NSoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>