{"componentChunkName":"component---src-pages-author-author-yaml-id-js","path":"/author/chris-yee/","result":{"data":{"allMarkdownRemark":{"edges":[{"node":{"id":"ac280a24-9415-5830-99fb-1ea29922fdc0","html":"<p>Web authentication or WebAuthn is a new standard for authentication published by the World Wide Web Consortium and supported by the <a href=\"https://fidoalliance.org/fido2/fido2-web-authentication-webauthn/\">FIDO alliance</a>. The standard works by providing a way for users to authenticate through a third-party authenticator. These authenticators can be built into the operating system software, like Windows Hello or Android biometrics, or through external authenticators, like a USB authenticator.</p>\n<p>This article will cover a high-level description of how WebAuthn works and two different ways an application can be set up to use the WebAuthn standard to authenticate users. One is through a basic registration flow where the authenticator data is stored upon registration and must be used to log in. The other is a flow where the authenticator can be used as a second-factor authentication method, prompted after a user logs in. For more information on this, resources like the <a href=\"https://www.w3.org/TR/webauthn-2/\"><code>World Wide Web Consortium documentation</code></a> can be useful.</p>\n<h2 id=\"webauthn-introduction\" style=\"position:relative;\"><a href=\"#webauthn-introduction\" aria-label=\"webauthn introduction permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>WebAuthn Introduction</h2>\n<p>The WebAuthn authentication process involves the interaction between three entities: The relying party, which is the server that handles the credentials of the authenticator and verifies it, a web browser that should support the <a href=\"/website-authentication-protocols/\">web authentication protocol</a>, and the authenticator itself.</p>\n<p>The registration process begins when the client generates some sort of identifying information about themselves. Typically, this would be a user name or an email from the user but could be another identifier sent from the client, such as a randomly generated GUID. Using the Web Authentication APIs with JavaScript, the client can request the relying party to register the authenticator credentials.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 48.92307692307692%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAACiklEQVQoz2PgK1jBgIz////P+OnrN4Yf374kf/369fbtW7fO3Lh58+Lde/cuv3n16syXb99vH77xJBeojsG2ewcPun4QwQfEIAlegaKV/OJlaziF8xYy6bXvFLvz57/IsRuP9D+8eGL94vo5qU1HzpluePJfRLttpyBD0hxmoHpukD6ofpA5LCAD2XmBWKBwJTuD7xRWBgYGRgaGJQwMVtlyDPbpGqqh+bYRxQ1ObBaBqlrxNVYMNqlaDB6lMnabfzFx5yxiA+mHYg4gZmLgyV/BwJK9lEGveTPDiuM3OTu2nBcCeVstc3K9dvXqn6aN617p1659a9a04Z1584aXSmUrfkrGd6VI5sxnUM+dyYjhZQOgQamLjjKcuPWMYcLOi2r9Oy9b/fjyQS24bt5S2/rV/52b1/0P69/6379r03+npnX/QWJmWRPKLMsXMThULmLmRTewd/tFhsVHbzGsPHmHYfLuywxrzz1mqFi0k4EhoN6FPWlKE0/ypFre5Ekl7NGdVdzxvU1sSVNqmQOrTZmiOhm4MuYyg7zJAzSIF5uBlWtOMYJiz7VvOzeDZAPPnSunGQ4dPCTVc+g+Q/fRpwy79h6UBckzCDVxuvTtYOXOXy4AigxeZAORAQ802YA0Hb3xKOLVx6/L7z14OOfE6bMLb9y+O+fdh48zPn75tvD5qzd2r99/Yrj08BWTYN5qBn6gPv5CIC5YiWogyJb7L98z3nz6luH/nx/qf37/ivn5/VvA8esPay/df17x/+9v/x8/fsR++fxZ6dfPHyDLga5aCXYZPzYXggQevv7AcOfFO6Ar/zD8+PkLqOkfAwNnsSADgwrH3VcfGP7/+8Pw7esXBqDBUANRIwUA9FBqAVFdUwgAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"WebAuthn Flow\"\n        title=\"WebAuthn Flow\"\n        src=\"/static/4e86f815c05935f7d4f29313348d03eb/e5715/webauthn-flow.png\"\n        srcset=\"/static/4e86f815c05935f7d4f29313348d03eb/a6d36/webauthn-flow.png 650w,\n/static/4e86f815c05935f7d4f29313348d03eb/e5715/webauthn-flow.png 768w,\n/static/4e86f815c05935f7d4f29313348d03eb/0f246/webauthn-flow.png 1118w\"\n        sizes=\"(max-width: 768px) 100vw, 768px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n    </span></p>\n<p>After receiving the request from the client, the relying party generates a challenge based on the options provided by the client. The client receives the response and attempts to get the user to verify the authenticator being used. For example, with Windows Hello as the authenticator, the user can enter their PIN. After verifying the authenticator, it signs the challenge-response back with identifying details and returns it to the relying party, which validates the authenticator and registers the user.</p>\n<p>When the registration begins, options are set from the relying party that changes how the flow will work. You can find a list of the options in this document on <a href=\"https://www.w3.org/TR/webauthn/#dictionary-makecredentialoptions\">W3C</a>. Here are some examples of the significant options that one might set for their flows:</p>\n<ul>\n<li><strong>Authenticator Attachment</strong> - The authenticator being used can be set to a platform authenticator or a cross-platform authenticator. Platform authenticators are limited to the usage of a single device to authenticate. Windows Hello and the fingerprint scanner on a smartphone are examples of platform authenticators, as the user is identified through the device being used to authenticate with. Cross-platform authenticators are external devices that can be used with a device to authenticate. Things like USB authenticators are an example of cross-platform authenticators. Depending on the implementation, USB authenticators can also set up multiple platform authenticators to work on their device, but this won't be covered in this blog.</li>\n<li><strong>Resident Key Required Boolean</strong> - By default, this value is set to false, which means that private key and authentication credentials are stored on the relying party server. However, if this option is set to true, the authenticator will store the private key credentials instead. With certain flows, by setting the resident key to required, only the authenticator's ids will be stored on the relying party server. This would allow for authentication systems to validate a user via the authenticator and identify them.</li>\n<li><strong>User Verification</strong> - This field can hold three values: Preferred by default, Discouraged, or Required. User verification is when the user can be identified to be one that is attempting the authentication request. If an authenticator supports user verification, the authenticator must set a flag during validation and sent to the relying party. When the authenticator attempts verification, it can be done through different means, such as password, PIN, or biometrics.</li>\n<li><strong>Attestation</strong> - Attestation certificates have the option to be created through this option. As a result, the certificate will contain details about the hardware being used as an authenticator and ensure that the device used for authentication is trustworthy. If the authentication flow does not require this, you can set it to None to reduce the workload done by the relying party to validate the attestation certificate. By setting the option to Indirect, the relying party allows the attestation certificate to be generated by a third party. This could allow the relying party to receive less private information surrounding the actual authenticator and use only what is needed. Lastly, if set to direct, the authenticator itself must generate the attestation certificate to be verified.</li>\n</ul>\n<p>The authentication or login process is similar to the registration process. Using the <a href=\"/write-a-javascript-library-using-webpack-and-babel/\">JavaScript libraries</a>, the authenticator passes identifying information to the relying party. The relying party returns a challenge, which should be validated by the user, processed by the authenticator, and finally validated by the relying party. User verification is checked from the options, but a majority of the options have been reduced as no credentials are generated or saved on either end.</p>\n<h2 id=\"setting-up-webauthn-for-your-application\" style=\"position:relative;\"><a href=\"#setting-up-webauthn-for-your-application\" aria-label=\"setting up webauthn for your application permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Setting up WebAuthn for your Application</h2>\n<p>When setting up WebAuthn as an authentication method for your application, two considerations are to use the standard as a primary registration and authentication method or as a second-factor authentication method.</p>\n<p>As a primary authentication method, the authenticator identification and credentials are stored by the server at the same time the user account is created.</p>\n<p>To begin, use the Web Authentication API to initiate the flow:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"javascript\" data-index=\"0\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk12\">Let</span><span class=\"mtk1\"> </span><span class=\"mtk12\">publicKeyCredentialCreationOptions</span><span class=\"mtk1\"> = {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    </span><span class=\"mtk12\">user:</span><span class=\"mtk1\"> {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">        </span><span class=\"mtk12\">id:</span><span class=\"mtk1\"> “</span><span class=\"mtk7\">1234</span><span class=\"mtk1\">”,</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">        </span><span class=\"mtk12\">name:</span><span class=\"mtk1\"> </span><span class=\"mtk8\">&quot;example@example.com&quot;</span><span class=\"mtk1\">,</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">        </span><span class=\"mtk12\">displayName:</span><span class=\"mtk1\"> </span><span class=\"mtk8\">&quot;example&quot;</span><span class=\"mtk1\">,</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    },</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    </span><span class=\"mtk12\">authenticatorSelection:</span><span class=\"mtk1\"> {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    </span><span class=\"mtk12\">authenticatorAttachment:</span><span class=\"mtk1\"> </span><span class=\"mtk8\">&quot;platform&quot;</span><span class=\"mtk1\">,</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    },</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    </span><span class=\"mtk12\">attestation:</span><span class=\"mtk1\"> </span><span class=\"mtk8\">&quot;direct&quot;</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">};</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk4\">const</span><span class=\"mtk1\"> </span><span class=\"mtk12\">credential</span><span class=\"mtk1\"> = </span><span class=\"mtk15\">await</span><span class=\"mtk1\"> </span><span class=\"mtk12\">navigator</span><span class=\"mtk1\">.</span><span class=\"mtk12\">credentials</span><span class=\"mtk1\">.</span><span class=\"mtk11\">create</span><span class=\"mtk1\">({</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    </span><span class=\"mtk12\">publicKey:</span><span class=\"mtk1\"> </span><span class=\"mtk12\">publicKeyCredentialCreationOptions</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">});</span></span></code></pre>\n<p>For this section, the creation options are generated by the client. In other scenarios, including this blog's example, the options are generated from the relying party by retrieving them via an API call.</p>\n<p>After the browser triggers this code, the authenticator will be prompted for validation. Since the authenticatorAttachment is set to 'platform', it would use the device's authenticator, like Windows Hello or an Android fingerprint scan.</p>\n<p>The data returned from the creation of the credentials will have a structure like this:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"javascript\" data-index=\"1\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk12\">PublicKeyCredential</span><span class=\"mtk1\"> {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    id: </span><span class=\"mtk8\">&#39;ADSUllKQmbqdGtpu4sjseh4cg2TxSvrbcHDTBsv4NSSX9...&#39;</span><span class=\"mtk1\">,</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    rawId: </span><span class=\"mtk10\">ArrayBuffer</span><span class=\"mtk1\">(</span><span class=\"mtk7\">59</span><span class=\"mtk1\">),</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    response: </span><span class=\"mtk12\">AuthenticatorAttestationResponse</span><span class=\"mtk1\"> {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">        clientDataJSON: </span><span class=\"mtk10\">ArrayBuffer</span><span class=\"mtk1\">(</span><span class=\"mtk7\">121</span><span class=\"mtk1\">),</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">        attestationObject: </span><span class=\"mtk10\">ArrayBuffer</span><span class=\"mtk1\">(</span><span class=\"mtk7\">306</span><span class=\"mtk1\">),</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    },</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    type: </span><span class=\"mtk8\">&#39;public-key&#39;</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">}</span></span></code></pre>\n<p>This object should be passed to the relying party to complete the registration. The relying party verifies that the credentials passed in are correct and stores the identifier and credentials in the data store. In addition to storing the credentials object, any other registration variables should be passed in this step to register on your data store.</p>\n<p>To use this flow as part of a second-factor authentication process, the user account should be created before initiating this process with an already existing authentication method, like a <a href=\"/password-security-best-practices-compliance/\">password system</a>. After the user account is created, running this flow should update the current user in the data store with the credential data. This will allow the second-factor authentication credentials to be configured and triggered after a password login.</p>\n<p>Most programming languages have a library built that supports WebAuthn for a relying party server. For example, Duo has created them for <a href=\"https://github.com/duo-labs/py_webauthn\"><code>Python</code></a> and <a href=\"https://github.com/duo-labs/webauthn\"><code>Go</code></a>. Using one of these libraries during the development of the relying party server will simplify the process. These resources also provide demos on how to implement their libraries to handle the credential data generated by the JavaScript Web Authentication APIs.</p>\n<h2 id=\"conclusion\" style=\"position:relative;\"><a href=\"#conclusion\" aria-label=\"conclusion permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Conclusion</h2>\n<p>Although the usage of WebAuthn is not widespread at the moment, the potential to <a href=\"https://www.loginradius.com/blog/identity/2019/10/passwordless-authentication-the-future-of-identity-and-security/\">authenticate a user passwordless</a> or with the extra security of an authenticator allows it to be a strong contender in the future over regular password authentication flows. Hopefully, this blog helps you get started on your authentication apps with WebAuthn.</p>\n<style class=\"grvsc-styles\">\n  .grvsc-container {\n    overflow: auto;\n    -webkit-overflow-scrolling: touch;\n    padding-top: 1rem;\n    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));\n    padding-bottom: 1rem;\n    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));\n    border-radius: 8px;\n    border-radius: var(--grvsc-border-radius, 8px);\n    font-feature-settings: normal;\n  }\n  \n  .grvsc-code {\n    display: inline-block;\n    min-width: 100%;\n  }\n  \n  .grvsc-line {\n    display: inline-block;\n    box-sizing: border-box;\n    width: 100%;\n    padding-left: 1.5rem;\n    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));\n    padding-right: 1.5rem;\n    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));\n  }\n  \n  .grvsc-line-highlighted {\n    background-color: var(--grvsc-line-highlighted-background-color, transparent);\n    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, transparent);\n  }\n  \n  .dark-default-dark {\n    background-color: #1E1E1E;\n    color: #D4D4D4;\n  }\n  .dark-default-dark .mtk12 { color: #9CDCFE; }\n  .dark-default-dark .mtk1 { color: #D4D4D4; }\n  .dark-default-dark .mtk7 { color: #B5CEA8; }\n  .dark-default-dark .mtk8 { color: #CE9178; }\n  .dark-default-dark .mtk4 { color: #569CD6; }\n  .dark-default-dark .mtk15 { color: #C586C0; }\n  .dark-default-dark .mtk11 { color: #DCDCAA; }\n  .dark-default-dark .mtk10 { color: #4EC9B0; }\n</style>","frontmatter":{"title":"WebAuthn: A Guide To Authenticate Your Application","author":{"id":"Chris Yee","github":null,"avatar":null},"date":"December 09, 2020","updated_date":null,"tags":["WebAuthn","FIDO","Authentication"],"coverImage":{"childImageSharp":{"fluid":{"aspectRatio":1.5037593984962405,"src":"/static/07ab3c5a163821d532aedbc23c7a48be/ee604/webauthn-logo.png","srcSet":"/static/07ab3c5a163821d532aedbc23c7a48be/69585/webauthn-logo.png 200w,\n/static/07ab3c5a163821d532aedbc23c7a48be/497c6/webauthn-logo.png 400w,\n/static/07ab3c5a163821d532aedbc23c7a48be/ee604/webauthn-logo.png 800w,\n/static/07ab3c5a163821d532aedbc23c7a48be/f3583/webauthn-logo.png 1200w","sizes":"(max-width: 800px) 100vw, 800px"}}}},"fields":{"authorId":"Chris Yee","slug":"/engineering/webauthn-authentication-application/"}}},{"node":{"id":"51e17045-40a3-54bd-a1d8-28b66237ca68","html":"<h2 id=\"introduction\" style=\"position:relative;\"><a href=\"#introduction\" aria-label=\"introduction permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Introduction</h2>\n<p>Redis's usage as both a temporary data store and even as a permanent data store is increasing today, as memory has started becoming much cheaper. Redis' quick memory-based CRUD operations allow database interactions to happen significantly faster than interactions with other databases that use drive-based storage. This blog is about three different methods to interact with your Redis data.</p>\n<p>This blog will require having access to a Redis instance. Guides can be found to set up a local Redis instance for Windows <a href=\"https://redislabs.com/blog/redis-on-windows-10/\">here</a> and for UNIX based machines like Mac or other Linux distros, it can be found <a href=\"https://redis.io/topics/quickstart\">here</a>. Alternatively, you can use a remote instance, but be aware that some of the commands in this blog may delete or modify the data.</p>\n<h2 id=\"redis-insight\" style=\"position:relative;\"><a href=\"#redis-insight\" aria-label=\"redis insight permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Redis Insight</h2>\n<p>For users that do not like working with command-line interfaces, the first tool introduced in this blog is a GUI interface called RedisInsight. This can be found <a href=\"https://redislabs.com/redis-enterprise/redis-insight/\">here</a>. After filling in their download form and installing, running the application will start a server on the localhost, which you can access with a browser.</p>\n<p>After installing and running the application, you will add the connection information for your Redis instance. If running a local instance of Redis, the connection information in the image will typically suffice. If connecting to a remote instance, you might need to fill in some additional details for TLS, such as connection certificates.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 80.46153846153847%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAABhklEQVQ4y6VU226CQBTk/7+gj/2GPtrXGhIrAmpSLwgSE6NWRBFElst0zyIENKWYnmSynOXs7OwsHOnl9Q2K0sdHtwtZltHlY6/3icVi0QijgHHLDQP9vgJJlrsIggCO4+BwOAi4rivmWsP3EYYhTNOCNBioEJFlqEZ2lzdFUTsejyGpqoqIJVzZsSRljMHzPFHYBkmSiHW6rpPCgUhostiJxjRNa3kTqJZiOBzmR2ZxCu90Lo9A5OSlz715mlCpKGyz+E9C8jDmCoNzUCqMokgobLNB4TnFaDT63cN/KBwgSTNstg784FKS03dVXMxzHiq5wmKyepS2ah9umdbv9i4u4bUkJx8LkEfF8/V65Z7Htc3uCHOFdFzG4lrRPZKkklPNDTRfu+VLyLD73vNv8SRUNN3ozYg6spxQ0zRImqa3/md3+yPfMMYlAjZuiv0pw5aPjpcTznnHkTqdd6zXa5iWheVyyWE/wLZtWPzdZDrjrcrkbWuJr5mFCQeN07mJ1WrF26CCHy+V0P4EYqRsAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Connect\"\n        title=\"Connect\"\n        src=\"/static/e39b9b677a5c9b82b15bbc694fc376d2/e5715/connect.png\"\n        srcset=\"/static/e39b9b677a5c9b82b15bbc694fc376d2/a6d36/connect.png 650w,\n/static/e39b9b677a5c9b82b15bbc694fc376d2/e5715/connect.png 768w,\n/static/e39b9b677a5c9b82b15bbc694fc376d2/1d553/connect.png 1159w\"\n        sizes=\"(max-width: 768px) 100vw, 768px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n    </span></p>\n<p>When connected to your Redis instance, the dashboard UI appears. Clicking the Browser sidebar tab will display all existing keys in your database.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 64%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAABg0lEQVQ4y62S3UrEMBCF8zii7l8VxBsR9EbfUPGq+7++i+C1i6i4iu5227TZpmmTHKdZlYKuuOLQj9DJ5EzOEHZ0dILD41Ps7R9gc7MGz9tFq+nBa+04Wi3vC41GE/V6w9Vt12uob1BtbQ9b21tgk/ENniYTBEGAMOJ4nYV4fpkh4jHmIYcQiy8kQiBJxOd/GIXgMUdE59lrwGEsQB9tSnCeQGYKYiGhCovCYCW5XlLNsVkooLSFVBpRQt2oIxep62ythTHmW7TWUCqvoJBKBRaTSLmpqehuFuGepyQqkVPrghr9HkOODNiUZpaRxTJBV0IZKi+Q5WZNrIPxWLgb5oUmK9bZzMhCWVTO8Pe83zCYc0iZkah1tsuoCq4rzF6mc2dZm6VdZ1kVfxcsyOrH3G4fnjCNBNJSpDKftQTdM6AZLtIM1+NHXI0neJjG9BYLLGSOlNYqZe4nGP452Nn5BXzfR6fbQ7vbR7vTg9/p0rqkPxihPxxhMLxEbzCE317m/RW8AWsht8xL0ZiyAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Browser\"\n        title=\"Browser\"\n        src=\"/static/e4e72b247e376fc38654a8e9ea97b8cc/e5715/browser.png\"\n        srcset=\"/static/e4e72b247e376fc38654a8e9ea97b8cc/a6d36/browser.png 650w,\n/static/e4e72b247e376fc38654a8e9ea97b8cc/e5715/browser.png 768w,\n/static/e4e72b247e376fc38654a8e9ea97b8cc/1df5b/browser.png 1999w\"\n        sizes=\"(max-width: 768px) 100vw, 768px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n    </span></p>\n<p>From here, CRUD operations are relatively straightforward. Key-value pair creation can be done by using the ADD KEY button. This will prompt for the type of key-value pair to be added (Hash, String, JSON, etc.) and the data that will be stored with the key.</p>\n<p>If a specific key is required to be queried, fill in the field next to the blue filter icon with search criteria, then click the icon. It will prompt the type of data stored with the key, and the results will be displayed.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 64%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAABeklEQVQ4y62S20rDQBCG93GkNdFGoXgjQr3RN1S8Ss/1XQSvLYKlqbRpmmzOm9PvZGMlF6204pKP3cwO//wzLOt07nBze4/21TUaDQWadonWuQatdSFptbSdqOqZzFNUBeoJ5SptNE+bYMb0DQvDgGVZsG2O1drG53INh7vY0L/vBzvxPP/nbDs2uMvhOBxsZXHkBUAf/CAC5x6iWMizSAukOfaSZBX1GFvbPkRWIBIZ1o4L2wvA/VBWLooCeZ7vJMsyCJHUEAgjAeaSQHmZkc257eGDh3DIXUKlUyp0ODl1lIOZNLOYWiwD25UkKeIkP5JCwrjrS4dJWrks24yphTKpnOHhfDu0NhxRFJNoQYKVy7rgscJsaW5ky6W77RIi/btgSq1KEZrb+2wB0/ERliK1+RwlKJ8BzTAIY7xO53iZGpiZLr3FFEGUIKS9Thn7DYZ/Xuzh8Qm6rqPXH6DbH6LbG0Dv9WmvGI4mGI4nGI2fMRiNoXeruL6HL5SRt9nF32tXAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Filter\"\n        title=\"Filter\"\n        src=\"/static/53a06909b443c049fd05525a7800f50e/e5715/filter.png\"\n        srcset=\"/static/53a06909b443c049fd05525a7800f50e/a6d36/filter.png 650w,\n/static/53a06909b443c049fd05525a7800f50e/e5715/filter.png 768w,\n/static/53a06909b443c049fd05525a7800f50e/1df5b/filter.png 1999w\"\n        sizes=\"(max-width: 768px) 100vw, 768px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n    </span></p>\n<p>Finally, keys can be deleted by clicking the trash can icon on the right side.</p>\n<h2 id=\"redis-cli\" style=\"position:relative;\"><a href=\"#redis-cli\" aria-label=\"redis cli permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Redis CLI</h2>\n<p>The Redis command-line interface is a more common approach for developers to interact with data from a Redis store. If you followed the steps at the beginning of the blog, the CLI should already be installed. To do a ping test to the server, type the following command in the terminal:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"console\" data-index=\"0\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">chris@LR-ENG-17:~$ redis-cli -h localhost -p 6379 ping</span>\n<span class=\"grvsc-line\">PONG</span></code></pre>\n<p>And to connect, run the previous command without pinging:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"console\" data-index=\"1\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">chris@LR-ENG-17:~$ redis-cli -h localhost -p 6379</span>\n<span class=\"grvsc-line\">localhost:6379&gt;</span></code></pre>\n<p>The basic command to get a key-value pair is to call get with the key:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"console\" data-index=\"2\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">localhost:6379&gt; get key:1</span>\n<span class=\"grvsc-line\">&quot;value1&quot;</span></code></pre>\n<p>Setting a value can be done with the set command:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"console\" data-index=\"3\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">localhost:6379&gt; set key:1 value1new</span>\n<span class=\"grvsc-line\">OK</span>\n<span class=\"grvsc-line\">localhost:6379&gt; get key:1</span>\n<span class=\"grvsc-line\">&quot;value1new&quot;</span></code></pre>\n<p>Creating a value can also be done in the same way:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"console\" data-index=\"4\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">localhost:6379&gt; get key:3</span>\n<span class=\"grvsc-line\">(nil)</span>\n<span class=\"grvsc-line\">localhost:6379&gt; set key:3 value3new</span>\n<span class=\"grvsc-line\">OK</span>\n<span class=\"grvsc-line\">localhost:6379&gt; get key:3</span>\n<span class=\"grvsc-line\">&quot;value3new&quot;</span></code></pre>\n<p>And deletion can be done with the del command:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"console\" data-index=\"5\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">localhost:6379&gt; del key:3</span>\n<span class=\"grvsc-line\">(integer) 1</span>\n<span class=\"grvsc-line\">localhost:6379&gt; get key:3</span>\n<span class=\"grvsc-line\">(nil)</span>\n<span class=\"grvsc-line\">localhost:6379&gt;</span></code></pre>\n<p>This is only the simple basics of the Redis CLI. You can do more advanced operations through the CLI, such as the flushall command, which wipes your database. It’s a powerful tool, so be careful when experimenting with it. An extensive list of commands can be found <a href=\"https://redis.io/commands\">here</a>.</p>\n<h2 id=\"with-your-chosen-programming-language\" style=\"position:relative;\"><a href=\"#with-your-chosen-programming-language\" aria-label=\"with your chosen programming language permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>With Your Chosen Programming Language</h2>\n<p>With Redis’ widespread usage, almost all major industry programming languages have either community or official supported releases of libraries that can be used to interact with your database. In this section, I will be using Node.JS, and one of the support Redis libraries called <a href=\"https://github.com/NodeRedis/node-redis\">node-redis</a>.</p>\n<p>Initialization of the package in the code can be done by calling the following command after initializing Node in your directory:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"console\" data-index=\"6\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">npm install redis</span></code></pre>\n<p>When the process is finished, the <code>redis</code> module should be imported within your code, where you can set up the Redis connection and any options that need to be included:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"javascript\" data-index=\"7\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk4\">const</span><span class=\"mtk1\"> </span><span class=\"mtk12\">redis</span><span class=\"mtk1\"> = </span><span class=\"mtk11\">require</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&quot;redis&quot;</span><span class=\"mtk1\">)</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk4\">const</span><span class=\"mtk1\"> </span><span class=\"mtk12\">redisOption</span><span class=\"mtk1\"> = {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">  </span><span class=\"mtk12\">host:</span><span class=\"mtk1\"> </span><span class=\"mtk8\">&quot;127.0.0.1&quot;</span><span class=\"mtk1\">,</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">  </span><span class=\"mtk12\">port:</span><span class=\"mtk1\"> </span><span class=\"mtk8\">&quot;6379&quot;</span><span class=\"mtk1\">,</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">}</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk4\">const</span><span class=\"mtk1\"> </span><span class=\"mtk12\">client</span><span class=\"mtk1\"> = </span><span class=\"mtk12\">redis</span><span class=\"mtk1\">.</span><span class=\"mtk11\">createClient</span><span class=\"mtk1\">()</span></span></code></pre>\n<p>For an extensive list of options supported by the library for connection, check this <a href=\"https://github.com/NodeRedis/node-redis#API\">link</a>.</p>\n<p>After connecting, calling the <code>on</code> method on the client will allow the connection to open and run commands.</p>\n<p>The commands used by the library are a 1 to 1 mapping of the commands used by the Redis CLI, so they follow the same method calls. To update or create an entry in Redis, the most basic method is to use the set command:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"javascript\" data-index=\"8\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk12\">client</span><span class=\"mtk1\">.</span><span class=\"mtk11\">set</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&quot;key:3&quot;</span><span class=\"mtk1\">, </span><span class=\"mtk8\">&quot;value3new&quot;</span><span class=\"mtk1\">)</span></span></code></pre>\n<p>Getting a value can be done with the get method:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"javascript\" data-index=\"9\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk12\">client</span><span class=\"mtk1\">.</span><span class=\"mtk11\">get</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&quot;key:3&quot;</span><span class=\"mtk1\">)</span></span></code></pre>\n<p>Deleting a value can be done with the del method:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"javascript\" data-index=\"10\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk12\">client</span><span class=\"mtk1\">.</span><span class=\"mtk11\">del</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&quot;key:3&quot;</span><span class=\"mtk1\">)</span></span></code></pre>\n<p>The methods used by the client also support callbacks. For example, if the operations should be console logged, we can include a callback to <code>redis.print</code> after each method:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"javascript\" data-index=\"11\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk12\">client</span><span class=\"mtk1\">.</span><span class=\"mtk11\">set</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&quot;key:3&quot;</span><span class=\"mtk1\">, </span><span class=\"mtk8\">&quot;value3new&quot;</span><span class=\"mtk1\">, </span><span class=\"mtk12\">redis</span><span class=\"mtk1\">.</span><span class=\"mtk12\">print</span><span class=\"mtk1\">)</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk12\">client</span><span class=\"mtk1\">.</span><span class=\"mtk11\">get</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&quot;key:3&quot;</span><span class=\"mtk1\">, </span><span class=\"mtk12\">redis</span><span class=\"mtk1\">.</span><span class=\"mtk12\">print</span><span class=\"mtk1\">)</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk12\">client</span><span class=\"mtk1\">.</span><span class=\"mtk11\">del</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&quot;key:3&quot;</span><span class=\"mtk1\">, </span><span class=\"mtk12\">redis</span><span class=\"mtk1\">.</span><span class=\"mtk12\">print</span><span class=\"mtk1\">)</span></span></code></pre>\n<p>The output of this in the console would be:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"console\" data-index=\"12\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">Reply: OK</span>\n<span class=\"grvsc-line\">Reply: value3new</span>\n<span class=\"grvsc-line\">Reply: 1</span></code></pre>\n<h2 id=\"conclusion\" style=\"position:relative;\"><a href=\"#conclusion\" aria-label=\"conclusion permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Conclusion</h2>\n<p>This blog has only touched the surface of the different operations Redis can do. There are many more resources online that can help you go further into your understanding of Redis. Whether using Redis as a temporary store, like a cache, or using it as a primary, permanent store, the operations' speed is something to be acknowledged and considered when building your next big app.</p>\n<style class=\"grvsc-styles\">\n  .grvsc-container {\n    overflow: auto;\n    -webkit-overflow-scrolling: touch;\n    padding-top: 1rem;\n    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));\n    padding-bottom: 1rem;\n    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));\n    border-radius: 8px;\n    border-radius: var(--grvsc-border-radius, 8px);\n    font-feature-settings: normal;\n  }\n  \n  .grvsc-code {\n    display: inline-block;\n    min-width: 100%;\n  }\n  \n  .grvsc-line {\n    display: inline-block;\n    box-sizing: border-box;\n    width: 100%;\n    padding-left: 1.5rem;\n    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));\n    padding-right: 1.5rem;\n    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));\n  }\n  \n  .grvsc-line-highlighted {\n    background-color: var(--grvsc-line-highlighted-background-color, transparent);\n    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, transparent);\n  }\n  \n  .dark-default-dark {\n    background-color: #1E1E1E;\n    color: #D4D4D4;\n  }\n  .dark-default-dark .mtk4 { color: #569CD6; }\n  .dark-default-dark .mtk1 { color: #D4D4D4; }\n  .dark-default-dark .mtk12 { color: #9CDCFE; }\n  .dark-default-dark .mtk11 { color: #DCDCAA; }\n  .dark-default-dark .mtk8 { color: #CE9178; }\n</style>","frontmatter":{"title":"Three Ways to do CRUD Operations On Redis","author":{"id":"Chris Yee","github":null,"avatar":null},"date":"October 21, 2020","updated_date":null,"tags":["Redis","NodeJs","CLI","GUI"],"coverImage":{"childImageSharp":{"fluid":{"aspectRatio":1.5037593984962405,"src":"/static/4eda1ce5a0f541d97fdf27cd88bf2a49/ee604/index.png","srcSet":"/static/4eda1ce5a0f541d97fdf27cd88bf2a49/69585/index.png 200w,\n/static/4eda1ce5a0f541d97fdf27cd88bf2a49/497c6/index.png 400w,\n/static/4eda1ce5a0f541d97fdf27cd88bf2a49/ee604/index.png 800w,\n/static/4eda1ce5a0f541d97fdf27cd88bf2a49/f3583/index.png 1200w","sizes":"(max-width: 800px) 100vw, 800px"}}}},"fields":{"authorId":"Chris Yee","slug":"/engineering/three-ways-to-do-crud-operations-on-redis/"}}},{"node":{"id":"81cb1214-08b9-55bf-9b37-848ad8a629a7","html":"<p>Golang Maps is a compilation of unordered key-value pairs. It is commonly used because it offers simple searches and values with the aid of keys that can be retrieved, modified or deleted. It is a reference to a table with a hash.</p>\n<p>This blog will cover the basic use of maps in Go, and how a newcomer to the language may utilize them for their applications.</p>\n<h2 id=\"what-is-a-map\" style=\"position:relative;\"><a href=\"#what-is-a-map\" aria-label=\"what is a map permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>What is a Map?</h2>\n<p>Maps (also called dictionaries) are a very useful tool in helping to store and organize objects to be accessed in an efficient method. </p>\n<p>Most basic implementations of a map involve using a key to access a value in the map, resulting in key-value pairs, in which one key is associated with a specific value in the map. Within Golang, maps follow this definition.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 66.61538461538463%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAUGAQP/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAABX9m+SypSB//EABsQAAIDAAMAAAAAAAAAAAAAAAEDAAITBBEh/9oACAEBAAEFAlLLCeKzozPzG0yqJ//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EAB8QAAEDAwUAAAAAAAAAAAAAAAABAhEQEiEiMTNxkf/aAAgBAQAGPwKGk4poWzo5XGyeH//EABsQAAICAwEAAAAAAAAAAAAAAAERACExQWFx/9oACAEBAAE/IS4LWXqNdPZQzRbyouEoVXghqEnUn//aAAwDAQACAAMAAAAQI8//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPxBH/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAERQf/aAAgBAgEBPxBTSn//xAAdEAEAAgICAwAAAAAAAAAAAAABABEhMUGhYYGR/9oACAEBAAE/EB6oWlQPLKixTjdr1AKICNMyasFjP0QgDBrf1GrDyrHqf//Z'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Dictionary\"\n        title=\"Dictionary\"\n        src=\"/static/d9d54b7f1e01c89b7af93a3e87f2eec9/212bf/dictionary.jpg\"\n        srcset=\"/static/d9d54b7f1e01c89b7af93a3e87f2eec9/6aca1/dictionary.jpg 650w,\n/static/d9d54b7f1e01c89b7af93a3e87f2eec9/212bf/dictionary.jpg 768w,\n/static/d9d54b7f1e01c89b7af93a3e87f2eec9/670dc/dictionary.jpg 1650w\"\n        sizes=\"(max-width: 768px) 100vw, 768px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n    </span></p>\n<h2 id=\"how-to-create-a-map\" style=\"position:relative;\"><a href=\"#how-to-create-a-map\" aria-label=\"how to create a map permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>How to create a map?</h2>\n<p>Initialization of a map can be done using the <code>make</code> command. This is similar to the initialization of a slice:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"0\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk12\">mapObject</span><span class=\"mtk1\"> := </span><span class=\"mtk11\">make</span><span class=\"mtk1\">(</span><span class=\"mtk4\">map</span><span class=\"mtk1\">[</span><span class=\"mtk10\">string</span><span class=\"mtk1\">]</span><span class=\"mtk10\">string</span><span class=\"mtk1\">)</span></span></code></pre>\n<p>In this case, <code>mapObject</code> is a map that uses strings as a key to map to another string. When creating a map, the key type must be a type that is <code>Comparable</code>, or more specifically types that can be compared using the <code>==</code> operator.</p>\n<p>Examples of valid key types include booleans, numbers, strings and several other primitives can be used as keys. </p>\n<p>One more thing to note is that structs can be used as a key, provided that all the properties of the struct are <code>Comparable</code>.</p>\n<p>Maps can also be created using a map literal. For an empty map:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"1\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk12\">mapObject</span><span class=\"mtk1\"> := </span><span class=\"mtk4\">map</span><span class=\"mtk1\">[</span><span class=\"mtk10\">string</span><span class=\"mtk1\">]</span><span class=\"mtk10\">string</span><span class=\"mtk1\">{}</span></span></code></pre>\n<p>Or for a map with initial data:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"2\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk12\">mapObject</span><span class=\"mtk1\"> := </span><span class=\"mtk4\">map</span><span class=\"mtk1\">[</span><span class=\"mtk10\">string</span><span class=\"mtk1\">]</span><span class=\"mtk10\">string</span><span class=\"mtk1\">{</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t“Key1”: “Value1”,</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t“Key2”: “Value2”,</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t“Key3”: “Value3”,</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">}</span></span></code></pre>\n<h2 id=\"how-to-use-go-maps-with-examples\" style=\"position:relative;\"><a href=\"#how-to-use-go-maps-with-examples\" aria-label=\"how to use go maps with examples permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>How to use Go Maps (with Examples)</h2>\n<h3 id=\"setting\" style=\"position:relative;\"><a href=\"#setting\" aria-label=\"setting permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Setting</h3>\n<p>Interaction with Go maps are similar to the dictionaries and maps of other languages. A simple way to return a value associated with the key is to use bracket notation. For example, to set a string value mapped to a string key:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"3\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk1\">mapObject[“test”] = “test_value”</span></span></code></pre>\n<h3 id=\"fetching\" style=\"position:relative;\"><a href=\"#fetching\" aria-label=\"fetching permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Fetching</h3>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 56.30769230769231%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAgD/xAAWAQEBAQAAAAAAAAAAAAAAAAABAAT/2gAMAwEAAhADEAAAAQGs4Km//8QAGBAAAwEBAAAAAAAAAAAAAAAAAAEREEH/2gAIAQEAAQUC5XREz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABYQAQEBAAAAAAAAAAAAAAAAAAEAIP/aAAgBAQAGPwKTP//EABoQAQEAAgMAAAAAAAAAAAAAAAEAETEhcaH/2gAIAQEAAT8hHoxlhny4dkSQ7Jv/2gAMAwEAAgADAAAAECvP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFhEBAQEAAAAAAAAAAAAAAAAAARAR/9oACAECAQE/ENCf/8QAGxABAAICAwAAAAAAAAAAAAAAAQAhETFBUWH/2gAIAQEAAT8QJougqFIABOURMiPpNrEuwU7IAan/2Q=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Fetching\"\n        title=\"Fetching\"\n        src=\"/static/292ea3839b19a996d8a3c5aea66193b0/212bf/fetching.jpg\"\n        srcset=\"/static/292ea3839b19a996d8a3c5aea66193b0/6aca1/fetching.jpg 650w,\n/static/292ea3839b19a996d8a3c5aea66193b0/212bf/fetching.jpg 768w,\n/static/292ea3839b19a996d8a3c5aea66193b0/1cf64/fetching.jpg 1778w\"\n        sizes=\"(max-width: 768px) 100vw, 768px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n    </span></p>\n<p>Retrieving a value uses the same format.</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"4\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk12\">result</span><span class=\"mtk1\"> := mapObject[“test”]</span></span></code></pre>\n<p>The value of <code>result</code> will be the value assigned to <code>“test”</code> in the map. In the case where no key is found, the zero value of the type is returned instead. In this case, if there is no associated value with <code>“test”</code>, an empty string is returned.</p>\n<p>A boolean that returns the key’s existence can also be returned if two arguments are assigned from the retrieval. For example:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"5\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk12\">result</span><span class=\"mtk1\">, </span><span class=\"mtk12\">exists</span><span class=\"mtk1\"> := mapObject[“test”]</span></span></code></pre>\n<p>If <code>“test”</code> exists in the map, <code>result</code> will be the value associated with <code>“test”</code> and <code>exists</code> will be true. If <code>“test”</code> does not exist, <code>result</code> will be an empty string and <code>exists</code> will be false. This is useful when the map being used contains zero-values to distinguish between existence or whether the value is just zero.</p>\n<h3 id=\"deleting\" style=\"position:relative;\"><a href=\"#deleting\" aria-label=\"deleting permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Deleting</h3>\n<p>Using Go’s built in delete method, key-value pairs can be deleted off the map. To delete the previous <code>“test”</code> key from the map, the following can be called:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"6\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk11\">delete</span><span class=\"mtk1\">(mapObject, </span><span class=\"mtk8\">&quot;test&quot;</span><span class=\"mtk1\">)</span></span></code></pre>\n<p>The delete method does not have a return value, so if the key does not exist in the map, nothing will happen.\nLength</p>\n<p>The number of key-value pairs of a map can be found using the len method:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"7\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk12\">length</span><span class=\"mtk1\"> := </span><span class=\"mtk11\">len</span><span class=\"mtk1\">(mapObject)</span></span></code></pre>\n<h3 id=\"iterating\" style=\"position:relative;\"><a href=\"#iterating\" aria-label=\"iterating permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Iterating</h3>\n<p>Using Go’s range keyword, a map can be iterated through.</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"8\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk15\">for</span><span class=\"mtk1\"> </span><span class=\"mtk12\">key</span><span class=\"mtk1\">, </span><span class=\"mtk12\">value</span><span class=\"mtk1\"> := </span><span class=\"mtk15\">range</span><span class=\"mtk1\"> m {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    fmt.</span><span class=\"mtk11\">Println</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&quot;Key:&quot;</span><span class=\"mtk1\">, key, </span><span class=\"mtk8\">&quot;Value:&quot;</span><span class=\"mtk1\">, value)</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">}</span></span></code></pre>\n<p>There is no specific order in which the map is iterated. If a specific order is needed, a slice or other data structure can be used to store or hold data which can then be sorted and iterated through.</p>\n<h3 id=\"concurrency\" style=\"position:relative;\"><a href=\"#concurrency\" aria-label=\"concurrency permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Concurrency</h3>\n<p>Although Go has a lot of support for concurrency through the use of goroutines and channels, maps alone are not a reliable approach to handling data in a concurrent setting. </p>\n<p>To work with maps that support concurrency, a separate synchronization method should be used, like <code>sync.RWMutex</code>. Alternatively, an open source map package which implements the synchronizations can be used.</p>\n<h2 id=\"conclusion\" style=\"position:relative;\"><a href=\"#conclusion\" aria-label=\"conclusion permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Conclusion</h2>\n<p>With the market space and number of applications being created by Go increasing, hopefully this blog will help touch onto the basics of one of the major structures in Go.</p>\n<p>Although it doesn’t support concurrency, maps in Go are still a useful tool in most applications developed in Go, to reliably access and manage data.</p>\n<p>If you want to learn more about golang here is a quick guide on, <a href=\"/sending-emails-with-golang/\">how to send email with golang</a>. If you like what you read leave a comment or any question and will communicate further. </p>\n<style class=\"grvsc-styles\">\n  .grvsc-container {\n    overflow: auto;\n    -webkit-overflow-scrolling: touch;\n    padding-top: 1rem;\n    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));\n    padding-bottom: 1rem;\n    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));\n    border-radius: 8px;\n    border-radius: var(--grvsc-border-radius, 8px);\n    font-feature-settings: normal;\n  }\n  \n  .grvsc-code {\n    display: inline-block;\n    min-width: 100%;\n  }\n  \n  .grvsc-line {\n    display: inline-block;\n    box-sizing: border-box;\n    width: 100%;\n    padding-left: 1.5rem;\n    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));\n    padding-right: 1.5rem;\n    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));\n  }\n  \n  .grvsc-line-highlighted {\n    background-color: var(--grvsc-line-highlighted-background-color, transparent);\n    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, transparent);\n  }\n  \n  .dark-default-dark {\n    background-color: #1E1E1E;\n    color: #D4D4D4;\n  }\n  .dark-default-dark .mtk12 { color: #9CDCFE; }\n  .dark-default-dark .mtk1 { color: #D4D4D4; }\n  .dark-default-dark .mtk11 { color: #DCDCAA; }\n  .dark-default-dark .mtk4 { color: #569CD6; }\n  .dark-default-dark .mtk10 { color: #4EC9B0; }\n  .dark-default-dark .mtk8 { color: #CE9178; }\n  .dark-default-dark .mtk15 { color: #C586C0; }\n</style>","frontmatter":{"title":"Golang Maps - A Beginner’s Guide","author":{"id":"Chris Yee","github":null,"avatar":null},"date":"September 25, 2020","updated_date":null,"tags":["Go","dictionaries","maps"],"coverImage":{"childImageSharp":{"fluid":{"aspectRatio":1.5037593984962405,"src":"/static/30bb9b9901e76f3498d68913a0675ea9/ee604/index.png","srcSet":"/static/30bb9b9901e76f3498d68913a0675ea9/69585/index.png 200w,\n/static/30bb9b9901e76f3498d68913a0675ea9/497c6/index.png 400w,\n/static/30bb9b9901e76f3498d68913a0675ea9/ee604/index.png 800w,\n/static/30bb9b9901e76f3498d68913a0675ea9/f3583/index.png 1200w,\n/static/30bb9b9901e76f3498d68913a0675ea9/0dadc/index.png 1500w","sizes":"(max-width: 800px) 100vw, 800px"}}}},"fields":{"authorId":"Chris Yee","slug":"/engineering/golang-maps/"}}},{"node":{"id":"4a330e44-55e2-5948-91b1-7df02fdf2b2a","html":"<p>The official supported drivers for Mongo for Go have been officially released for several months now and is slowly seeing more usage. Although many of the features of standard Mongo drivers have been implemented within the supported driver, the documentation for a majority of the features are still in the process of being created. This post will provide some information on how to set up custom JSON encoders so you can change how BSON operators are processed whenever Mongo documents are marshalled into JSON. For this blog, a custom date and object id encoder will be created to handle the BSON operators and transform them into more easily processed JSON.</p>\n<p>This blog assumes that you have a basic understanding of Go and how to write some code in the language. If you want to follow along with the guide, you will also require a working Mongo instance, as well as your environment set up for Go.</p>\n<p>Before interacting with the database, we can set up a quick Go application that finds data from a Mongo database and outputs it. For this example, the Mongo database is set up with a collection containing documents with a name, a date and the object id. For our blog, the Mongo document that is being worked with is quite simple:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"0\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk1\">{</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    </span><span class=\"mtk8\">&quot;_id&quot;</span><span class=\"mtk1\"> : </span><span class=\"mtk11\">ObjectId</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&quot;5de6b0850f3c7b5cce642529&quot;</span><span class=\"mtk1\">),</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    </span><span class=\"mtk8\">&quot;Name&quot;</span><span class=\"mtk1\"> : </span><span class=\"mtk8\">&quot;dateOne&quot;</span><span class=\"mtk1\">,</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    </span><span class=\"mtk8\">&quot;Date&quot;</span><span class=\"mtk1\"> : </span><span class=\"mtk11\">ISODate</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&quot;2019-04-08T11:55:02.658Z&quot;</span><span class=\"mtk1\">)</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">}</span></span></code></pre>\n<p>A simple main function that retrieves data from Mongo and outputs the JSON string is as follows:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"1\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk4\">func</span><span class=\"mtk1\"> </span><span class=\"mtk11\">main</span><span class=\"mtk1\">() {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk4\">var</span><span class=\"mtk1\"> </span><span class=\"mtk12\">result</span><span class=\"mtk1\"> </span><span class=\"mtk4\">struct</span><span class=\"mtk1\"> {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t\tID   primitive.ObjectID </span><span class=\"mtk8\">`bson:&quot;_id&quot;`</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t\tName </span><span class=\"mtk10\">string</span><span class=\"mtk1\">             </span><span class=\"mtk8\">`bson:&quot;Name&quot;`</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t\tDate primitive.DateTime </span><span class=\"mtk8\">`bson:&quot;Date&quot;`</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t}</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk12\">ctx</span><span class=\"mtk1\">, </span><span class=\"mtk12\">cancel</span><span class=\"mtk1\"> := context.</span><span class=\"mtk11\">WithTimeout</span><span class=\"mtk1\">(context.</span><span class=\"mtk11\">Background</span><span class=\"mtk1\">(), </span><span class=\"mtk7\">10</span><span class=\"mtk1\">*time.Second)</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk12\">client</span><span class=\"mtk1\">, </span><span class=\"mtk12\">connErr</span><span class=\"mtk1\"> := mongo.</span><span class=\"mtk11\">Connect</span><span class=\"mtk1\">(ctx, options.</span><span class=\"mtk11\">Client</span><span class=\"mtk1\">().</span><span class=\"mtk11\">ApplyURI</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&quot;&quot;</span><span class=\"mtk1\">))</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk15\">if</span><span class=\"mtk1\"> connErr != </span><span class=\"mtk4\">nil</span><span class=\"mtk1\"> {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t\tlog.</span><span class=\"mtk11\">Fatal</span><span class=\"mtk1\">(connErr)</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t}</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk12\">collection</span><span class=\"mtk1\"> := client.</span><span class=\"mtk11\">Database</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&quot;test&quot;</span><span class=\"mtk1\">).</span><span class=\"mtk11\">Collection</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&quot;dates&quot;</span><span class=\"mtk1\">)</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk12\">filter</span><span class=\"mtk1\"> := bson.M{</span><span class=\"mtk8\">&quot;Name&quot;</span><span class=\"mtk1\">: </span><span class=\"mtk8\">&quot;dateOne&quot;</span><span class=\"mtk1\">}</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk12\">findErr</span><span class=\"mtk1\"> := collection.</span><span class=\"mtk11\">FindOne</span><span class=\"mtk1\">(ctx, filter).</span><span class=\"mtk11\">Decode</span><span class=\"mtk1\">(&result)</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk15\">if</span><span class=\"mtk1\"> findErr != </span><span class=\"mtk4\">nil</span><span class=\"mtk1\"> {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t\tlog.</span><span class=\"mtk11\">Fatal</span><span class=\"mtk1\">(findErr)</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t}</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\tfmt.</span><span class=\"mtk11\">Print</span><span class=\"mtk1\">(result.Date)</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk12\">data</span><span class=\"mtk1\">, </span><span class=\"mtk12\">writeErr</span><span class=\"mtk1\"> := bson.</span><span class=\"mtk11\">MarshalExtJSON</span><span class=\"mtk1\">(result, </span><span class=\"mtk4\">false</span><span class=\"mtk1\">, </span><span class=\"mtk4\">false</span><span class=\"mtk1\">)</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk15\">if</span><span class=\"mtk1\"> writeErr != </span><span class=\"mtk4\">nil</span><span class=\"mtk1\"> {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t\tlog.</span><span class=\"mtk11\">Fatal</span><span class=\"mtk1\">(writeErr)</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t}</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\tfmt.</span><span class=\"mtk11\">Print</span><span class=\"mtk1\">(</span><span class=\"mtk11\">string</span><span class=\"mtk1\">(data))</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk11\">cancel</span><span class=\"mtk1\">()</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">}</span></span></code></pre>\n<p>The function MarshalExtJSON is a part of the bson package that is a part of the driver. It uses a default registry to define the rules when converting Mongo primitive types into JSON. The arguments it takes are the Go struct being passed in, whether the result should be returned in canonical form (For more details, check this <a href=\"https://docs.mongodb.com/manual/reference/mongodb-extended-json/%5D(https://docs.mongodb.com/manual/reference/mongodb-extended-json/)\">link</a>, and whether HTML strings are escaped.</p>\n<p>The output of this function when reading our document produces a JSON string with all the BSON operators included.</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"2\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk1\">{</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk8\">&quot;_id&quot;</span><span class=\"mtk1\">: {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t\t</span><span class=\"mtk8\">&quot;$oid&quot;</span><span class=\"mtk1\">:</span><span class=\"mtk8\">&quot;5de6b0850f3c7b5cce642529&quot;</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t},</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk8\">&quot;Name&quot;</span><span class=\"mtk1\">:</span><span class=\"mtk8\">&quot;dateOne&quot;</span><span class=\"mtk1\">,</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk8\">&quot;Date&quot;</span><span class=\"mtk1\">:{</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t\t</span><span class=\"mtk8\">&quot;$date&quot;</span><span class=\"mtk1\">:</span><span class=\"mtk8\">&quot;2019-04-08T11:55:02.658Z&quot;</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t}</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">}</span></span></code></pre>\n<p>Using custom encoders, we can change how the bson package marshals the JSON and remove all the BSON operators. The first thing to do is to define package level variables that will hold the types of the Mongo primitives that need to be changed in the JSON response. Outside the main function, include these two lines:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"3\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk4\">var</span><span class=\"mtk1\"> </span><span class=\"mtk12\">tOID</span><span class=\"mtk1\"> = reflect.</span><span class=\"mtk11\">TypeOf</span><span class=\"mtk1\">(primitive.ObjectID{})</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk4\">var</span><span class=\"mtk1\"> </span><span class=\"mtk12\">tDateTime</span><span class=\"mtk1\"> = reflect.</span><span class=\"mtk11\">TypeOf</span><span class=\"mtk1\">(primitive.</span><span class=\"mtk11\">DateTime</span><span class=\"mtk1\">(</span><span class=\"mtk7\">0</span><span class=\"mtk1\">))</span></span></code></pre>\n<p>Next, we can make new functions to customize how these types are handled. For the date encoding, we want to have the Date key matched with the returned date string.</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"4\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk4\">func</span><span class=\"mtk1\"> </span><span class=\"mtk11\">dateTimeEncodeValue</span><span class=\"mtk1\">(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) </span><span class=\"mtk10\">error</span><span class=\"mtk1\"> {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk4\">const</span><span class=\"mtk1\"> </span><span class=\"mtk12\">jDateFormat</span><span class=\"mtk1\"> = </span><span class=\"mtk8\">&quot;2006-01-02T15:04:05.999Z&quot;</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk15\">if</span><span class=\"mtk1\"> !val.</span><span class=\"mtk11\">IsValid</span><span class=\"mtk1\">() || val.</span><span class=\"mtk11\">Type</span><span class=\"mtk1\">() != tDateTime {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t\t</span><span class=\"mtk15\">return</span><span class=\"mtk1\"> bsoncodec.ValueEncoderError{Name: </span><span class=\"mtk8\">&quot;DateTimeEncodeValue&quot;</span><span class=\"mtk1\">, Types: []reflect.Type{tDateTime}, Received: val}</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t}</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk12\">ints</span><span class=\"mtk1\"> := val.</span><span class=\"mtk11\">Int</span><span class=\"mtk1\">()</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk12\">t</span><span class=\"mtk1\"> := time.</span><span class=\"mtk11\">Unix</span><span class=\"mtk1\">(</span><span class=\"mtk7\">0</span><span class=\"mtk1\">, ints*</span><span class=\"mtk7\">1000000</span><span class=\"mtk1\">).</span><span class=\"mtk11\">UTC</span><span class=\"mtk1\">()</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk15\">return</span><span class=\"mtk1\"> vw.</span><span class=\"mtk11\">WriteString</span><span class=\"mtk1\">(t.</span><span class=\"mtk11\">Format</span><span class=\"mtk1\">(jdateFormat))</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">}</span></span></code></pre>\n<p>The parameters in this function are pre-defined within the driver package of the driver. This involves passing in the BSON encoding context, the value writer and the value that is being processed. The value writer and the value are the parameters that we use within our function.</p>\n<p>First, we check if the value passed in is the correct type. If there is an issue with the value, then an error is thrown during the encoding. Next, we can set the format we want our date string to appear in, and process our value into a Go Time object. After that, we can use the value writer to write the formatted date into our JSON string.</p>\n<p>Similar to the custom date encoding, the object id encoding function has the same structure.</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"5\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk4\">func</span><span class=\"mtk1\"> </span><span class=\"mtk11\">objectIDEncodeValue</span><span class=\"mtk1\">(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) </span><span class=\"mtk10\">error</span><span class=\"mtk1\"> {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk15\">if</span><span class=\"mtk1\"> !val.</span><span class=\"mtk11\">IsValid</span><span class=\"mtk1\">() || val.</span><span class=\"mtk11\">Type</span><span class=\"mtk1\">() != tOID {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t\t</span><span class=\"mtk15\">return</span><span class=\"mtk1\"> bsoncodec.ValueEncoderError{Name: </span><span class=\"mtk8\">&quot;ObjectIDEncodeValue&quot;</span><span class=\"mtk1\">, Types: []reflect.Type{tOID}, Received: val}</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t}</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk12\">s</span><span class=\"mtk1\"> := val.</span><span class=\"mtk11\">Interface</span><span class=\"mtk1\">().(primitive.ObjectID).</span><span class=\"mtk11\">Hex</span><span class=\"mtk1\">()</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk15\">return</span><span class=\"mtk1\"> vw.</span><span class=\"mtk11\">WriteString</span><span class=\"mtk1\">(s)</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">}</span></span></code></pre>\n<p>First, we check if the type is valid, and if it is, we simply write out the object id as a hex string. To create the hex string, we first convert the value into an interface, then cast it as an object id to allow the Hex function to be called.</p>\n<p>Now that our two functions are created to handle the BSON types, we can create our custom registry.</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"6\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk4\">func</span><span class=\"mtk1\"> </span><span class=\"mtk11\">createCustomRegistry</span><span class=\"mtk1\">() *bsoncodec.RegistryBuilder {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk4\">var</span><span class=\"mtk1\"> </span><span class=\"mtk12\">primitiveCodecs</span><span class=\"mtk1\"> bson.PrimitiveCodecs</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk12\">rb</span><span class=\"mtk1\"> := bsoncodec.</span><span class=\"mtk11\">NewRegistryBuilder</span><span class=\"mtk1\">()</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">        bsoncodec.DefaultValueEncoders{}.</span><span class=\"mtk11\">RegisterDefaultEncoders</span><span class=\"mtk1\">(rb)</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">        bsoncodec.DefaultValueDecoders{}.</span><span class=\"mtk11\">RegisterDefaultDecoders</span><span class=\"mtk1\">(rb)</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\trb.</span><span class=\"mtk11\">RegisterEncoder</span><span class=\"mtk1\">(tDateTime, bsoncodec.</span><span class=\"mtk11\">ValueEncoderFunc</span><span class=\"mtk1\">(dateTimeEncodeValue))</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\trb.</span><span class=\"mtk11\">RegisterEncoder</span><span class=\"mtk1\">(tOID, bsoncodec.</span><span class=\"mtk11\">ValueEncoderFunc</span><span class=\"mtk1\">(objectIDEncodeValue))</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\tprimitiveCodecs.</span><span class=\"mtk11\">RegisterPrimitiveCodecs</span><span class=\"mtk1\">(rb)</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">\t</span><span class=\"mtk15\">return</span><span class=\"mtk1\"> rb</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">}</span></span></code></pre>\n<p>In this function, we are setting up our registry to use our custom encoders, while also setting up default encoders for all other types that are being marshalled. Using the bsoncodec package, the NewRegistryBuilder function is called to initialize a new registry. Next, we set up this registry with the default encoders and decoders that are pre-programmed in the driver using RegisterDefaultEncoders. During this step, we can also register encoders and decoders for raw types by calling RegisterPrimitiveCodecs. Finally, we can include our custom encoders by calling RegisterEncoder on the custom registry struct. Our encoder will be called whenever the type defined in the first argument in RegisterEncoder is found in the struct being marshalled.</p>\n<p>To ensure our new custom registry is being used, we have to do two things. The first is to build the custom registry. This should be done before the marshalling of the struct.</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"7\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk4\">var</span><span class=\"mtk1\"> </span><span class=\"mtk12\">customRegistry</span><span class=\"mtk1\"> = </span><span class=\"mtk11\">createCustomRegistry</span><span class=\"mtk1\">().</span><span class=\"mtk11\">Build</span><span class=\"mtk1\">()</span></span></code></pre>\n<p>Next, we need to change the marshalling method to use the new custom registry instead of the default one. We can do this by changing the method being used to marshal the struct.</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"8\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk12\">customData</span><span class=\"mtk1\">, </span><span class=\"mtk12\">writeErr</span><span class=\"mtk1\"> := bson.</span><span class=\"mtk11\">MarshalExtJSONWithRegistry</span><span class=\"mtk1\">(customRegistry, result, </span><span class=\"mtk4\">false</span><span class=\"mtk1\">, </span><span class=\"mtk4\">false</span><span class=\"mtk1\">)</span></span></code></pre>\n<p>The function MarshalExtJSONWithRegistry takes the same parameters as MarshalExtJSON, but also requires a custom registry to be passed in as the first parameter. We pass in the custom registry we created into the method to allow the marshalling to use our custom encoders. As a result, when printing our new JSON string output, we get this:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"go\" data-index=\"9\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk1\">{</span><span class=\"mtk8\">&quot;_id&quot;</span><span class=\"mtk1\">:</span><span class=\"mtk8\">&quot;5de6b0850f3c7b5cce642529&quot;</span><span class=\"mtk1\">,</span><span class=\"mtk8\">&quot;Name&quot;</span><span class=\"mtk1\">:</span><span class=\"mtk8\">&quot;dateOne&quot;</span><span class=\"mtk1\">,</span><span class=\"mtk8\">&quot;Date&quot;</span><span class=\"mtk1\">:</span><span class=\"mtk8\">&quot;2019-04-08T11:55:02.658Z&quot;</span><span class=\"mtk1\">}</span></span></code></pre>\n<p>The Mongo driver for Go is still in active development and new versions continue to be released. Hopefully this blog provides some useful information on how the driver works while new documentation is being created.</p>\n<style class=\"grvsc-styles\">\n  .grvsc-container {\n    overflow: auto;\n    -webkit-overflow-scrolling: touch;\n    padding-top: 1rem;\n    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));\n    padding-bottom: 1rem;\n    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));\n    border-radius: 8px;\n    border-radius: var(--grvsc-border-radius, 8px);\n    font-feature-settings: normal;\n  }\n  \n  .grvsc-code {\n    display: inline-block;\n    min-width: 100%;\n  }\n  \n  .grvsc-line {\n    display: inline-block;\n    box-sizing: border-box;\n    width: 100%;\n    padding-left: 1.5rem;\n    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));\n    padding-right: 1.5rem;\n    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));\n  }\n  \n  .grvsc-line-highlighted {\n    background-color: var(--grvsc-line-highlighted-background-color, transparent);\n    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, transparent);\n  }\n  \n  .dark-default-dark {\n    background-color: #1E1E1E;\n    color: #D4D4D4;\n  }\n  .dark-default-dark .mtk1 { color: #D4D4D4; }\n  .dark-default-dark .mtk8 { color: #CE9178; }\n  .dark-default-dark .mtk11 { color: #DCDCAA; }\n  .dark-default-dark .mtk4 { color: #569CD6; }\n  .dark-default-dark .mtk12 { color: #9CDCFE; }\n  .dark-default-dark .mtk10 { color: #4EC9B0; }\n  .dark-default-dark .mtk7 { color: #B5CEA8; }\n  .dark-default-dark .mtk15 { color: #C586C0; }\n</style>","frontmatter":{"title":"Custom Encoders in the Mongo Go Driver","author":{"id":"Chris Yee","github":null,"avatar":null},"date":"December 03, 2019","updated_date":null,"tags":["Go","Golang","MongoDriver"],"coverImage":{"childImageSharp":{"fluid":{"aspectRatio":1.25,"src":"/static/0bf88d6b53aaaf119a2556b41f4c8383/ee604/mongo-drivers.png","srcSet":"/static/0bf88d6b53aaaf119a2556b41f4c8383/69585/mongo-drivers.png 200w,\n/static/0bf88d6b53aaaf119a2556b41f4c8383/497c6/mongo-drivers.png 400w,\n/static/0bf88d6b53aaaf119a2556b41f4c8383/ee604/mongo-drivers.png 800w,\n/static/0bf88d6b53aaaf119a2556b41f4c8383/30c15/mongo-drivers.png 936w","sizes":"(max-width: 800px) 100vw, 800px"}}}},"fields":{"authorId":"Chris Yee","slug":"/engineering/custom-encoders-in-the-mongo-go-driver/"}}},{"node":{"id":"64cdfe0e-5159-596b-944f-78d57d35e11b","html":"<p>Bots are tools that are created to automate tedious processes and reduce work. For example, chatbots automate replies to users for customer support, and search bots are used to populate search results on a Google search. However, there are many bots that are crafted for the malicious self-interest of a party. Examples of these hostile bots include DDOS (Direct Denial of Service) botnets and spam bots. This post will provide some information on how to implement bot protection to protect your systems from these nasty bot attacks.</p>\n<h3 id=\"captcha\" style=\"position:relative;\"><a href=\"#captcha\" aria-label=\"captcha permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>CAPTCHA:</h3>\n<p>One of the most popular methods of bot protection that is used today is CAPTCHA, which is provided through companies such as ReCAPTCHA, NuCaptcha and Solve Media. CAPTCHA, which stands for “Completely Automated Public Turing Test to tell Computers and Humans Apart”, is an anti-bot measure which consists of a challenge which a user must complete to verify if the user is human. Examples of challenges include translating images of distorted text, or recognition of objects in an image which match a given word. CAPTCHAs are useful in blocking automated form submissions by bots and are constantly being updated to be more friendly to human users. Some of the latest CAPTCHA implementations only require the user to click on a checkbox to pass their validation check.</p>\n<p>An example of a ReCAPTCHA with distorted text</p>\n<p><img src=\"https://media-s3-us-east-1.ceros.com/editorial-content/images/2018/05/31/c5c224dc0fb2a058625073c470d70c3c/recaptcha-big.png?ver=1552286291?imageOpt=1&#x26;fit=bounds&#x26;width=1077\"></p>\n<p>To implement CAPTCHA using Google’s ReCAPTCHA solution, you can access <a href=\"https://www.google.com/recaptcha/\">Google reCAPTCHA bot protection</a> and login with your Google account. Following that you will be redirected to an interface where you can register your site. Different types of CAPTCHAs can be set up for different events on your domain and can be built to match your use case.</p>\n<p>An example of a ReCAPTCHA with a checkbox validation</p>\n<p><img src=\"https://media.giphy.com/media/10p3VEnw29dD44/giphy.gif?ver=1552286291?ver=1552286291\"></p>\n<p>CAPTCHAs have a variety of uses, and can be used to prevent automated form completions and even prevent access to your domain. Setting up a CAPTCHA detection solution for different scenarios will typically provide a strong bot defense for your system.</p>\n<h3 id=\"spam-honeypots\" style=\"position:relative;\"><a href=\"#spam-honeypots\" aria-label=\"spam honeypots permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Spam Honeypots:</h3>\n<p>Spam honeypots are traps that ensnare bots by placing hidden input fields within a form that reject registration upon being filled in. Bots that use detect form fields through HTML may be programmed to fill in all the input fields in a form including the honeypot. Meanwhile, since the fields are hidden, human users should not be able to see the honeypot and should not be filling in the field.</p>\n<p>The implementation of a honey pot can be as simple as implementing an extra form field onto your page that should not be filled in. Hide the element using CSS and set up logic to prevent users that fill in the field from successfully completing the form. A simple implementation can be done with this code:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"js\" data-index=\"0\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk17\">&lt;</span><span class=\"mtk4\">input</span><span class=\"mtk1\"> </span><span class=\"mtk12\">id</span><span class=\"mtk1\">=</span><span class=\"mtk8\">&quot;first-name-input&quot;</span><span class=\"mtk1\"> </span><span class=\"mtk12\">type</span><span class=\"mtk1\">=</span><span class=\"mtk8\">&quot;text&quot;</span><span class=\"mtk1\"> </span><span class=\"mtk12\">name</span><span class=\"mtk1\">=</span><span class=\"mtk8\">&quot;firstname&quot;</span><span class=\"mtk1\"> </span><span class=\"mtk12\">value</span><span class=\"mtk1\">=</span><span class=\"mtk8\">&quot;Fill me in&quot;</span><span class=\"mtk17\">&gt;</span><span class=\"mtk1\"> </span><span class=\"mtk17\">&lt;</span><span class=\"mtk4\">input</span><span class=\"mtk1\"> </span><span class=\"mtk12\">style</span><span class=\"mtk1\">=</span><span class=\"mtk8\">&quot;display: none;&quot;</span><span class=\"mtk1\"> </span><span class=\"mtk12\">id</span><span class=\"mtk1\">=</span><span class=\"mtk8\">&quot;honeypot-input&quot;</span><span class=\"mtk1\"> </span><span class=\"mtk12\">type</span><span class=\"mtk1\">=</span><span class=\"mtk8\">&quot;text&quot;</span><span class=\"mtk1\"> </span><span class=\"mtk12\">name</span><span class=\"mtk1\">=</span><span class=\"mtk8\">&quot;honeypot&quot;</span><span class=\"mtk17\">&gt;</span><span class=\"mtk1\"> </span><span class=\"mtk17\">&lt;</span><span class=\"mtk4\">button</span><span class=\"mtk1\"> </span><span class=\"mtk12\">type</span><span class=\"mtk1\">=</span><span class=\"mtk8\">&quot;button&quot;</span><span class=\"mtk1\"> </span><span class=\"mtk12\">onclick</span><span class=\"mtk1\">=</span><span class=\"mtk8\">&quot;submitForm()&quot;</span><span class=\"mtk1\"> </span><span class=\"mtk12\">value</span><span class=\"mtk1\">=</span><span class=\"mtk8\">&quot;button&quot;</span><span class=\"mtk17\">&gt;</span><span class=\"mtk1\">Submit</span><span class=\"mtk17\">&lt;/</span><span class=\"mtk4\">button</span><span class=\"mtk17\">&gt;</span><span class=\"mtk1\"> </span><span class=\"mtk17\">&lt;</span><span class=\"mtk4\">button</span><span class=\"mtk1\"> </span><span class=\"mtk12\">type</span><span class=\"mtk1\">=</span><span class=\"mtk8\">&quot;button&quot;</span><span class=\"mtk1\"> </span><span class=\"mtk12\">onclick</span><span class=\"mtk1\">=</span><span class=\"mtk8\">&quot;fill()&quot;</span><span class=\"mtk1\"> </span><span class=\"mtk12\">value</span><span class=\"mtk1\">=</span><span class=\"mtk8\">&quot;button&quot;</span><span class=\"mtk17\">&gt;</span><span class=\"mtk1\">Fill Honeypot</span><span class=\"mtk17\">&lt;/</span><span class=\"mtk4\">button</span><span class=\"mtk17\">&gt;</span><span class=\"mtk1\"> </span><span class=\"mtk17\">&lt;</span><span class=\"mtk4\">script</span><span class=\"mtk17\">&gt;</span><span class=\"mtk1\">    let submitForm = function() </span><span class=\"mtk4\">{</span><span class=\"mtk1\">         </span><span class=\"mtk12\">let</span><span class=\"mtk1\"> </span><span class=\"mtk12\">honeypot</span><span class=\"mtk1\"> = </span><span class=\"mtk12\">document</span><span class=\"mtk1\">.</span><span class=\"mtk11\">getElementById</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&quot;honeypot-input&quot;</span><span class=\"mtk1\">).</span><span class=\"mtk12\">value</span><span class=\"mtk1\">;         </span><span class=\"mtk11\">if</span><span class=\"mtk1\">(!</span><span class=\"mtk12\">honeypot</span><span class=\"mtk1\">) { </span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">   </span><span class=\"mtk3\">// Handle input submit            </span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">        console.log(“Pass”);         } </span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">   </span><span class=\"mtk12\">else</span><span class=\"mtk1\"> { </span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">   </span><span class=\"mtk3\">// Handle honeypot error            </span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    console.log(“Fail”);  }  </span><span class=\"mtk4\">}</span><span class=\"mtk1\"> </span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">     let fill = function() </span><span class=\"mtk4\">{</span><span class=\"mtk1\">  </span><span class=\"mtk12\">document</span><span class=\"mtk1\">.</span><span class=\"mtk11\">getElementById</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&quot;honeypot-input&quot;</span><span class=\"mtk1\">).</span><span class=\"mtk12\">value</span><span class=\"mtk1\"> = </span><span class=\"mtk8\">&quot;Example&quot;</span><span class=\"mtk1\">;     </span><span class=\"mtk4\">}</span><span class=\"mtk1\"> </span></span>\n<span class=\"grvsc-line\"><span class=\"mtk17\">&lt;/</span><span class=\"mtk4\">script</span><span class=\"mtk17\">&gt;</span></span></code></pre>\n<p>With the implementation of the above code, if a bot is setup to fill in all input fields on your web page, then the hidden honeypot input will be filled in and the bot will be detected. On the other hand, if a normal user attempts to complete the form on the page, the honeypot would be invisible and the registration will be successful.</p>\n<p>Honeypots are a solution to weed out basic bots, but they can be easily circumvented depending on how the honeypot is implemented. Regardless, adding a honeypot still provides an additional layer of defense against bot inputs and will help deter some bot traffic on your site.</p>\n<h3 id=\"lockout-time\" style=\"position:relative;\"><a href=\"#lockout-time\" aria-label=\"lockout time permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Lockout Time:</h3>\n<p>Often times, when bots are created to automate a task, they would be programmed to complete these tasks as quickly as possible to maximize efficiency. As a countermeasure, time lockouts can be set up to prevent bots from repeatedly spamming requests. By setting up a time lockout between requests to your domain, bots which attempt to quickly submit data on your domain will be detected. Meanwhile, human users that are registering onto the site will be working at a slower pace and will not notice the time lockout at all.</p>\n<p>Setting a timer on form completion does not prevent any bots from affecting your domain, but can significantly slow down their efficiency. Combined with ReCAPTCHA or other anti-bot measures, it can be very useful in reducing the impact of bots.</p>\n<h3 id=\"blacklisting-ips\" style=\"position:relative;\"><a href=\"#blacklisting-ips\" aria-label=\"blacklisting ips permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Blacklisting IPs:</h3>\n<p>If an entity accessing your website is from an unexpected location, for example, a Russian IP accessing your domain for an American service, using IP blacklists may be useful to prevent possible bot attacks. IP blacklisting can usually be set up through your hosting services and allows you to customize where users may access your domain.</p>\n<p>There are some issues with blacklisting, though. Choosing which targets to blacklist could be a tedious task. Even with a bot set up to detect users with suspicious activity and block them, there could be a chance of a false positive, which may result in users of your domain being blacklisted.</p>\n<h3 id=\"proof-of-work\" style=\"position:relative;\"><a href=\"#proof-of-work\" aria-label=\"proof of work permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Proof Of Work:</h3>\n<p>If you want to save your site from spams and denial-of-service attacks you can incorporate a layer of Proof Of Work algorithm in your site. Whenever any client will try to connect to your server they need to commit some of their resources to the Proof Of Work algorithm first and then the server should be connected.</p>\n<p>With this approach, any legitimate user would experience just a negligible computational cost, but a spammer/attacker trying to establish a large number of connections would bear the computational cost and time delay, it deters the abuser to do so. There are many POW algorithms which you can use eg:- <a href=\"https://en.wikipedia.org/wiki/Client_Puzzle_Protocol\">Client Puzzle Protocol</a>, Productive Puzzle Protocol, <a href=\"https://en.wikipedia.org/wiki/Guided_tour_puzzle_protocol\">Guided Tour Puzzle Protocol</a></p>\n<h3 id=\"other-forms-of-malicious-bots\" style=\"position:relative;\"><a href=\"#other-forms-of-malicious-bots\" aria-label=\"other forms of malicious bots permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Other Forms of Malicious Bots:</h3>\n<p>Other malicious bots that can impact user experience negatively include, but are not limited to, bots designed for DDOS attacks, spam bots that harvest user data, bots that create links to phishing websites which generate viruses, and malicious bot worms that infect computers.</p>\n<p>Countermeasures for these bots vary depending on what is being prevented. Honeypot data fields can act as a detection method against bots harvesting data, and with proper preventative training, phishing and scam bots can be handled. Third party services may also be used to protect from different forms of bots. For example, to mitigate impacts of DDOS attacks, a user may implement a solution offered by CloudFlare. Another example is the use of an ad blocker to prevent malware from being drive-by downloaded by intrusive ads.</p>\n<h3 id=\"conclusion\" style=\"position:relative;\"><a href=\"#conclusion\" aria-label=\"conclusion permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Conclusion:</h3>\n<p>Unless your domain is a highly popular website, or is being targeted by technical security violation experts, there is a good chance that utilizing a simple Google ReCaptcha prompt for form completion is enough to handle any malicious bots that attempt to access your website. For domains with significantly more traffic, paid solutions like Cloudflare might also be useful in dealing with malicious bots.</p>\n<p>Keep in mind that although some bots are created for bad purposes, a large number of bots exist to automate beneficial tasks and make it easier for humans. Even though there are a significant amount of bots that are not helpful, it is important to embrace how useful benevolent bots actually are.</p>\n<style class=\"grvsc-styles\">\n  .grvsc-container {\n    overflow: auto;\n    -webkit-overflow-scrolling: touch;\n    padding-top: 1rem;\n    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));\n    padding-bottom: 1rem;\n    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));\n    border-radius: 8px;\n    border-radius: var(--grvsc-border-radius, 8px);\n    font-feature-settings: normal;\n  }\n  \n  .grvsc-code {\n    display: inline-block;\n    min-width: 100%;\n  }\n  \n  .grvsc-line {\n    display: inline-block;\n    box-sizing: border-box;\n    width: 100%;\n    padding-left: 1.5rem;\n    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));\n    padding-right: 1.5rem;\n    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));\n  }\n  \n  .grvsc-line-highlighted {\n    background-color: var(--grvsc-line-highlighted-background-color, transparent);\n    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, transparent);\n  }\n  \n  .dark-default-dark {\n    background-color: #1E1E1E;\n    color: #D4D4D4;\n  }\n  .dark-default-dark .mtk17 { color: #808080; }\n  .dark-default-dark .mtk4 { color: #569CD6; }\n  .dark-default-dark .mtk1 { color: #D4D4D4; }\n  .dark-default-dark .mtk12 { color: #9CDCFE; }\n  .dark-default-dark .mtk8 { color: #CE9178; }\n  .dark-default-dark .mtk11 { color: #DCDCAA; }\n  .dark-default-dark .mtk3 { color: #6A9955; }\n</style>","frontmatter":{"title":"A Bot Protection Overview","author":{"id":"Chris Yee","github":null,"avatar":null},"date":"May 31, 2019","updated_date":null,"tags":["Engineering","Captcha","Spam","Secure","IP"],"coverImage":{"childImageSharp":{"fluid":{"aspectRatio":1.2578616352201257,"src":"/static/010dd09dba7a6b0a11d58d980e3f0306/14b42/Productshot.jpg","srcSet":"/static/010dd09dba7a6b0a11d58d980e3f0306/f836f/Productshot.jpg 200w,\n/static/010dd09dba7a6b0a11d58d980e3f0306/2244e/Productshot.jpg 400w,\n/static/010dd09dba7a6b0a11d58d980e3f0306/14b42/Productshot.jpg 800w","sizes":"(max-width: 800px) 100vw, 800px"}}}},"fields":{"authorId":"Chris Yee","slug":"/engineering/a-bot-protection-overview/"}}}]},"authorYaml":{"id":"Chris Yee","bio":"A software engineer who graduated with a Bachelor of Software Engineering from the University of British Columbia.","github":null,"stackoverflow":null,"linkedin":null,"medium":null,"twitter":null,"avatar":null}},"pageContext":{"id":"Chris Yee","__params":{"id":"chris-yee"}}},"staticQueryHashes":["1171199041","1384082988","2100481360","23180105","528864852"]}