<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Griko Nibras</title>
    <description>The latest articles on DEV Community by Griko Nibras (@grikomsn).</description>
    <link>https://dev.to/grikomsn</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F40339%2F5ec70475-0e25-45e4-b2d8-80150873bea5.jpg</url>
      <title>DEV Community: Griko Nibras</title>
      <link>https://dev.to/grikomsn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/grikomsn"/>
    <language>en</language>
    <item>
      <title>I Made a Deno Module Registry Using Next.js, Vercel, and GitHub</title>
      <dc:creator>Griko Nibras</dc:creator>
      <pubDate>Thu, 04 Jun 2020 10:11:30 +0000</pubDate>
      <link>https://dev.to/grikomsn/i-made-a-deno-module-registry-using-next-js-vercel-and-github-3a73</link>
      <guid>https://dev.to/grikomsn/i-made-a-deno-module-registry-using-next-js-vercel-and-github-3a73</guid>
      <description>&lt;p&gt;&lt;em&gt;An adventure log about how I remade &lt;a href="https://deno.land" rel="noopener noreferrer"&gt;deno.land&lt;/a&gt; module registry from scratch for &lt;a href="https://denoland.id" rel="noopener noreferrer"&gt;denoland.id&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;When &lt;a href="https://twitter.com/deno_land/status/1260701643311583234?s=20" rel="noopener noreferrer"&gt;Deno premiered its version 1.0.0&lt;/a&gt;, everyone was on the Deno hype train. And one of the most interesting feature about Deno is that &lt;a href="https://deno.land/manual/linking_to_external_code" rel="noopener noreferrer"&gt;external modules are imported using direct URLs&lt;/a&gt;, so rather than downloading a module then store metadata on some &lt;code&gt;package-lock.json&lt;/code&gt;-like file, Deno uses URLs which the runtime will download and cache the module locally for future usage. But if you need to have some kind of lockfile, Deno can do this too, which you can read more on the &lt;a href="https://deno.land/manual/linking_to_external_code/integrity_checking" rel="noopener noreferrer"&gt;manual about integrity checking&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Third Party Modules 📦
&lt;/h1&gt;

&lt;p&gt;And speaking of modules, lots of third party modules are being submitted on &lt;a href="https://deno.land/x" rel="noopener noreferrer"&gt;deno.land/x&lt;/a&gt;, which is Deno's official module registry for third party modules.&lt;/p&gt;

&lt;p&gt;Unlike npm or other package/module registries, Deno uses &lt;a href="https://github.com/denoland/deno_website2/blob/master/database.json" rel="noopener noreferrer"&gt;a single JSON file&lt;/a&gt; to store module names, source type (GitHub or npm), user/org name and repo name. With that metadata,  the deno.land website/API will proxy to its module source. For example, here's the entry for the &lt;code&gt;airtable&lt;/code&gt; module:&lt;/p&gt;

&lt;center&gt;
![_](https://dev-to-uploads.s3.amazonaws.com/i/vymucm8vz6mq2dnz8oou.png)
&lt;small&gt;

&lt;/small&gt;
&lt;/center&gt;

&lt;p&gt;So if we want to import a &lt;code&gt;mod.ts&lt;/code&gt; file on the module, deno.land will resolve the &lt;code&gt;airtable&lt;/code&gt; module from GitHub with the owner/org &lt;code&gt;grikomsn&lt;/code&gt;, the repo &lt;code&gt;airtable-deno&lt;/code&gt;, and the contents of &lt;code&gt;mod.ts&lt;/code&gt; file, which the proxied URL will be &lt;a href="https://deno.land/x/airtable/mod.ts" rel="noopener noreferrer"&gt;https://deno.land/x/airtable/mod.ts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So no need to use GitHub's very long CDN URL like &lt;code&gt;https://raw.githubusercontent.com/grikomsn/airtable-deno/master/mod.ts&lt;/code&gt;. There are also other services like &lt;a href="https://denopkg.com" rel="noopener noreferrer"&gt;https://denopkg.com&lt;/a&gt; where it doesn't proxy sources but redirects to GitHub CDN, so &lt;a href="https://denopkg.com/grikomsn/airtable-deno/mod.ts" rel="noopener noreferrer"&gt;https://denopkg.com/grikomsn/airtable-deno/mod.ts&lt;/a&gt; redirects to the GitHub CDN URL. Neat!&lt;/p&gt;

&lt;h1&gt;
  
  
  The Magic URL ✨
&lt;/h1&gt;

&lt;p&gt;Now you may or may not noticed that the import URL for modules and the module explorer page URL is &lt;em&gt;exactly the same&lt;/em&gt;.&lt;/p&gt;

&lt;center&gt;
![_](https://dev-to-uploads.s3.amazonaws.com/i/fqm6xx427xfdn7m931kl.png)
&lt;small&gt;
The magical deno.land/x route.
&lt;/small&gt;
&lt;/center&gt;

&lt;p&gt;It was designed on purpose so by just using the same URL, you can explore the module contents and just copy paste the same URL to import on Deno. This is achieved using &lt;a href="https://workers.cloudflare.com" rel="noopener noreferrer"&gt;Cloudflare Workers&lt;/a&gt; where it proxies HTML requests to the module explorer page which is deployed on &lt;a href="https://vercel.com/home" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt;, and non-HTML requests to the module source files from their respective sources. This was the most mind-blowing thing when I started experimenting on Deno, and I'm still astonished that the Deno team can pull this off. 🤯&lt;/p&gt;

&lt;p&gt;Sidetrack for a moment. I joined and help maintain a new Deno user group for Indonesia folks, aptly named &lt;a href="https://denoland.id" rel="noopener noreferrer"&gt;Deno Land Indonesia&lt;/a&gt;. Everyone was &lt;a href="https://t.me/deno_id" rel="noopener noreferrer"&gt;joining the Telegram group&lt;/a&gt; from various places, probably someone broadcasted the invite link to other groups, which resulted from 10–20 members to instantly 200–300 members. And one day, someone bought the &lt;code&gt;denoland.id&lt;/code&gt; domain and filled it with a Gatsby-based placeholder page, and then out of the blue, this thought hit me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"What if we have our own module registry?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;…which followed with another thought:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Could this be achieved only using Vercel rewrites?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And with that, my week long journey on sleepless nights and constant trial and error begins.&lt;/p&gt;

&lt;h1&gt;
  
  
  Testing and Tinkering 👨‍🔧
&lt;/h1&gt;

&lt;p&gt;First order of business was experimenting if &lt;a href="https://vercel.com/docs/configuration#project/rewrites" rel="noopener noreferrer"&gt;Vercel &lt;code&gt;rewrites&lt;/code&gt; config&lt;/a&gt; could proxy another URL, which it could. But realizing that I also need to detect if the request accepts HTML or not, I definitely need something else that just rewriting requests. I created a &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; project with a &lt;a href="https://nextjs.org/docs/basic-features/pages" rel="noopener noreferrer"&gt;page route&lt;/a&gt; to view the module explorer page and an &lt;a href="https://nextjs.org/docs/api-routes/introduction" rel="noopener noreferrer"&gt;API route&lt;/a&gt; to resolve module sources, which it a success. Just need to proxy those URLs with Vercel &lt;code&gt;rewrites&lt;/code&gt; config and ready to go. And so I thought.&lt;/p&gt;

&lt;p&gt;So here's the basic idea on that iteration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/x/[...segments]&lt;/code&gt; is the page route showing the module explorer page.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/x/[...segments]&lt;/code&gt; is the API route which returns the module source.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;/x&lt;/code&gt; is &lt;code&gt;accept&lt;/code&gt;-ing HTML, it shouldn't proxy to &lt;code&gt;/api/x&lt;/code&gt; and show as is.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;/x&lt;/code&gt; is not &lt;code&gt;accept&lt;/code&gt;-ing HTML, it should proxy to &lt;code&gt;/api/x&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Since &lt;code&gt;/x&lt;/code&gt; is the page route and I also want it to handle &lt;code&gt;/api/x&lt;/code&gt;, that means I should rewrite &lt;code&gt;/x&lt;/code&gt; to &lt;code&gt;/api/x&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;And if &lt;code&gt;/x&lt;/code&gt; is now &lt;code&gt;/api/x&lt;/code&gt;, the API should be able to load regular &lt;code&gt;/x&lt;/code&gt; page.&lt;/li&gt;
&lt;li&gt;But since &lt;code&gt;/x&lt;/code&gt; is &lt;code&gt;/api/x&lt;/code&gt;, so that means it's requesting itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For clarity, here's a rough diagram I made using &lt;a href="https://excalidraw.com/" rel="noopener noreferrer"&gt;Excalidraw&lt;/a&gt;:&lt;/p&gt;

&lt;center&gt;
![_](https://dev-to-uploads.s3.amazonaws.com/i/vr53xq1jt3vi5czfxefm.png)
&lt;small&gt;
The recursion route dilemma.
&lt;/small&gt;
&lt;/center&gt;

&lt;p&gt;So basically rewriting to and redirecting to itself. And I only realized this when working on &lt;em&gt;the third day&lt;/em&gt;! Imagine those sleepless nights. 😞&lt;/p&gt;

&lt;h1&gt;
  
  
  Using Different Routes ⤵️
&lt;/h1&gt;

&lt;p&gt;After the recursion problem, I tried a workaround using a different route for the module explorer, like &lt;code&gt;/mod&lt;/code&gt; for module explorer page and rewrite &lt;code&gt;/api/x&lt;/code&gt; to &lt;code&gt;/x&lt;/code&gt;. And same as before where if &lt;code&gt;/x&lt;/code&gt; is requesting HTML, it should be able to load &lt;code&gt;/mod&lt;/code&gt; since it's not referring to anything else like before, and it should be able to load modules since it's &lt;code&gt;/api/x&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So here's what this should do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/mod&lt;/code&gt; is the page route showing the module explorer page.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/x&lt;/code&gt; is a rewrite of &lt;code&gt;/api/x&lt;/code&gt; which is the API route returning the module source.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;/x&lt;/code&gt; is &lt;code&gt;accept&lt;/code&gt;-ing HTML, it should proxy to &lt;code&gt;/mod&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;/x&lt;/code&gt; is not &lt;code&gt;accept&lt;/code&gt;-ing HTML, it should load the module.&lt;/li&gt;
&lt;li&gt;With that, there's no looping routes like before.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's another diagram:&lt;/p&gt;

&lt;center&gt;
![_](https://dev-to-uploads.s3.amazonaws.com/i/rverlphq3l0t3dxoejz8.png)
&lt;small&gt;
An alternative route solution, should probably work.
&lt;/small&gt;
&lt;/center&gt;

&lt;p&gt;This should work, right? There's no recursion requests, no same route conflicts, this should definitely work. (Yeah, it doesn't.)&lt;/p&gt;

&lt;p&gt;Apparently it's a Next.js related issue. Let's trace things one by one. &lt;code&gt;/mod&lt;/code&gt; is the page route and &lt;code&gt;/x&lt;/code&gt; is the rewritten API route which also returns the &lt;code&gt;/mod&lt;/code&gt; page if it's &lt;code&gt;accept&lt;/code&gt;-ing HTML. But since &lt;code&gt;/x&lt;/code&gt; technically does not exist and if you were to navigate to &lt;code&gt;/x&lt;/code&gt; on a loaded page, Next.js will throw an error since it doesn't find any &lt;code&gt;/x&lt;/code&gt; pages. &lt;code&gt;/x&lt;/code&gt; will only work on first page load since it's just a rewrite/proxy from &lt;code&gt;/api/x&lt;/code&gt;, so what if maybe renaming all Next.js navigation links from &lt;code&gt;/mod&lt;/code&gt; to &lt;code&gt;/x&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;I tried that "solution", but Next.js can't do aliased client-side navigation routes that aren't dynamic, so it can't access &lt;code&gt;/mod&lt;/code&gt; as &lt;code&gt;/x&lt;/code&gt;, only dynamic routes like &lt;code&gt;/x/[...segments]&lt;/code&gt; can utilize the aliased navigation or just hard reload or on first page load requests. So yeah, this one also does not work. Back to the drawing board. 😣&lt;/p&gt;

&lt;h1&gt;
  
  
  Separate Deployment 🔀
&lt;/h1&gt;

&lt;p&gt;Okay so we know that we can't use same route proxying and rewriting a Next.js page route is also not an option. I start re-exploring the Deno website codebase and thinking of another way to achieve the magical &lt;code&gt;/x&lt;/code&gt; route, when it hit me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"So if deno.land is using Cloudflare Workers, that means it's using a separate thing to proxy routes. What if using a separate Vercel deployment to proxy routes?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which basically means that I have given up trying to do same route proxying and just implement a proxy like a normal human being. So with that in mind, I deleted all the "same route" stuff and spin up a separate project to test if using &lt;a href="https://vercel.com/docs/v2/serverless-functions/introduction" rel="noopener noreferrer"&gt;Vercel serverless functions&lt;/a&gt; or &lt;code&gt;rewrite&lt;/code&gt; configs could forward requests to the original Next.js codebase. Here's another breakdown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Let's say the proxy project will be called &lt;code&gt;example.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;And the original website with the module explorer page and the API route to load modules will be called &lt;code&gt;web.example.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;So that means the module explorer page will be &lt;code&gt;web.example.com/x&lt;/code&gt; and the API route will be &lt;code&gt;web.example.com/api/x&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Which means the proxy should rewrite requests from &lt;code&gt;example.com&lt;/code&gt; to &lt;code&gt;web.example.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;And for &lt;code&gt;/x&lt;/code&gt; routes, the proxy should check if it's &lt;code&gt;accept&lt;/code&gt;-ing HTML, pass it to &lt;code&gt;web.example.com/x&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If it's not, the proxy should pass it to &lt;code&gt;web.example.com/api/x&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Diagrams should explain things better:&lt;/p&gt;

&lt;center&gt;
![_](https://dev-to-uploads.s3.amazonaws.com/i/uo210rz58b89p90xkcwx.png)
&lt;small&gt;
A sane routing solution, I'd say.
&lt;/small&gt;
&lt;/center&gt;

&lt;p&gt;No route conflicts, no loop requests, no hacky rewrites, this definitely checks a lot of boxes! So with that planned ahead, I start iterating many versions on how the proxy is handling requests and forwarding responses. 👏&lt;/p&gt;

&lt;h1&gt;
  
  
  If This Then That 👨‍💻
&lt;/h1&gt;

&lt;p&gt;The proxy project was quite challenging since there are a lot of edge cases that I need to cover, not to mention the technical limitations of Vercel &lt;code&gt;rewrites&lt;/code&gt; and serverless functions, and also finding out how to properly forward requests to specific routes. It took me another three or four days to develop the proxy and also working on the module explorer page. But the proxy is my main priority since the module explorer is basically ready at that time.&lt;/p&gt;

&lt;p&gt;The first iteration of the proxy was just testing if using Vercel &lt;code&gt;rewrites&lt;/code&gt; config could actually rewrite &lt;code&gt;example.com&lt;/code&gt; to &lt;code&gt;web.example.com&lt;/code&gt;. But since we need to handle &lt;code&gt;/x&lt;/code&gt; routes differently, so again, &lt;code&gt;rewrites&lt;/code&gt; won't do the job.&lt;/p&gt;

&lt;center&gt;
![_](https://dev-to-uploads.s3.amazonaws.com/i/ua5821fz5f15yt1d0u30.png)
&lt;small&gt;
First iteration diagram, just basic rewriting.
&lt;/small&gt;
&lt;/center&gt;




&lt;p&gt;The second iteration was adding an function to handle &lt;code&gt;/x&lt;/code&gt; routes while any other routes can just use Vercel &lt;code&gt;rewrites&lt;/code&gt; config. This works as intended, but in further development I realized that if I want to test something that isn't production ready, I need a way so that the proxy can forward request to a separate branch deployment. And again, &lt;code&gt;rewrites&lt;/code&gt; won't do the job.&lt;/p&gt;

&lt;center&gt;
![_](https://dev-to-uploads.s3.amazonaws.com/i/o2crio6ucyg89wkbfyee.png)
&lt;small&gt;
Second iteration diagram, now using functions.
&lt;/small&gt;
&lt;/center&gt;




&lt;p&gt;The third iteration was removing Vercel &lt;code&gt;rewrites&lt;/code&gt; completely and use functions to handle all requests but with an additional checking phase. So now if the request is from &lt;code&gt;example.com&lt;/code&gt;, it should forward to &lt;code&gt;web.example.com&lt;/code&gt;. But if it's from &lt;code&gt;staging.example.com&lt;/code&gt;, it should forward to &lt;code&gt;web-staging.example.com&lt;/code&gt;. The staging domains are just deployments from the &lt;code&gt;develop&lt;/code&gt; branch from both projects (Next.js website and proxy), so I can test something without actually pushing to the production domain &lt;code&gt;example.com&lt;/code&gt;.&lt;/p&gt;

&lt;center&gt;
![_](https://dev-to-uploads.s3.amazonaws.com/i/8i6b7oq9yz7xlib3zt30.png)
&lt;small&gt;
Third iteration diagram, same as second one but with different destinations.
&lt;/small&gt;
&lt;/center&gt;

&lt;p&gt;And with further testing and improvements on how the proxy forwards request and response headers, this was the final and working solution, and boy am I relieved that this is finished. 😅&lt;/p&gt;

&lt;h1&gt;
  
  
  Painting the Picture ️👨‍🎨
&lt;/h1&gt;

&lt;p&gt;The proxy is finished, now it's time to continue working on the main website.&lt;/p&gt;

&lt;p&gt;deno.land uses Next.js, &lt;a href="https://preactjs.com/" rel="noopener noreferrer"&gt;Preact&lt;/a&gt;, and &lt;a href="https://tailwindui.com/" rel="noopener noreferrer"&gt;Tailwind UI&lt;/a&gt;, whereas denoland.id uses Next.js, &lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt;, and &lt;a href="https://chakra-ui.com/" rel="noopener noreferrer"&gt;Chakra UI&lt;/a&gt;. So it's not quite apples to apples, but since the theme specs are inspired from &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt;, I can achieve the same visual identity of deno.land just by using Chakra UI. Here's a screenshot side by side of deno.land and denoland.id:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1266390919755882496-836" src="https://platform.twitter.com/embed/Tweet.html?id=1266390919755882496"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1266390919755882496-836');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1266390919755882496&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;The only thing I can't replicate completely is the nav animation from Tailwind UI, so I just use Chakra UI's &lt;code&gt;&amp;lt;Drawer /&amp;gt;&lt;/code&gt; component. But  that's not the main attraction, the module explorer is! So after tidying things up, I start working on the module explorer page.&lt;/p&gt;

&lt;p&gt;When I was tinkering the first iteration of the proxy, the module explorer page was already up and running but only listing and redirecting to the modules' repository. The module list was initially stored on &lt;a href="https://airtable.com/home" rel="noopener noreferrer"&gt;Airtable&lt;/a&gt; where I utilize &lt;a href="https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation" rel="noopener noreferrer"&gt;Next.js static data fetching&lt;/a&gt; to populate the list and resolve the module repository URL. Airtable was chosen since the &lt;a href="https://airtable.com/api" rel="noopener noreferrer"&gt;API is dead simple&lt;/a&gt; and quick for small cases like this, and with its form creation, I can share a form quickly where other module creators can submit their modules. But this didn't last long since I ended up using another solution (more on that later on).&lt;/p&gt;

&lt;center&gt;
![_](https://dev-to-uploads.s3.amazonaws.com/i/oxyn97yg6ue5nm1o3lpr.png)
&lt;small&gt;
Airtable table and form for the initial module registry.
&lt;/small&gt;
&lt;/center&gt;

&lt;p&gt;With that implemented, I start working on the API route to resolve repository trees and file contents by inspecting how deno.land/x loads content.&lt;/p&gt;

&lt;p&gt;The upside is that deno.land/x fetches the tree and contents on the client side using the browser's built-in fetch, which is using the &lt;a href="https://uxplanet.org/optimistic-1000-34d9eefe4c05" rel="noopener noreferrer"&gt;optimistic UI approach&lt;/a&gt; so users can get an early response by showing a skeleton content then show the actual content when it resolves. Another bonus point is since it's fetching client-side, response will be cached so next navigations will be quick and snappy. The only downside is it fetches the data from the browser to GitHub's API directly, so if you were to say refresh the page multiple times, you'll get rate limited and deno.land/x will show an error, which rarely happens but unfortunate if it happens.&lt;/p&gt;

&lt;p&gt;After learning how deno.land/x does does things, I start working on the tree list but with another approach where I use Next.js &lt;a href="https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering" rel="noopener noreferrer"&gt;server-side data fetching&lt;/a&gt;. The upside is the page requests once and there's no client-side fetching, but with the downside where every page load will take some time since it's fetching things server-side. Probably in a future update, I'll update the page like deno.land/x. &lt;a href="https://github.com/denoland-id/denoland.id/issues" rel="noopener noreferrer"&gt;Let me know what you think!&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;
![_](https://dev-to-uploads.s3.amazonaws.com/i/cx544d6k9ouohl1i8lpa.png)
&lt;small&gt;

&lt;/small&gt;
&lt;/center&gt;

&lt;p&gt;The module explorer is finally up and running, tree lists and file contents are showing correctly, another task finished. After wrapping things up, I started a discussion on the Telegram group about how we should implement the module registry else than Airtable. &lt;a href="https://twitter.com/zainfathoni" rel="noopener noreferrer"&gt;Zain Fathoni&lt;/a&gt; &lt;a href="https://t.me/deno_id/4989" rel="noopener noreferrer"&gt;pitched an idea&lt;/a&gt; where we could store metadatas on JSON files like how deno.land does but instead on a single file, we split the modules alphabetically. I thought it was a great idea since everyone can view and submit PRs whereas Airtable is closed and must be maintained by the internal members. So with that, I started planning to refactor the registry system. 😱&lt;/p&gt;

&lt;h1&gt;
  
  
  Delivering the Package 📦
&lt;/h1&gt;

&lt;p&gt;Fortunately it only took me a day or two to work on this since I already have a basic idea on how to implement the module registry. So I spin up another project, create a &lt;code&gt;database&lt;/code&gt; directory, and create a script where it iterates the 26 alphabets to check if the file exists on &lt;code&gt;database/[letter].json&lt;/code&gt; file (&lt;code&gt;a.json&lt;/code&gt;, &lt;code&gt;b.json&lt;/code&gt;, and so on), which also if it has any contents, it validates and sorts the JSON contents alphabetically.&lt;/p&gt;

&lt;center&gt;
![_](https://dev-to-uploads.s3.amazonaws.com/i/ppdhcjnhktd712zx1vgc.png)
&lt;small&gt;

&lt;/small&gt;
&lt;/center&gt;

&lt;p&gt;The next step is to design the module specification, and by design I mean defining a &lt;a href="https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html" rel="noopener noreferrer"&gt;type definition using TypeScript&lt;/a&gt; and a &lt;a href="https://json-schema.org/" rel="noopener noreferrer"&gt;JSON schema&lt;/a&gt; for future usage. Since the current script only sorts the modules alphabetically, there's no way to validate if the properties are correct, or even validating if there are duplicate modules. So by creating the specification, it'll be useful for future development and other contributors. Which means that I have to learn how JSON schema works and how to write one. Definitely worth it.&lt;/p&gt;

&lt;p&gt;With the specs finished, I update the JSON database files to have a &lt;code&gt;$schema&lt;/code&gt; field with the defined schema I previously made so editors and validators can pick up the schema definitions. So if you're using &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt;, the editor will fetch the schema from the field and type-hint the current file.&lt;/p&gt;

&lt;center&gt;
![_](https://dev-to-uploads.s3.amazonaws.com/i/dx1zw98ixa2ycffcv6lu.png)
&lt;small&gt;
VS Code type-hinting the database file.
&lt;/small&gt;
&lt;/center&gt;

&lt;p&gt;After all of that, now it's time to deploy the registry so it can be accessed for the module explorer website and also the API.&lt;/p&gt;

&lt;p&gt;The first iteration was using Vercel serverless functions which captured &lt;code&gt;/[a-z].json&lt;/code&gt; routes to import the specified JSON letter database. And for those wondering "why did you use functions when you can just serve the JSON files?", yes you are correct, and I realized that on the next day and I changed to just serve static files. This is why you shouldn't code until sunrise.&lt;/p&gt;

&lt;p&gt;Then I remember that the module explorer website needs to list all the packages from A to Z. So I added a build script where on deployment, it'll combine all the JSON files to a single &lt;code&gt;all.json&lt;/code&gt; file so the website can fetch the list easily without needing to parse from A to Z again. As for the module resolving API, it checks the first letter of the module and only fetch from that letter database, so if you're fetching &lt;code&gt;airtable&lt;/code&gt;, the API will fetch the &lt;code&gt;a.json&lt;/code&gt; registry file and not the whole list.&lt;/p&gt;

&lt;p&gt;And with the registry finished, I refactored the website and module resolver API to fetch from the new registry. 😎&lt;/p&gt;

&lt;h1&gt;
  
  
  Party Time 🥳
&lt;/h1&gt;

&lt;p&gt;Since I'm developing for Deno Land Indonesia, I use the &lt;code&gt;denoland.id&lt;/code&gt; domain for all the deployment. Which means the proxy is &lt;code&gt;denoland.id&lt;/code&gt;, the main website is &lt;code&gt;web.denoland.id&lt;/code&gt;, and the registry is &lt;code&gt;registry.denoland.id&lt;/code&gt;. Whereas the staging domains are &lt;code&gt;staging.denoland.id&lt;/code&gt; and &lt;code&gt;web-staging.denoland.id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The proxy is live, the website is up and ready, the API is loading properly and &lt;code&gt;curl&lt;/code&gt;-ing actually returns the module contents, it's time to test if Deno can actually use the routes. I opened up a terminal, &lt;code&gt;vim&lt;/code&gt;-ing a &lt;code&gt;deps.ts&lt;/code&gt; file with &lt;code&gt;export * from 'https://denoland.id/x/airtable/mod.ts&lt;/code&gt;, and run &lt;code&gt;deno cache deps.ts&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1268092150232702984-607" src="https://platform.twitter.com/embed/Tweet.html?id=1268092150232702984"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1268092150232702984-607');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1268092150232702984&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Sweet mother of Deno this actually works.&lt;/p&gt;




&lt;p&gt;And that concludes this adventure log on how I made a Deno module registry using Next.js, Vercel, and GitHub. And a little bit of Airtable. You can view the repository for the &lt;a href="https://github.com/denoland-id/denoland.id" rel="noopener noreferrer"&gt;website&lt;/a&gt;, &lt;a href="https://github.com/denoland-id/proxy" rel="noopener noreferrer"&gt;proxy&lt;/a&gt;, and &lt;a href="https://github.com/denoland-id/registry" rel="noopener noreferrer"&gt;registry&lt;/a&gt; on &lt;a href="https://github.com/denoland-id" rel="noopener noreferrer"&gt;Deno Land Indonesia's GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This was originally posted on my personal blog at &lt;a href="https://griko.id/blog/i-made-a-deno-module-registry-using-next-js-vercel-and-github" rel="noopener noreferrer"&gt;https://griko.id/blog/i-made-a-deno-module-registry-using-next-js-vercel-and-github&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>deno</category>
      <category>nextjs</category>
      <category>vercel</category>
      <category>github</category>
    </item>
    <item>
      <title>Exploring Undocumented getInitialProps Properties on Next.js</title>
      <dc:creator>Griko Nibras</dc:creator>
      <pubDate>Fri, 01 May 2020 09:44:44 +0000</pubDate>
      <link>https://dev.to/grikomsn/exploring-undocumented-getinitialprops-properties-on-next-js-pgb</link>
      <guid>https://dev.to/grikomsn/exploring-undocumented-getinitialprops-properties-on-next-js-pgb</guid>
      <description>&lt;p&gt;&lt;em&gt;An adventure log on discovering the secrets of the getInitialProps function and its mysterious object properties — updated March 2020&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Table of contents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Something’s not right 🔥&lt;/li&gt;
&lt;li&gt;Inspecting and dumping ✨&lt;/li&gt;
&lt;li&gt;Type? What type? 🐴&lt;/li&gt;
&lt;li&gt;Getting to know more 🚀&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Sometime around 2018, I was working on a web app using Next.js on an older version. At the time, one of the features of &lt;a href="https://github.com/zeit/next.js"&gt;Next.js&lt;/a&gt; is that it supports initial data population using the &lt;a href="https://nextjs.org/docs/api-reference/data-fetching/getInitialProps"&gt;&lt;code&gt;getInitialProps&lt;/code&gt; static method&lt;/a&gt;, which means that you can populate the page component &lt;code&gt;props&lt;/code&gt; before loading the page (e.g. fetching news feeds).&lt;/p&gt;

&lt;p&gt;The latest docs for Next.js is available on their &lt;a href="https://nextjs.org/docs/getting-started"&gt;official website&lt;/a&gt;. At &lt;a href="https://github.com/zeit/next.js/tree/v9.1.7"&gt;version 9.1.7&lt;/a&gt; and before, the docs was published on GitHub. Specifically at the &lt;a href="https://github.com/zeit/next.js/tree/v9.1.7#fetching-data-and-component-lifecycle"&gt;"Fetching data and component lifecycle"&lt;/a&gt; section, it shows how to use &lt;code&gt;getInitialProps&lt;/code&gt; and what parameters that can be destructured. A snippet from their readme:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;getInitialProps&lt;/code&gt; receives a context object with the following properties:&lt;br&gt;
&lt;code&gt;pathname&lt;/code&gt; - path section of URL&lt;br&gt;
&lt;code&gt;query&lt;/code&gt; - query string section of URL parsed as an object&lt;br&gt;
&lt;code&gt;asPath&lt;/code&gt; - &lt;code&gt;String&lt;/code&gt; of the actual path (including the query) shows in the browser&lt;br&gt;
&lt;code&gt;req&lt;/code&gt; - HTTP request object (server only)&lt;br&gt;
&lt;code&gt;res&lt;/code&gt; - HTTP response object (server only)&lt;br&gt;
&lt;code&gt;err&lt;/code&gt; - Error object if any error is encountered during the rendering&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before updating this post, &lt;code&gt;getInitialProps&lt;/code&gt; has one additional property which you can read on their readme at &lt;a href="https://github.com/zeit/next.js/tree/v8.0.0#fetching-data-and-component-lifecycle"&gt;version 8.0.0 and below&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;jsonPageRes&lt;/code&gt; - &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Response"&gt;Fetch Response&lt;/a&gt; object (client only)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Pretty straightforward, right? Except for one minor problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Something’s not right 🔥
&lt;/h2&gt;

&lt;p&gt;On another part of the readme which explains on &lt;a href="https://github.com/zeit/next.js/tree/v9.0.0#custom-app"&gt;how to use a custom &lt;code&gt;App&lt;/code&gt; component on &lt;code&gt;_app.js&lt;/code&gt;&lt;/a&gt;, it also uses &lt;code&gt;getInitialProps&lt;/code&gt; but with different destructured context parameters. Here's the code snippet from the readme:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;From the snippet above, it shows that &lt;code&gt;getInitialProps&lt;/code&gt; doesn't use the documented object properties. And also it seems that I'm not the only one confused about this. Quoting from a &lt;a href="https://spectrum.chat/?t=07e9ab0e-0fae-43a8-8bc0-350c79e921a3"&gt;discussion on a Spectrum thread&lt;/a&gt;,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What damn are and where are coming from the following properties of the context parameter?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So for many weeks, I searched the codebase, issues, and even Spectrum threads related to &lt;code&gt;getInitialProps&lt;/code&gt;. And in this post I will try my best to explain the &lt;code&gt;getInitialProps&lt;/code&gt; debacle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspecting and dumping ✨
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://spectrum.chat/?t=bd0da841-05f0-4b3b-8f1f-9e6b16b1952b"&gt;another Spectrum thread that I created&lt;/a&gt;, @revskill recommends using &lt;code&gt;util.inspect&lt;/code&gt; to analyze objects. So I made a temporary page (&lt;code&gt;pages/temp.js&lt;/code&gt;) and use this snippet below to dump the &lt;code&gt;getInitialProps&lt;/code&gt; parameter using &lt;code&gt;util.inspect&lt;/code&gt; (note that this is Next.js before version 9):&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Checking the console, it returns this:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;All properties shown are already documented on the readme, so where's &lt;code&gt;Component&lt;/code&gt;, &lt;code&gt;router&lt;/code&gt;, and &lt;code&gt;ctx&lt;/code&gt;? Because the readme shows that those three properties are used on a custom App, so I made pages/_app.js and dump the parameter on getInitialProps like before (again, note that this is before version 9):&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now the console returns two logs:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;As you can see on the snippet above, I destructured two properties: &lt;code&gt;Component&lt;/code&gt; and &lt;code&gt;ctx&lt;/code&gt;. So from my understanding is that the &lt;code&gt;Component&lt;/code&gt; object is the page component that will be loaded (e.g. &lt;code&gt;pages/index.js&lt;/code&gt;), and the &lt;code&gt;ctx&lt;/code&gt; object is the &lt;code&gt;App&lt;/code&gt; context (which explains why it has a &lt;code&gt;router&lt;/code&gt; property). Note the &lt;code&gt;if (Component.getInitialProps)&lt;/code&gt;, it's quite obvious that what it does is checks whether the page component has a getInitialProps function to run.&lt;/p&gt;

&lt;p&gt;So what that means is the &lt;code&gt;getInitialProps&lt;/code&gt; parameter (or context) differs from a page component. But this doesn't explain another thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Type? What type? 🐴
&lt;/h2&gt;

&lt;p&gt;I’m a sucker for object types, so it really bothers me when statically adding &lt;code&gt;getInitialProps&lt;/code&gt; to an &lt;code&gt;App&lt;/code&gt; or page component obviously doesn't give any hints on my editor. And after inspecting a lot above, at some point I asked myself, "does &lt;code&gt;next&lt;/code&gt; has a &lt;code&gt;@types&lt;/code&gt; package?" &lt;a href="https://github.com/DefinitelyTyped/DefinitelyTyped/blob/5b30992fd59e4a145b7589f2b333673f231fd36f/types/next/index.d.ts"&gt;And they have!&lt;/a&gt; Why did I bother inspecting ony by one?&lt;/p&gt;

&lt;p&gt;March 2020 update note: &lt;a href="https://github.com/DefinitelyTyped"&gt;DefinitelyTyped&lt;/a&gt; has deprecated the Next.js typings since version 9 already includes its own TypeScript declaration file. You can &lt;a href="https://github.com/DefinitelyTyped/DefinitelyTyped/pull/40235"&gt;view the deprecation pull request on GitHub&lt;/a&gt;, courtesy of &lt;a href="https://resir014.xyz/"&gt;Resi Respati&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After that sudden realization, I added the type package and checked whether it has an object or interface with the name ‘context’ using the IntelliSense extensions on Visual Studio Code. Lo and behold, I found three interfaces ‘context’ related (remember that this is before version 9):&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;After finding those three, I tried type hinting the &lt;code&gt;getInitialProps&lt;/code&gt; function on both &lt;code&gt;_app.js&lt;/code&gt; and a page component, and the results was fantastic:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Bx8FefaU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/0%2A-cLBxljNj-47AcFF.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Bx8FefaU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/0%2A-cLBxljNj-47AcFF.png" alt="App context object properties"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;center&gt;
&lt;br&gt;
  &lt;small&gt;👆🏻 App context object properties&lt;/small&gt;&lt;br&gt;
&lt;/center&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4juIzKXO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/0%2Aya_uM_Fm8FQDQrcw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4juIzKXO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/875/0%2Aya_uM_Fm8FQDQrcw.png" alt="Page context object properties"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;center&gt;
&lt;br&gt;
  &lt;small&gt;👆🏻 Page context object properties&lt;/small&gt;&lt;br&gt;
&lt;/center&gt;

&lt;p&gt;Much better! Now I have found out that it has a &lt;code&gt;@types&lt;/code&gt; package, getting to know more about its type and contents is much easier.&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting to know more 🚀
&lt;/h2&gt;

&lt;p&gt;In Visual Studio Code, you can jump to the definition of the type by command or control clicking the variable like below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HuZrYEgV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://miro.medium.com/max/658/0%2Agq2q3Rku-imCRAJV.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HuZrYEgV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://miro.medium.com/max/658/0%2Agq2q3Rku-imCRAJV.gif" alt="Preview on control or command clicking the object type"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;center&gt;
&lt;br&gt;
  &lt;small&gt;👆🏻 Preview on control or command clicking the object type&lt;/small&gt;&lt;br&gt;
&lt;/center&gt;

&lt;p&gt;From the GIF above, it opens the declaration files in the &lt;code&gt;node_modules&lt;/code&gt; directory in &lt;code&gt;node_modules/@types/next&lt;/code&gt;. Or you can view the file on the &lt;a href="https://github.com/DefinitelyTyped/DefinitelyTyped/blob/5b30992fd59e4a145b7589f2b333673f231fd36f/types/next/index.d.ts"&gt;&lt;code&gt;@types/next&lt;/code&gt; repository on GitHub&lt;/a&gt;. Here's a snippet from the declaration files for the context object on the &lt;code&gt;App&lt;/code&gt; component (&lt;code&gt;NextAppContext&lt;/code&gt;):&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And here’s the context declaration for page components (&lt;code&gt;NextContext&lt;/code&gt;):&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;That’s much better than inspecting objects one by one. 😅&lt;/p&gt;




&lt;p&gt;Hopefully this adventure log isn’t that confusing, since &lt;code&gt;getInitialProps&lt;/code&gt; is already confusing at the start. Thanks for reading, and happy coding! 👋🏻&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@griko/exploring-undocumented-getinitialprops-properties-on-next-js-1265a6abc652"&gt;&lt;em&gt;This was originally posted at Medium on November 26th, 2018 with the same title and contents.&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>node</category>
      <category>nextjs</category>
    </item>
  </channel>
</rss>
