<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mahadevan Sreenivasan</title>
    <description>The latest articles on DEV Community by Mahadevan Sreenivasan (@mahadevans87).</description>
    <link>https://dev.to/mahadevans87</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F479935%2F0fcbd2b0-5399-4ad1-850d-d6e4f0e97357.png</url>
      <title>DEV Community: Mahadevan Sreenivasan</title>
      <link>https://dev.to/mahadevans87</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mahadevans87"/>
    <language>en</language>
    <item>
      <title>Building a fast URL Shortener with Go and Redis</title>
      <dc:creator>Mahadevan Sreenivasan</dc:creator>
      <pubDate>Tue, 18 Oct 2022 14:36:55 +0000</pubDate>
      <link>https://dev.to/mahadevans87/building-a-fast-url-shortener-with-go-and-redis-31b9</link>
      <guid>https://dev.to/mahadevans87/building-a-fast-url-shortener-with-go-and-redis-31b9</guid>
      <description>&lt;h2&gt;
  
  
  Why build a URL shortener now?
&lt;/h2&gt;

&lt;p&gt;URLs shorteners have been around for a while now and one could just pick any of the hundreds of URL Shorteners available on the web and start using it.&lt;/p&gt;

&lt;p&gt;However building our own URL shortener has its own benefits -&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Most URL shorteners require you to pay for creating custom URL shorts (or &lt;a href="https://help.short.io/en/articles/4065948-what-is-a-url-slug" rel="noopener noreferrer"&gt;slugs&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Many mature URL shorteners will allow you to hold your redirects only for a short period of time.&lt;/li&gt;
&lt;li&gt;URL shorteners generally make money by selling URL redirects and customers' IP addresses to third parties. If you are concerned about data privacy, you would build your own URL shorteners.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Functional Requirements
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;We should be able to generate unique short-URLs for every valid HTTPS URL&lt;/li&gt;
&lt;li&gt;We should be able to resolve short-URLs quickly and redirect users to actual URLs&lt;/li&gt;
&lt;li&gt;Short-URLs should have a lifetime / expiry.&lt;/li&gt;
&lt;li&gt;We should be able to specifiy an optional custom short-URL. If such a custom slug is provided, we will try to map the original URL to the custom short, else we will generate a new one.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Non Functional Requirements
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The system will be read heavy i.e the ratio of redirects will be much higher than generating short-urls. Let's assume this ratio to be 100 : 1&lt;/li&gt;
&lt;li&gt;Resolving short-urls and redirecting users to the original URL should happen instantaneously with minimal delay.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Traffic Estimates
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Let's assume that users generate 100K short-urls every month. That means we can expect 100K * 100 redirects -&amp;gt; 10M redirects every month.&lt;/li&gt;
&lt;li&gt;10M redirects would translate to 10M / (30d * 24h * 3600s) = ~3 redirects a second.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Storage Estimates
&lt;/h2&gt;

&lt;p&gt;To compute storage, let's assume that we use a key-value store with the key being the short-url and the value as the original URL and the entire record to have an expiry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;custom-short: ~10 characters
url: ~1000 characters
ttl: int32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this, we can assume that a record would be ~1KB. With 100K unique short-urls every month, we can assume our memory capacity at 100K * 1KB = ~100 MB. For an entire year, we can assume ~1.2GB / year&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating the short-URL or Slug
&lt;/h2&gt;

&lt;p&gt;If the user has already provided a custom-short, we will try to set that as the short-URL. Otherwise, we will have to generate a unique short-URL.&lt;/p&gt;

&lt;p&gt;Remember that we are generating 100K short-urls per month or 1.2M short-urls a year or ~15M short-urls in 12 years :)&lt;/p&gt;

&lt;p&gt;How do we generate unique URLs with minimal collision for 15M urls over time?&lt;/p&gt;

&lt;h3&gt;
  
  
  Base62 Encoding
&lt;/h3&gt;

&lt;p&gt;Our custom-short URLs have to be reasonably short 1 - 10 characters. To generate such URLS, initially we can generate a random &lt;code&gt;int64&lt;/code&gt; number within a specific range &lt;code&gt;(0, 15M)&lt;/code&gt;. The idea is to convert this number from &lt;code&gt;Base10&lt;/code&gt; to &lt;code&gt;Base62&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But before that, lets try to get some intuition -&lt;/p&gt;

&lt;p&gt;Assume that the random &lt;code&gt;int64&lt;/code&gt; that we generate is &lt;code&gt;234556&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;The equivalent binary or &lt;code&gt;Base2&lt;/code&gt; -&amp;gt; &lt;code&gt;001111000010100111&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The equivalent Hex or &lt;code&gt;Base16&lt;/code&gt; -&amp;gt; &lt;code&gt;C3493&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The equivalent &lt;code&gt;Base64&lt;/code&gt; encoded string would be -&amp;gt; &lt;code&gt;8q5&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Base64&lt;/code&gt; alphabet consists of the following characters&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const (
    alphabet      = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/="
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, we don't want URLs to contain special characters. So we remove the slash and the equals sign and create a &lt;code&gt;Base62&lt;/code&gt; alphabet and encode our &lt;code&gt;int64&lt;/code&gt; to a &lt;code&gt;Base62&lt;/code&gt; string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const (
    alphabet      = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678"
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember, our assumption is that we would store at-most 15M shorts. So if we generate &lt;code&gt;15,000,000&lt;/code&gt; as a random integer for our short, the equivalent &lt;code&gt;Base62&lt;/code&gt; encoded string would be -&amp;gt; &lt;code&gt;El6ab&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Thats only 5 characters! What about &lt;code&gt;100,000,000&lt;/code&gt; (100M)? The equivalent &lt;code&gt;Base62&lt;/code&gt; would be -&amp;gt; &lt;code&gt;oJKVg&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You see, our short-urls / slugs are &amp;lt; 5 characters, which is exactly what we want.&lt;/p&gt;

&lt;p&gt;Here is a version of a int64 -&amp;gt; Base62 algorithm written in &lt;code&gt;go&lt;/code&gt; and the algorithm is pretty straightforward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const (
    alphabet      = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
)

func Base62Encode(number uint64) string {
    length := len(alphabet)
    var encodedBuilder strings.Builder
    encodedBuilder.Grow(10)
    for ; number &amp;gt; 0; number = number / uint64(length) {
        encodedBuilder.WriteByte(alphabet[(number % uint64(length))])
    }

    return encodedBuilder.String()
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  System Design
&lt;/h2&gt;

&lt;p&gt;Since the title of the post has Golang and Redis, we are obviously going to use both of them to build our URL shortener service.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffe47quh6mck0evpmatcm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffe47quh6mck0evpmatcm.png" alt="Design" width="800" height="396"&gt;&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Redis is a fast key-value store and for our purposes, it seems like an ideal solution. For the webserver in go, I used go-fiber inspried by &lt;a href="https://www.youtube.com/watch?v=3ExDEeSnyvE" rel="noopener noreferrer"&gt;Akhil Sharma's YT channel&lt;/a&gt;. If you are from a Node.JS / Express background, you should feel at home with go-fiber.&lt;/p&gt;

&lt;p&gt;Our primary request and response can be defined like so -&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Models
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type request struct {
    URL         string        `json:"url"`
    CustomShort string        `json:"short"`
    Expiry      time.Duration `json:"expiry"`
}

type response struct {
    URL             string        `json:"url"`
    CustomShort     string        `json:"short"`
    Expiry          time.Duration `json:"expiry"`
    XRateRemaining  int           `json:"rate_limit"`
    XRateLimitReset time.Duration `json:"rate_limit_reset"`
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Connecting to Redis
&lt;/h3&gt;

&lt;p&gt;We will connect with Redis with a helper like so -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package database

import (
    "context"
    "os"

    "github.com/go-redis/redis/v8"
)

var Ctx = context.Background()

func CreateClient(dbNo int) *redis.Client {
    rdb := redis.NewClient(&amp;amp;redis.Options{
        Addr:     os.Getenv("DB_ADDR"),
        Password: os.Getenv("DB_PASS"),
        DB:       dbNo,
    })

    return rdb
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The shorten endpoint
&lt;/h2&gt;

&lt;p&gt;The Shorten endpoint takes in the following payload -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "url": "https://your-really-long-url/"
  "custom-short": "optional",
  "expiry": 24
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The shorten URL is mapped to a &lt;code&gt;shorten.go&lt;/code&gt; file. We initially validate the request by performing the following checks&lt;/p&gt;

&lt;h3&gt;
  
  
  Rate Limiting
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    r2 := database.CreateClient(1)
    defer r2.Close()

    val, err := r2.Get(database.Ctx, ctx.IP()).Result()
    limit, _ := r2.TTL(database.Ctx, ctx.IP()).Result()

    if err == redis.Nil {
    // Set quota for the current IP Address
        _ = r2.Set(database.Ctx, ctx.IP(), os.Getenv("API_QUOTA"), 30*60*time.Second).Err()
    } else if err == nil {
        valInt, _ := strconv.Atoi(val)
    // If Quota has been exhausted
        if valInt &amp;lt;= 0 {
            return ctx.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{
                "error":            "Rate limit exceeded",
                "rate_limit_reset": limit / time.Nanosecond / time.Minute,
            })
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use Redis-0 for storing URL Shorts and Redis-1 for managing IP Addresses &amp;amp; Rate limiting. Our Assumption is that the same IP Address will not be allowed to hit the /shorten end-point more than 10 times (default value in &lt;code&gt;.env&lt;/code&gt;)&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating the Base-62 short or store the custom-short
&lt;/h3&gt;

&lt;p&gt;We simply check the db for a collision before storing the generated / custom-short&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if body.CustomShort == "" {
        id = helpers.Base62Encode(rand.Uint64())
    } else {
        id = body.CustomShort
    }

    r := database.CreateClient(0)
    defer r.Close()

    val, _ = r.Get(database.Ctx, id).Result()

    if val != "" {
        return ctx.Status(fiber.StatusForbidden).JSON(fiber.Map{
            "error": "URL Custom short is already in use",
        })
    }

    if body.Expiry == 0 {
        body.Expiry = 24
    }

    err = r.Set(database.Ctx, id, body.URL, body.Expiry*3600*time.Second).Err()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After generating the custom-short, we simply return the response using the struct defined above.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Resolve endpoint
&lt;/h2&gt;

&lt;p&gt;URL resolution is pretty straightforward. We simply check for the path param against our keys in redis and redirect to the original value (URL) using a &lt;code&gt;HTTP 301 Redirect&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func Resolve(ctx *fiber.Ctx) error {
    url := ctx.Params("url")

    r := database.CreateClient(0)
    defer r.Close()

    value, err := r.Get(database.Ctx, url).Result()
    if err == redis.Nil {
        return ctx.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "short-url not found in db"})
    } else if err != nil {
        return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal error"})
    }

    return ctx.Redirect(value, 301)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it. We now have a fast working URL Shortener up and running. Check out the complete source code &lt;a href="https://github.com/mahadevans87/short-url" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Do let me know your thoughts and feeback :)&lt;/p&gt;

</description>
      <category>go</category>
      <category>redis</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Mock REST APIs with Mockatron</title>
      <dc:creator>Mahadevan Sreenivasan</dc:creator>
      <pubDate>Sun, 20 Mar 2022 05:42:37 +0000</pubDate>
      <link>https://dev.to/mahadevans87/mock-rest-apis-with-mockatron-22nk</link>
      <guid>https://dev.to/mahadevans87/mock-rest-apis-with-mockatron-22nk</guid>
      <description>&lt;h2&gt;
  
  
  Why Mock APIs?
&lt;/h2&gt;

&lt;p&gt;As a mobile / front-end developer, have you ever been blocked due to a dependency on a back-end API that is not readily available for consumption? This scenario is very frequent in fast paced development teams where multiple front-end and back-end teams work in parallel whilst striving to resolve dependencies with one another.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Mockatron
&lt;/h2&gt;

&lt;p&gt;I am building &lt;a href="https://www.npmjs.com/package/mockatron"&gt;Mockatron&lt;/a&gt; - a simple tool that helps front-end / mobile developers setup a quick Mock HTTP API Server without having to wait for actual back-end APIs to be available. &lt;/p&gt;

&lt;p&gt;The goal is to help parallelize development efforts of teams so that once the actual APIs are available, consumers can simply switch to the original implementation without having to make a whole lot of changes.&lt;/p&gt;

&lt;p&gt;By defining a simple set of configuration rules, you can stand up a portable mock HTTP server that can return **static **as well as **dynamic **response data. Some of the features that are offered today include: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Handlebars style templates to support dynamic generation of responses from object templates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Proxy support to mock specific routes and redirect others to your actual back-end server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Support for static responses (e.g plain old JSON data).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;Let us assume that we are going to build a mobile / web application like Amazon that will list products from a back-end API. Our goal is to use Mockatron and setup a mock API server that will return mock products each time a call to &lt;code&gt;/api/products&lt;/code&gt; is called from the front-end / mobile app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install Mockatron
&lt;/h3&gt;

&lt;p&gt;Pre-requisities:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Make sure that you have Node and npm installed.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ npm install -g mockatron&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup your mock definitions
&lt;/h3&gt;

&lt;p&gt;Create an empty folder for us to configure our mock server. Give it an arbitrary name (e.g products). &lt;/p&gt;

&lt;p&gt;Inside the &lt;code&gt;products&lt;/code&gt; folder, create a configuration folder named &lt;code&gt;config&lt;/code&gt;. We will be configuring our mock end points and constraints (more on this later) in this folder. &lt;/p&gt;

&lt;p&gt;Inside the &lt;code&gt;config&lt;/code&gt; folder, create a &lt;strong&gt;main.json&lt;/strong&gt; file. Here is my folder structure that I have created&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;products
└── config
    ├── main.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let us start by writing a simple config in our &lt;code&gt;main.json&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
     "contextPath": "/api", 
     "routes": [ 
         { 
             "path": "/products", 
             "method": "GET", 
             "responses": [ 
                 { 
                     "body": "'no results found'", 
                     "statusCode": 200 
                 } 
             ] 
         } 
     ] 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above configuration should be straightforward. The &lt;code&gt;contextPath&lt;/code&gt; key specifies where the defined routes will be mounted while running our mock server. For e.g if we are hosting our mock server at &lt;code&gt;localhost:8080&lt;/code&gt;, &lt;code&gt;http://localhost:8080/api/products&lt;/code&gt; would match the &lt;code&gt;/products&lt;/code&gt; route that we have defined above.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;responses&lt;/code&gt; array specifies the different response bodies that this route can potentially return. In this case, we are going to return a simple string body with a status code of 200.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generate a mock server
&lt;/h3&gt;

&lt;p&gt;Now that we have setup our configuration, go ahead and run the below command inside the &lt;code&gt;products&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mockatron --config config --out output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything went well without errors, an output directory will be created in the specified path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run the mock server
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;cd&lt;/code&gt; into the &lt;code&gt;output&lt;/code&gt; path&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$ npm i&lt;/code&gt; - This will install all dependencies for the mock server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$ npm start&lt;/code&gt; - This will run a mock server on port &lt;code&gt;8080&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ideally, you should see the following messages in &lt;code&gt;stdout&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; mockatron_server@1.0.0 start
&amp;gt; node index.js

Started application on port 8080

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open a tab in your browser or send a &lt;code&gt;curl&lt;/code&gt; request to &lt;code&gt;http://localhost:8080/api/products&lt;/code&gt; and you should get &lt;code&gt;no results found&lt;/code&gt; as the response with a status code of 200.&lt;/p&gt;

&lt;p&gt;And that's it! We have a mock API server up and running in less than 10 lines of code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic responses
&lt;/h3&gt;

&lt;p&gt;The above config returns the same static text every time we hit /api/products. However, most of the time while building a front-end or mobile app, we would need to generate variability in our responses to simulate a real world scenario. To achieve this go back to the configuration folder  and create another file named &lt;code&gt;products.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;products
└── config
    ├── main.json
    ├── products.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the &lt;code&gt;products.json&lt;/code&gt; file, we will try to create a template for our response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "productList": [
            {{#array 5 20}}
            {
                "price": "{{float 50.0 5000.0}}",
                "rating": {{int 1 5}},
                "id": {{@index}},
                "sku": "{{uuid}}",
                "name": "{{word 2}}"
            }
            {{/array}}
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will use the &lt;code&gt;productList&lt;/code&gt; object to return an array of products based on the template that we have defined above. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;#array&lt;/code&gt; helper generates a random number of objects (between 5 &amp;amp; 20) that it encompasses. The array is closed out with the &lt;code&gt;{{/array}}&lt;/code&gt; tag.&lt;/p&gt;

&lt;p&gt;Each object in the array can again be randomized. The &lt;code&gt;float&lt;/code&gt; and &lt;code&gt;int&lt;/code&gt; helpers take min and max arguments and generate a random number in between. Similarly there are helpers for generating a random &lt;code&gt;uuid&lt;/code&gt;, random text using the &lt;code&gt;word&lt;/code&gt; helper and so on. A complete list of response helpers can be found &lt;a href="https://github.com/mahadevans87/mockatron#available-response-helpers"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To hook up the &lt;code&gt;productList&lt;/code&gt; &lt;strong&gt;definition&lt;/strong&gt; array to our &lt;code&gt;/api/products&lt;/code&gt; route, head back to our &lt;code&gt;main.json&lt;/code&gt; file and modify the existing config like so.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
     "contextPath": "/api", 
     "routes": [ 
         { 
             "path": "/products", 
             "method": "GET", 
             "responses": [ 
                 { 
                     "body": "{{{def 'products' 'productList'}}}", 
                     "statusCode": 200 
                 } 
             ] 
         } 
     ] 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have only changed the &lt;code&gt;body&lt;/code&gt; to load the &lt;code&gt;productList&lt;/code&gt; definition that we defined in the &lt;code&gt;products.json&lt;/code&gt; file instead of static text. For this, we use the &lt;code&gt;{{{def &amp;lt;json-file-name&amp;gt; &amp;lt;definition&amp;gt;&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Now, lets rebuild our mock server. Go back to the root folder (the &lt;code&gt;products&lt;/code&gt; directory) and run the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mockatron --config config --out output

$ cd output

$ npm i &amp;amp;&amp;amp; npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now open a browser and try hitting &lt;code&gt;http://localhost:8080/api/products&lt;/code&gt; multiple times. You will see that you get a different response each time!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ll6g8FiS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v4ysjp1c5csy1o7x9ifz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ll6g8FiS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v4ysjp1c5csy1o7x9ifz.gif" alt="Mockatron rendering dynamic responses" width="580" height="920"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Constraints to Routes
&lt;/h3&gt;

&lt;p&gt;Now that we are able to generate dynamic response data, let us look at adding constraints that determine when the response should be generated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mahadevans87/mockatron#available-constraint-helpers"&gt;Constraints&lt;/a&gt; are synonymous to &lt;code&gt;if (condition) then return response&lt;/code&gt; in Mockatron. Think of a constraint as logical blocks that we will put inside an &lt;code&gt;if&lt;/code&gt; block.&lt;/p&gt;

&lt;p&gt;Constraints can be added to each element of the &lt;code&gt;responses&lt;/code&gt; key inside a &lt;code&gt;route&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;As an example, assume that the products API that we are building mandates a &lt;code&gt;search&lt;/code&gt; query parameter in the URL and we should return a response only if the &lt;code&gt;search&lt;/code&gt; query parameter is not null.&lt;/p&gt;

&lt;p&gt;Let's head back to our &lt;code&gt;main.json&lt;/code&gt; file and add the above rule as a constraint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "contextPath": "/api",
  "routes": [
    {
      "path": "/products",
      "method": "GET",
      "responses": [
        {
          "constraint": "{{neq (query 'search') undefined}}",
          "body": "{{{def 'products' 'productList'}}}",
          "statusCode": 200
        },
        {
          "body": "'No Results found'",
          "statusCode": 200
        }
      ]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We see that the &lt;code&gt;responses&lt;/code&gt; array now contains 2 elements. The first element contains a &lt;code&gt;constraint&lt;/code&gt; key that evaluates if the incoming request's query parameter - &lt;code&gt;search !== undefined&lt;/code&gt;. Only if this condition is satisfied, will the body get executed. &lt;/p&gt;

&lt;p&gt;Otherwise, Mockatron will fall back to the next object in the &lt;code&gt;responses&lt;/code&gt; array which basically returns a static string like before.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remember: Contents in the &lt;code&gt;responses&lt;/code&gt; array are evaluated in the order in which they are listed in the configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L5DHdi33--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g5wv6xiz0hymr87q8njj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L5DHdi33--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g5wv6xiz0hymr87q8njj.gif" alt="Mockatron response with constraints" width="672" height="920"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A full list of Mockatron constraint helpers are available &lt;a href="https://github.com/mahadevans87/mockatron#available-constraint-helpers"&gt;here &lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nesting Constraints
&lt;/h3&gt;

&lt;p&gt;We can nest multiple constraints into a single constraint. For e.g what if we want to return the response only if the search query param is not null and the price param &amp;gt; 0?  &lt;/p&gt;

&lt;p&gt;Modify the &lt;code&gt;constraint&lt;/code&gt; section like so.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"constraint": "{{and 
                    (neq (query 'search') undefined)
                    (gt (query 'price') 0) 
               }}",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Static Response Data
&lt;/h3&gt;

&lt;p&gt;Sometimes we may not want to deal with templating or needing dynamic response data for all our routes and a simple plain JSON object would be sufficient. To return static JSON content as response, use the &lt;code&gt;{{file &amp;lt;json_file_name&amp;gt;}}&lt;/code&gt; helper instead of the &lt;code&gt;{{def}}&lt;/code&gt; helper that we were using till now.&lt;/p&gt;

&lt;p&gt;Let's assume that we add a new route called &lt;code&gt;/static-product&lt;/code&gt; to our list of routes. In main.json, lets add the following configuration to the existing &lt;code&gt;routes&lt;/code&gt; array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "path": "/static-product",
  "method": "GET",
  "responses": [
     {
       "statusCode": 200,
       "body": "{{file 'static-products'}}"
     }
   ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All you need is a valid JSON response defined in a file named &lt;code&gt;static-products.json&lt;/code&gt; in the &lt;code&gt;config&lt;/code&gt; folder alongside the &lt;code&gt;main.json&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's next
&lt;/h3&gt;

&lt;p&gt;Try adding multiple routes, responses and constraints and see what works for you. If you want to report a bug / discuss a new feature request, reach out to me / raise an issue in Github. &lt;/p&gt;

&lt;p&gt;In the next post, I will talk about configuring proxy support in Mockatron. This could be really powerful if you want to use Mockatron as a proxy server that will mock only specific routes while proxying other requests to your actual back-end APIs. Stay tuned!&lt;/p&gt;

</description>
      <category>api</category>
      <category>productivity</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
