<?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: Jasper Moelker</title>
    <description>The latest articles on DEV Community by Jasper Moelker (@jbmoelker).</description>
    <link>https://dev.to/jbmoelker</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%2F112689%2Fbc199872-d510-4fff-aecd-7c45745f0017.jpg</url>
      <title>DEV Community: Jasper Moelker</title>
      <link>https://dev.to/jbmoelker</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jbmoelker"/>
    <language>en</language>
    <item>
      <title>Svelte Kit, the first ‘serverless-first’ framework?</title>
      <dc:creator>Jasper Moelker</dc:creator>
      <pubDate>Wed, 03 Feb 2021 17:04:50 +0000</pubDate>
      <link>https://dev.to/jbmoelker/svelte-kit-the-first-serverless-first-framework-1ebd</link>
      <guid>https://dev.to/jbmoelker/svelte-kit-the-first-serverless-first-framework-1ebd</guid>
      <description>&lt;p&gt;"The whole serverless thing is a pretty good trend, and &lt;strong&gt;Svelte is now a serverless-first framework&lt;/strong&gt;"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/qSfdtmcZ4d0?t=1194"&gt;That&lt;/a&gt; &lt;a href="https://youtu.be/qSfdtmcZ4d0?t=1194"&gt;quote is from&lt;/a&gt; &lt;a href="https://youtu.be/qSfdtmcZ4d0?t=1194"&gt;Rich Harris&lt;/a&gt;, creator of Svelte. A bold statement, but is the newly introduced Svelte Kit really “serverless-first”? And what does that even mean? And if so, how well can this new Svelte seamlessly integrate with serverless platforms like Netlify and Vercel?&lt;/p&gt;

&lt;p&gt;Big fat disclaimer: &lt;a href="https://svelte.dev/blog/whats-the-deal-with-sveltekit"&gt;Svelte Kit is in early development&lt;/a&gt;. The source code isn't public yet. To get a sneak peek I installed the "next" version (&lt;code&gt;@1.0.0-next.0&lt;/code&gt;) of Svelte and its &lt;a href="https://www.npmjs.com/search?q=%40sveltejs%2Fadapter-"&gt;new serverless adapters&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init svelte@next
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; @sveltejs/adapter-node
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; @sveltejs/adapter-static
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; @sveltejs/adapter-netlify
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; @sveltejs/adapter-vercel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My findings are based on sifting through the &lt;a href="https://github.com/jbmoelker/test-sveltejs-kit/"&gt;compiled code&lt;/a&gt; with the help of &lt;a href="https://sokra.github.io/source-map-visualization"&gt;Sokra's source code visualiser&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Serverless-first using adapters
&lt;/h2&gt;

&lt;p&gt;Svelte’s answer to become serverless-first in a fragmented landscape of serverless platforms is its introduction of so-called serverless adapters. While all serverless providers like Netlify and Vercel offer similar functionality — static hosting, cloud functions and configurable redirects — they each have their own vendor-specific implementation.&lt;br&gt;
To ensure Svelte runs seamlessly on all of them, Svelte will provide an adapter for each. Each adapter does three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;copy Svelte’s compiled client-side JS bundles and other static files to the serverless hosting directory.&lt;/li&gt;
&lt;li&gt;copy Svelte’s server-side JS bundles and a platform specific render function to the serverless functions directory.&lt;/li&gt;
&lt;li&gt;create the redirects configuration with a catch-all route to the serverless render function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The adapter takes platform specific configuration (files) into account. Here’s the gist taken from the &lt;a href="https://github.com/jbmoelker/test-sveltejs-kit/blob/master/node_modules/%40sveltejs/adapter-netlify/index.js"&gt;Svelte adapter for Netlify (&lt;code&gt;@1.0.0-next.0&lt;/code&gt;)&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// get netlify configuration, defined by user in netlify.toml:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;netlify_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;toml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;netlify.toml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;publish&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;netlify_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;functions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;netlify_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// copy static and client files files to static hosting directory:&lt;/span&gt;
&lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;copy_static_files&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;copy_client_files&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// copy the renderer and server files to cloud functions directory:&lt;/span&gt;
&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;copyFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;files/render.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/render/index.js`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;copy_server_files&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/render`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// create _redirects file with catch all route to serverless render function:&lt;/span&gt;
&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/_redirects`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/* /.netlify/functions/render 200&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While I believe adapters are the way forward towards true serverless integration of JavaScript frameworks, I don’t think Svelte is truly serverless-first yet. Here’s why:&lt;/p&gt;

&lt;h2&gt;
  
  
  Serverless-first requires server logic
&lt;/h2&gt;

&lt;p&gt;Part of the fun of serverless rendering over static site generation is being able to respond dynamically to incoming requests. For instance if a request has query parameters or a payload, you'd want to use that request data in your Svelte app. As far as I can tell the adapters and Svelte app don't expose this data to the routes yet. Here’s the &lt;a href="https://github.com/jbmoelker/test-sveltejs-kit/blob/master/node_modules/%40sveltejs/adapter-netlify/files/render.js"&gt;serverless &lt;code&gt;render&lt;/code&gt;function from the Netlify adapter (&lt;code&gt;@1.0.0-next.0&lt;/code&gt;)&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;httpMethod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;queryStringParameters&lt;/span&gt;
    &lt;span class="c1"&gt;// body, // TODO pass this to renderer&lt;/span&gt;
    &lt;span class="c1"&gt;// isBase64Encoded // TODO is this useful?&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// [...] code to append queryStringParameters to query object&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rendered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;httpMethod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="c1"&gt;// TODO&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;isBase64Encoded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Judging by the &lt;code&gt;TODO&lt;/code&gt; comments, this is clearly a work-in-progress. Svelte already passes some request parameters to the Svelte app render function. But as you can see it doesn’t pass the payload &lt;code&gt;body&lt;/code&gt; and &lt;code&gt;host&lt;/code&gt; yet. For meaningful serverless apps, Svelte will need to pass these as well as the missing second argument of this lambda handler (&lt;code&gt;async (event, context)&lt;/code&gt;) and also pass these onto the routes. In addition Svelte will need to normalise the parameters (and context?) best it can between the different serverless providers for a unified developer experience.&lt;/p&gt;

&lt;p&gt;Once Svelte exposes the request and context, developers will want to handle these in their app. A good reference of what this could look like is the recently added &lt;code&gt;[getServerSideProps](https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering)&lt;/code&gt; &lt;a href="https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering"&gt;in Next.js&lt;/a&gt;. Here’s an example of server-side only logic leveraging the request context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// example of server-side only logic in a Next.js route:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;decode&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jws&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getServerSideProps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HS256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app_metadata&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// will be passed to the page component as props&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// render page with user data&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sapper - the Svelte version of Next.js - has &lt;a href="https://sapper.svelte.dev/docs#Preloading"&gt;&lt;code&gt;preload&lt;/code&gt;&lt;/a&gt; which is somewhat similar, but runs both on server- and client-side; and doesn’t expose as much of the context. Whatever it will look like, the new Svelte will need to introduce some way for developers to add their own server-side logic to really be serverless-first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Serverless-first in development mode
&lt;/h2&gt;

&lt;p&gt;Serverless providers including Netlify and Vercel worked hard to offer tooling to make local development as close to production as possible. Both &lt;code&gt;netlify dev&lt;/code&gt; and &lt;code&gt;vercel dev&lt;/code&gt; offer full support for serverless functions. But while Svelte Kit offers adapters for these serverless providers, they come in the shape of a post-build step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build   &lt;span class="c"&gt;# svelte-kit build&lt;/span&gt;
npm run adapt   &lt;span class="c"&gt;# svelte-kit adapt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This feels like an afterthought rather than a serverless-first approach. And this means I can’t test my serverless Svelte app in development, but always need to create a (production) build.&lt;br&gt;
Svelte could achieve serverless-first in development mode by creating the redirects configuration during &lt;code&gt;svelte-kit dev&lt;/code&gt;, connecting the serverless render function to unoptimised server bundles and putting the local server of the selected serverless provider in front of Svelte’s own local server. This way, I can also add my own non-Svelte serverless functions to the project and run them in the same development setup. Svelte already made the client-side development experience instant by adopting Snowpack, now it’s time to do the same for serverless development.&lt;/p&gt;

&lt;h2&gt;
  
  
  A serverless-first future
&lt;/h2&gt;

&lt;p&gt;Seeing as Svelte Kit is in early stages and the Svelte team has already proven they can make a serverless framework with Sapper, I’m optimistic the next Svelte will be truly serverless-first. I hope the serverless functions will be extendible and work in development mode in a future version of Svelte.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>svelte</category>
      <category>netlify</category>
      <category>vercel</category>
    </item>
    <item>
      <title>Dropbox Paper as a headless CMS</title>
      <dc:creator>Jasper Moelker</dc:creator>
      <pubDate>Wed, 30 Jan 2019 12:47:00 +0000</pubDate>
      <link>https://dev.to/jbmoelker/dropbox-paper-as-a-headless-cms-361h</link>
      <guid>https://dev.to/jbmoelker/dropbox-paper-as-a-headless-cms-361h</guid>
      <description>

&lt;p&gt;We turned Dropbox Paper into a headless CMS and use it to publish our content elsewhere. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;note: &lt;a href="https://www.voorhoede.nl/en/blog/dropbox-paper-as-a-headless-cms/"&gt;originally posted on Voorhoede.nl&lt;/a&gt;, so you could read it there too :)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Why? Dropbox Paper is great for editing. We can write in markdown, add rich media, organise documents in folders, content is automatically versioned and others can give feedback in comments. This makes Paper a perfect CMS candidate for document collections like our company playbook. However the Paper UI isn’t ideal for reading, browsing and navigating and a folder with documents doesn’t feel like it’s &lt;em&gt;our playbook&lt;/em&gt;. So this is what we came up with:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yIHfIRLl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/1aam3q8gvizr1yac2ht7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yIHfIRLl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/1aam3q8gvizr1yac2ht7.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Organise and edit content on Dropbox Paper
&lt;/h2&gt;

&lt;p&gt;We start our project content-first. Through the familiar Dropbox Paper UI (&lt;a href="paper.dropbox.com"&gt;paper.dropbox.com&lt;/a&gt;) we write our content in Paper documents and organise them in folders:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hiEDD4p_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rn1d0cr13y4xcm3ude8x.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hiEDD4p_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rn1d0cr13y4xcm3ude8x.jpg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the URL of our root folder (&lt;code&gt;https://paper.dropbox.com/folder/show/Playbook-e.1g...Mxe&lt;/code&gt;), we extract its &lt;code&gt;DIRECTORY_ID&lt;/code&gt; (in our case &lt;code&gt;e.1g...Mxe&lt;/code&gt;), so we can access it programmatically through the API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gather content through the Paper API
&lt;/h2&gt;

&lt;p&gt;We use the &lt;a href="https://www.dropbox.com/developers/documentation/http/documentation#paper"&gt;Dropbox Paper API&lt;/a&gt; to retrieve the folders, documents and their metadata:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We start by &lt;a href="https://www.dropbox.com/developers/documentation/http/documentation#paper-docs-list"&gt;listing all documents&lt;/a&gt; and &lt;a href="https://www.dropbox.com/developers/documentation/http/documentation#paper-docs-get_folder_info"&gt;getting all folders&lt;/a&gt; within our project root folder, filtering by our &lt;code&gt;DIRECTORY_ID&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We then &lt;a href="https://www.dropbox.com/developers/paper-api-alpha#paper-docs-get_metadata"&gt;get the meta data&lt;/a&gt; of each document to filter out “deleted” documents and gather useful meta data like “date last updated”.&lt;/li&gt;
&lt;li&gt;Finally we &lt;a href="https://www.dropbox.com/developers/documentation/http/documentation#paper-docs-download"&gt;download the contents&lt;/a&gt; of each document as markdown.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We write the data from the API into a folder structure, with a markdown file for each document containing the content plus the meta data as Front Matter (data in YAML format in between triple dashed lines) at the top. For example a document written to &lt;code&gt;playbook/voorhoede-events/meetups.md&lt;/code&gt; would look like:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
doc_id: "rzWK0vA059CSQcQdmKydy"
last_updated_date: "2019-01-08T15:24:33Z"
---

# Meetups

We love sharing our learnings and experiences with others ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The full &lt;a href="https://github.com/voorhoede/playbook/blob/master/src/fetch-papers.js"&gt;source of the Paper API integration is on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build and deploy as a static site
&lt;/h2&gt;

&lt;p&gt;With our content from Dropbox Paper written to our filesystem we can do with it whatever we like. Markdown files with Front Matter are a popular input format for static site generators. We’re already using &lt;a href="https://vuepress.vuejs.org/"&gt;VuePress&lt;/a&gt;, so we’re using this to publish our documents as a static site. The code of our project, including the &lt;a href="https://github.com/voorhoede/playbook/tree/master/docs/.vuepress"&gt;VuePress setup&lt;/a&gt; and Paper API integration, are on GitHub. We’re using Netlify to build and deploy a new version of our site on every code change. And this is the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M1YVFK1_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hyc1h2bmwidyacltimng.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M1YVFK1_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hyc1h2bmwidyacltimng.jpg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can view a &lt;a href="https://playbook-demo.netlify.com/"&gt;simplified live demo version of our Playbook&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  /publish from Slack
&lt;/h2&gt;

&lt;p&gt;While our site is automatically updated on &lt;em&gt;code&lt;/em&gt; changes, it doesn’t update on &lt;em&gt;content&lt;/em&gt; changes. 😕 Unfortunately Paper has no means to notify us programmatically when content changes. This means we can’t automatically trigger a new publication. So as developers, we came up with something else close to home: a custom ‘slash command’ to trigger a publication directly from Slack using &lt;code&gt;/playbook&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QoWM2g6H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/4xn7p84119ll86q8sx6y.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QoWM2g6H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/4xn7p84119ll86q8sx6y.jpg" alt="/playbook command to publish from Slack"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that’s it, we’re using Paper as a CMS with a mechanism to publish our content in a custom site, with the look-and-feel we want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Paper API limitations and drawbacks
&lt;/h2&gt;

&lt;p&gt;During our development we ran into a few limitations of the Paper API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Paper API only returns documents opened at least once by the account the API token belongs to.&lt;/li&gt;
&lt;li&gt;Paper’s RPC API is very hard to use compared to all the friendly REST and GraphQL APIs out there today.&lt;/li&gt;
&lt;li&gt;The Paper API returns documents in a “last accessed/modified/created” order which isn’t very useful to us.&lt;/li&gt;
&lt;li&gt;Paper has no &lt;a href="https://en.wikipedia.org/wiki/Webhook"&gt;webhooks&lt;/a&gt; or other mechanism to trigger a new publication on document changes.&lt;/li&gt;
&lt;li&gt;Paper has no “team token” to use for authentication. We ended up creating a Dropbox user (~€300/year) just to have a Playbook token.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Our verdict
&lt;/h2&gt;

&lt;p&gt;Dropbox Paper is very suitable as a headless CMS for document collections. It has great authoring tools. As long as you can work with its limitations, you should keep it in mind for the next time you need a quick CMS.&lt;/p&gt;

&lt;p&gt;Useful links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/voorhoede/playbook"&gt;Project source code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="/"&gt;Dropbox Paper&lt;/a&gt; and the &lt;a href="https://www.dropbox.com/developers/documentation/http/documentation#paper"&gt;Dropbox Paper API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.staticgen.com/"&gt;Static Site Generators&lt;/a&gt; and the &lt;a href="https://jamstack.org/"&gt;JAM Stack&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


</description>
      <category>dropboxpaper</category>
      <category>headlesscms</category>
      <category>jamstack</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
