PageSourceSearch

https://shakacode.com/component---src-template-blog-article-tsx-co…-rescript-index-mdx-104f7eb060ee08463c02.js

js shakacode.com collected 2026-10-04 00:28:29 UTC 35,690 bytes, 2 lines download raw bytes

1"use strict";(self.webpackChunkgatsby_starter_default=self.webpackChunkgatsby_starter_default||[]).push([[3179],{27598:function(n,a,e){e.r(a),e.d(a,{Head:function(){return w},default:function(){return f}});var s=e(28453),t=e(96540);function o(n){const a=Object.assign({p:"p",ol:"ol",li:"li",h2:"h2",a:"a",span:"span",ul:"ul",h3:"h3"},(0,s.R)(),n.components);return t.createElement(t.Fragment,null,t.createElement(a.p,null,"Once you start using a language with sound and expressive type system, to fully leverage its advantages you should push loosely typed entities to the edges of application. One such entity is the URL. As you don’t let raw JSON leak into internals of your app, the same way, raw URLs shouldn’t leak either."),"\n",t.createElement(a.p,null,"What is the URL? URL is stringily typed identifier which is extremely flexible but not safe at all. Its main purpose is to identify the current location of a user in a web app."),"\n",t.createElement(a.p,null,"Let’s break down what actually needs to be done to make URL-based routing safe:"),"\n",t.createElement(a.ol,null,"\n",t.createElement(a.li,null,"At the moment, when URL from a browser hits an application, it should be deserialized from a string into a safe domain-specific type."),"\n",t.createElement(a.li,null,"When application navigates a user to a different view, the value of the next location should be serialized back into a string, since this is what a browser expects."),"\n"),"\n",t.createElement(a.p,null,"As I mentioned in the beginning, this process is very similar to JSON handling. Let’s see how it might look in an actual app like a blog."),"\n",t.createElement(a.h2,{id:"foreword",style:{position:"relative"}},t.createElement(a.a,{href:"#foreword","aria-label":"foreword permalink",className:"anchor before"},t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><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>'}})),"Foreword"),"\n",t.createElement(a.p,null,"Before we get started, here is the link to the example apps, in case you want to skim through it or poke around implementation."),"\n",t.createElement(a.p,null,t.createElement(a.a,{href:"https://github.com/MinimaHQ/safe-routing-blog-app-example"},"github.com/MinimaHQ/safe-routing-blog-app-example")),"\n",t.createElement(a.p,null,"There are 2 key modules you should pay attention to:"),"\n",t.createElement(a.ul,null,"\n",t.createElement(a.li,null,t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Route.res</code>'}}),": application routes"),"\n",t.createElement(a.li,null,t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Router.res</code>'}}),": router specific code"),"\n"),"\n",t.createElement(a.p,null,"Now, let’s dive into details."),"\n",t.createElement(a.h2,{id:"url-deserialization",style:{position:"relative"}},t.createElement(a.a,{href:"#url-deserialization","aria-label":"url deserialization permalink",className:"anchor before"},t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><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>'}})),"URL deserialization"),"\n",t.createElement(a.p,null,"This step is one of the very first steps performed on an application start. To start rendering UI, we need to know where a user is. Once we figured it, we can either render an appropriate view or redirect the user somewhere else."),"\n",t.createElement(a.p,null,"Since current route might have only one value at the given moment in time, this is a perfect case for a variant:"),"\n",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="rescript"><pre class="language-rescript"><code class="language-rescript"><span class="token comment">// Route.res</span>\n\n<span class="token keyword">type</span> <span class="token constant">t</span> <span class="token operator">=</span>\n  <span class="token operator">|</span> <span class="token class-name">Main</span>\n  <span class="token operator">|</span> <span class="token class-name">Posts</span>\n  <span class="token operator">|</span> <span class="token class-name">Post</span><span class="token punctuation">(</span><span class="token punctuation">{</span>slug<span class="token operator">:</span> <span class="token keyword">string</span><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre></div>'}}),"\n",t.createElement(a.p,null,"To convert browser url into this type, we will use a router that c
1omes with ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">@rescript/react</code>'}})," (honestly, this is all you need to handle routing in your web apps)."),"\n",t.createElement(a.p,null,"It provides a hook ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">RescriptReactRouter.useUrl()</code>'}})," which returns ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">url</code>'}})," record:"),"\n",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="rescript"><pre class="language-rescript"><code class="language-rescript"><span class="token keyword">type</span> <span class="token constant">url</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n  path<span class="token operator">:</span> list<span class="token operator">&lt;</span><span class="token tag">string</span><span class="token operator">></span><span class="token punctuation">,</span>\n  <span class="token comment">// ... other props like hash, search etc.</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",t.createElement(a.p,null,"Let’s implement a function ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Route.fromUrl</code>'}})," that takes ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">RescriptReactRouter.url</code>'}})," and returns ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">option&lt;Route.t></code>'}}),"."),"\n",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="rescript"><pre class="language-rescript"><code class="language-rescript"><span class="token comment">// Route.res</span>\n\n<span class="token keyword">type</span> <span class="token constant">t</span> <span class="token operator">=</span>\n  <span class="token operator">|</span> <span class="token class-name">Main</span>\n  <span class="token operator">|</span> <span class="token class-name">Posts</span>\n  <span class="token operator">|</span> <span class="token class-name">Post</span><span class="token punctuation">(</span><span class="token punctuation">{</span>slug<span class="token operator">:</span> <span class="token keyword">string</span><span class="token punctuation">}</span><span class="token punctuation">)</span>\n\n<span class="token keyword">let</span> fromUrl <span class="token operator">=</span> <span class="token punctuation">(</span>url<span class="token operator">:</span> <span class="token class-name">RescriptReactRouter</span><span class="token punctuation">.</span><span class="token function">url</span><span class="token punctuation">)</span> <span class="token operator">=></span>\n  <span class="token keyword">switch</span> url<span class="token punctuation">.</span><span class="token function">path</span> <span class="token punctuation">{</span>\n  <span class="token operator">|</span> list<span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token operator">=></span> <span class="token class-name">Main</span><span class="token operator">-></span><span class="token class-name">Some</span>\n  <span class="token operator">|</span> list<span class="token punctuation">{</span><span class="token string">"posts"</span><span class="token punctuation">}</span> <span class="token operator">=></span> <span class="token class-name">Posts</span><span class="token operator">-></span><span class="token class-name">Some</span>\n  <span class="token operator">|</span> list<span class="token punctuation">{</span><span class="token string">"posts"</span><span class="token punctuation">,</span> slug<span class="token punctuation">}</span> <span class="token operator">=></span> <span class="token class-name">Post</span><span class="token punctuation">(</span><span class="token punctuation">{</span>slug<span class="token operator">:</span> slug<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token class-name">
1Some</span>\n  <span class="token operator">|</span> _ <span class="token operator">=></span> <span class="token class-name">None</span> <span class="token comment">// 404</span>\n  <span class="token punctuation">}</span></code></pre></div>'}}),"\n",t.createElement(a.p,null,"Alright, now we can deserialize url that comes from a browser into our own safe type. We can wrap ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">RescriptReactRouter.useUrl</code>'}})," hook into a thin application-specific hook that would produce app route:"),"\n",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="rescript"><pre class="language-rescript"><code class="language-rescript"><span class="token comment">// Router.res</span>\n\n<span class="token keyword">let</span> useRouter <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token class-name">RescriptReactRouter</span><span class="token punctuation">.</span><span class="token function">useUrl</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token class-name">Route</span><span class="token punctuation">.</span><span class="token function">fromUrl</span></code></pre></div>'}}),"\n",t.createElement(a.p,null,"And finally, we can render the app:"),"\n",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="rescript"><pre class="language-rescript"><code class="language-rescript"><span class="token comment">// App.res</span>\n\n<span class="token class-name">@react.component</span>\n<span class="token keyword">let</span> make <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>\n  <span class="token keyword">let</span> route <span class="token operator">=</span> <span class="token class-name">Router</span><span class="token punctuation">.</span><span class="token function">useRouter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>\n\n  <span class="token keyword">switch</span> route <span class="token punctuation">{</span>\n  <span class="token operator">|</span> <span class="token class-name">Some</span><span class="token punctuation">(</span><span class="token class-name">Main</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token operator">&lt;</span><span class="token class-name">Main</span> <span class="token operator">/</span><span class="token operator">></span>\n  <span class="token operator">|</span> <span class="token class-name">Some</span><span class="token punctuation">(</span><span class="token class-name">Posts</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token operator">&lt;</span><span class="token class-name">Posts</span> <span class="token operator">/</span><span class="token operator">></span>\n  <span class="token operator">|</span> <span class="token class-name">Some</span><span class="token punctuation">(</span><span class="token class-name">Post</span><span class="token punctuation">(</span><span class="token punctuation">{</span>slug<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token operator">&lt;</span><span class="token class-name">Post</span> slug <span class="token operator">/</span><span class="token operator">></span>\n  <span class="token operator">|</span> <span class="token class-name">None</span> <span class="token operator">=></span> <span class="token operator">&lt;</span><span class="token class-name">NotFound</span> <span class="token operator">/</span><span class="token operator">></span>\n  <span class="token punctuation">}</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",t.createElement(a.h2,{id:"navigation",style:{position:"relative"}}
1,t.createElement(a.a,{href:"#navigation","aria-label":"navigation permalink",className:"anchor before"},t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><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>'}})),"Navigation"),"\n",t.createElement(a.p,null,"Now when we rendered UI, we should provide a way to navigate from one screen to another. In general, there are 2 ways navigation can be approached:"),"\n",t.createElement(a.ol,null,"\n",t.createElement(a.li,null,"via HTML links"),"\n",t.createElement(a.li,null,"programmatically, i.e. dispatching next location after some side effects, such as login/logout."),"\n"),"\n",t.createElement(a.p,null,"Therefore, the task is reduced to the implementation of 2 handlers:"),"\n",t.createElement(a.ol,null,"\n",t.createElement(a.li,null,t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Router.Link</code>'}})," component that handles navigation via HTML links. It should accept the application-specific route and serialize it internally into a string to dispatch the next location to a browser when a user interacts with a link."),"\n",t.createElement(a.li,null,t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Router.push</code>'}})," function, that takes the application-specific route, serializes it and dispatches the next location to a browser using ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">History</code>'}})," API (",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Router.replace</code>'}})," function can be implemented the same way if required)."),"\n"),"\n",t.createElement(a.p,null,"I can offer two ways to solve this problem: one is more concise but with additional runtime overhead, and another one is faster performance-wise but requires additional type. Let’s start with the former."),"\n",t.createElement(a.h3,{id:"using-single-type-for-matching-and-navigation",style:{position:"relative"}},t.createElement(a.a,{href:"#using-single-type-for-matching-and-navigation","aria-label":"using single type for matching and navigation permalink",className:"anchor before"},t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><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>'}})),"Using single type for matching and navigation"),"\n",t.createElement(a.p,null,"If your app doesn't have a lot of links, this approach should be good to go. We already have ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Route.t</code>'}})," type implemented. To be able to dispatch it to a browser, we need to implement ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">toString</code>'}})," function that would take ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Route.t</code>'}})," and return a url string that browser expects."),"\n",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="rescript"><pre class="language-rescript"><code class="language-rescript"><span class="token comment">// Route.res</span>\n\n<span class="token keyword">let</span> toString <span class="token operator">=</span> route <span class="token operator">=></span>\n  <span class="token keyword">switch</span> route <span class="token punctuation">{</span>\n  <span class="token operator">|</span> <span class="token class-name">Main</span> <span class="token operator">=></span> <span class="token string">"/"</span>\n  <span class="token operator">|</span> <span class="token class-name">Posts</span> <span class="token operator">=></span> <span class="token string">"/posts"</span>\n  <span class="token operator">|</span> <span class="token class-name">Post</span><span class="token punctuation">(</span><span class="token punctuation">{</span>slug<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">/posts/</span><span class="token interpolation"><span class="token interpolation-punctuation tag">${</span>slug<span class="token interpolation-punctuation tag">}</span></span><span class="token template-punctuation string">`</span></span>\n  <span class="token punctuation">}</span></code></pre></div>'}}),"\n",t.createElement(a.p,null,"And this is all we need to implement ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Router.Link</code>'}})," component and ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Router.push</code>'}})," function."),"\n",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="rescript"><pre class="language-rescript"><code class="language-rescript"><span class="token comment">// Router.res</span>\n\n<span class="token keyword">let</span>
1 push <span class="token operator">=</span> <span class="token punctuation">(</span>route<span class="token operator">:</span> <span class="token class-name">Route</span><span class="token punctuation">.</span><span class="token function">t</span><span class="token punctuation">)</span> <span class="token operator">=></span> route<span class="token operator">-></span><span class="token class-name">Route</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token operator">-></span><span class="token class-name">RescriptReactRouter</span><span class="token punctuation">.</span><span class="token function">push</span>\n\n<span class="token keyword">module</span> <span class="token class-name">Link</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n  <span class="token class-name">@react.component</span>\n  <span class="token keyword">let</span> make <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token operator">~</span>route<span class="token operator">:</span> <span class="token class-name">Route</span><span class="token punctuation">.</span><span class="token function">t</span><span class="token punctuation">,</span> <span class="token operator">~</span>children<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>\n    <span class="token keyword">let</span> location <span class="token operator">=</span> route<span class="token operator">-></span><span class="token class-name">Route</span><span class="token punctuation">.</span><span class="token function">toString</span>\n\n    <span class="token operator">&lt;</span><span class="token tag">a</span>\n      <span class="token attr-value">href</span><span class="token operator">=</span>
1location\n      <span class="token attr-value">onClick</span><span class="token operator">=</span><span class="token punctuation">{</span>event <span class="token operator">=></span>\n        <span class="token comment">// Simplified implementation of handler</span>\n        <span class="token comment">// See example repository for the full version</span>\n        event<span class="token operator">-></span><span class="token class-name">ReactEvent</span><span class="token punctuation">.</span><span class="token class-name">Mouse</span><span class="token punctuation">.</span><span class="token function">preventDefault</span>\n        location<span class="token operator">-></span><span class="token class-name">RescriptReactRouter</span><span class="token punctuation">.</span><span class="token function">push</span>\n      <span class="token punctuation">}</span><span class="token operator">></span>\n      children\n    <span class="token tag"><span class="token operator">&lt;</span><span class="token operator">/</span>a</span><span class="token operator">></span>\n  <span class="token punctuation">}</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",t.createElement(a.p,null,"Note that both these functions accept ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Route.t</code>'}})," type instead of arbitrary ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">string</code>'}}),", which makes it impossible to dispatch a wrong url through these interfaces."),"\n",t.createElement(a.p,null,"A link in the app would look like this:"),"\n",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="rescript"><pre class="language-rescript"><code class="language-rescript"><span class="token operator">&lt;</span><span class="token class-name">Router</span><span class="token punctuation">.</span><span class="token class-name">Link</span> <span class="token attr-value">route</span><span class="token operator">=</span><span class="token class-name">Posts</span><span class="token operator">></span>\n  <span class="token punctuation">{</span><span class="token string">"Posts"</span><span class="token operator">-></span><span class="token class-name">React</span><span class="token punctuation">.</span><span class="token function">string</span><span class="token punctuation">}</span>\n<span class="token operator">&lt;</span><span class="token operator">/</span><span class="token class-name">Router</span><span class="token punctuation">.</span><span class="token class-name">Link</span><span class="token operator">></span></code></pre></div>'}}),"\n",t.createElement(a.p,null,"This approach has one downside though. If you render a lot of links, you might notice runtime overhead since on each re-render ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Route.t</code>'}})," gets serialized into a string. Of course, you can try to memoize things, but if perf is critical, you can get rid of runtime overhead completely by considering the second approach."),"\n",t.createElement(a.h3,{id:"using-different-types-for-matching-and-navigation",style:{position:"relative"}},t.createElement(a.a,{href:"#using-different-types-for-matching-and-navigation","aria-label":"using different types for matching and navigation permalink",className:"anchor before"},t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><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>'}})),"Using different types for matching and navigation"),"\n",t.createElement(a.p,null,"This approach is similar to the one we discussed in ",t.createElement(a.a,{href:"https://alexfedoseev.com/blog/dev/2020/safe-identifiers-in-rescript"},"“Safe Identifiers”")," post. We can skip the serialization step and make it zero-cost. The trade-off is that we must introduce a new type and handle packing/unpacking."),"\n",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="rescript"><pre class="language-rescript"><code class="language-rescript"><span class="token comment">// Route.res</span>\n\n<span class="token keyword">type</span> <span class="token constant">t</span><span class="token operator">\'</span>\n\n<span class="token keyword">
1external</span> make<span class="token operator">:</span> <span class="token keyword">string</span> <span class="token operator">=></span> t<span class="token operator">\'</span> <span class="token operator">=</span> <span class="token string">"%identity"</span>\n<span class="token keyword">external</span> toString<span class="token operator">:</span> t<span class="token operator">\'</span> <span class="token operator">=></span> <span class="token keyword">string</span> <span class="token operator">=</span> <span class="token string">"%identity"</span>\n\n<span class="token keyword">let</span> main <span class="token operator">=</span> <span class="token string">"/"</span><span class="token operator">-></span>make\n<span class="token keyword">let</span> posts <span class="token operator">=</span> <span class="token string">"/posts"</span><span class="token operator">-></span>make\n<span class="token keyword">let</span> post <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token operator">~</span>slug<span class="token operator">:</span> <span class="token keyword">string</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">/posts/</span><span class="token interpolation"><span class="token interpolation-punctuation tag">${</span>slug<span class="token interpolation-punctuation tag">}</span></span><span class="token template-punctuation string">`</span></span><span class="token operator">-></span>make</code></pre></div>'}}),"\n",t.createElement(a.p,null,"Here, we introduced abstract type ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">t\'</code>'}}),". This is the type that would be accepted by ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Router.Link</code>'}})," component and ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Router.push</code>'}})," function instead of ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Route.t</code>'}})," variant. And this is the only change to ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Router</code>'}})," module implementation."),"\n",t.createElement(a.p,null,"Inside the ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Route</code>'}})," module, we pack every URL string into ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Route.t\'</code>'}}),", so only URLs defined in this module would be accepted for navigation. It doesn't have runtime cost, since we used ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">%identity</code>'}})," external to pack/unpack strings."),"\n",t.createElement(a.p,null,"Finishing touch to make implementation completely safe is creating ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Route.rei</code>'}})," interface file, in which we remove ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">make</code>'}})," function thus it won’t be exposed to the rest of the app and it won’t be possible to create ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Route.t\'</code>'}})," outside of the ",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Route</code>'}})," module."),"\n",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="rescript"><pre class="language-rescript"><code class="language-rescript"><span class="token comment">// Route.resi</span>\n\n<span class="token keyword">type</span> <span class="token constant">t</span> <span class="token operator">=</span>\n  <span class="token operator">|</span> <span class="token class-name">Main</span>\n  <span class="token operator">|</span> <span class="token class-name">Posts</span>\n  <span class="token operator">|</span> <span class="token class-name">Post</span><span class="token punctuation">(</span><span class="token punctuation">{</span>slug<span class="token operator">:</span> <span class="token keyword">string</span><span class="token punctuation">}</span><span class="token punctuation">)</span>\n\n<span class="token keyword">let</span> fromUrl<span class="token operator">:</span> <span class="token class-name">RescriptReactRouter</span><span class="token punctuation">.</span><span class="token function">url</span> <span class="token operator">=></span> option<span class="token operator">&lt;</span><span class="token tag">t</span><span class="token operator">></span>\n\n<span class="token keyword">type</span> <span class="token constant">t</span><span class="token operator">\'</span>\n\n<span class="token comment">// No `make` external here</span>\n<span class="token keyword">
1external</span> toString<span class="token operator">:</span> t<span class="token operator">\'</span> <span class="token operator">=></span> <span class="token keyword">string</span> <span class="token operator">=</span> <span class="token string">"%identity"</span>\n\n<span class="token keyword">let</span> main<span class="token operator">:</span> t<span class="token operator">\'</span>\n<span class="token keyword">let</span> posts<span class="token operator">:</span> t<span class="token operator">\'</span>\n<span class="token keyword">let</span> post<span class="token operator">:</span> <span class="token punctuation">(</span><span class="token operator">~</span>slug<span class="token operator">:</span> <span class="token keyword">string</span><span class="token punctuation">)</span> <span class="token operator">=></span> t<span class="token operator">\'</span></code></pre></div>'}}),"\n",t.createElement(a.p,null,"Now you can render links as following:"),"\n",t.createElement(a.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="rescript"><pre class="language-rescript"><code class="language-rescript"><span class="token operator">&lt;</span><span class="token class-name">Router</span><span class="token punctuation">.</span><span class="token class-name">Link</span> <span class="token attr-value">route</span><span class="token operator">=</span><span class="token class-name">Route</span><span class="token punctuation">.</span><span class="token function">posts</span><span class="token operator">></span>\n  <span class="token punctuation">{</span><span class="token string">"Posts"</span><span class="token operator">-></span><span class="token class-name">React</span><span class="token punctuation">.</span><span class="token function">string</span><span class="token punctuation">}</span>\n<span class="token operator">&lt;</span><span class="token operator">/</span><span class="token class-name">Router</span><span class="token punctuation">.</span><span class="token class-name">Link</span><span class="token operator">></span></code></pre></div>'}}),"\n",t.createElement(a.p,null,"And that's pretty much it. Happy & safe coding!"))}var p=function(n){void 0===n&&(n={});const{wrapper:a}=Object.assign({},(0,s.R)(),n.components);return a?t.createElement(a,n,t.createElement(o,n)):o(n)},l=e(5588),c=e(65427),r=e(20504),i=e(43397),u=e(61942);const k=n=>n.replace(/\/+$/,""),m=(n,a)=>a?""+n+(a.startsWith("/")?a:"/"+a):n,d=(n,a)=>m(k(n),a),g=(n,a)=>m((n=>"undefined"!=typeof window?window.location.origin:k(n))(n),a),h=(n,a)=>{const e=(0,l.d)(a);return e?d(n,e):(n=>d(n,"/shakacode-og.png"))(n)},y=n=>{let{data:a,location:e,children:s}=n;const{frontmatter:o}=a.mdx,{siteUrl:p}=a.site.siteMetadata,l=g(p,e.pathname);return t.createElement(t.Fragment,null,t.createElement(c.A,null),t.createElement(r.A,{header:t.createElement(u.r,{breadcrumb:o.title}),main:t.createElement(t.Fragment,null,t.createElement(u.c,{body:s,title:o.title,author:o.author,date:o.date,link:l,categories:o.categories,featuredImage:o.featuredImage,showFeaturedImage:o.showFeaturedImage}),t.createElement(i.LN,null))}))};function f(n){return t.createElement(y,n,t.createElement(p,n))}const w=n=>{let{data:a,location:e}=n;const{frontmatter:s,excerpt:o}=a.mdx,{siteMetadata:p}=a.site,{siteUrl:l,title:c,description:r}=p,i=((n,a,e)=>n.excerpt||a||e)(s,o,r),u=h(l,s.featuredImage),k=d(l,e.pathname);return t.createElement(t.Fragment,null,t.createElement("title",null,s.title+" | "+c),t.createElement("link",{rel:"canonical",href:k}),t.createElement("meta",{name:"description",content:i}),t.createElement("meta",{property:"og:title",content:s.title}),t.createElement("meta",{property:"og:description",content:i}),t.createElement("meta",{property:"og:type",content:"article"}),t.createElement("meta",{property:"og:url",content:k}),t.createElement("meta",{property:"og:image",content:u}),t.createElement("meta",{name:"image",content:u}),t.createElement("meta",{name:"twitter:card",content:"summary_large_image"}),t.createElement("meta",{name:"twitter:title",content:s.title}),t.createElement("meta",{name:"twitter:description",content:i}),t.createElement("meta",{name:"twitter:image",content:u}))}}}]);
2//# sourceMappingURL=component---src-template-blog-article-tsx-content-file-path-src-posts-safe-routing-in-rescript-index-mdx-104f7eb060ee08463c02.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.