Fastify DX and SolidJS in the Real World
Tags: fastifydx, fastify, solidjs, auth0
Published: 2022-07-20 08:56:33

---
cover_image: 'https://zentered.co/images//articles/2022-07/fastify-solidjs.jpeg'
date: '2022-07-15T00:00:00.000Z'
created_at: '2022-07-15T00:00:00.000Z'
edited_at: '2022-07-18T00:00:00.000Z'
crossposted_at: '2022-07-20T08:53:44.064Z'
---
<h2 data-reactroot="">Motivation and Introduction</h2>
<p data-reactroot="">SolidJS ranked #1 in the 2021 &quot;State of JS Front-End Frameworks&quot;, so we wanted
to see what the fuzz is about and give it a proper chance with a side project.
We started with a simple Single Page Application (SPA) and a few components but
wanted to add data with GraphQL. For GraphQL we needed some sort of
authentication, to identify users. This quickly turned more complex day by day.
The biggest pitfalls and challenges were understanding Reactivity in SolidJS and
the use of context providers. Last but not least, we wanted to add some
server-side rendering capabilities, which lead to a three week rabbit hole of
reading SolidJS code, <a href="https://github.com/fastify/fastify-vite">Fastify Vite</a>
and lots of trial and error. Fortunately,
<a href="https://github.com/galvez">Jonas Galvez</a> was already working on
<a href="https://github.com/fastify/fastify-dx">Fastify DX</a> a new full stack framework
based on Fastify and Vite, where he added support for SolidJS as well. A nice
side effect was that we not only have server-side rendering, but also async
rendering, streaming web components/html streaming<a href="https://www.section.io/html-streaming/">1</a> available, which is pretty
awesome. As most of this is new and a lot of those technologies are barely
documented past &quot;Hello World&quot; or &quot;Todo List&quot;, we decided to extract the most
important/difficult parts of our project into this &quot;Real World Application with
SolidJS and Fastify DX&quot;.</p>
<p data-reactroot="">TLDR; You can skip ahead and browse through the code on GitHub:
<a href="https://github.com/zentered/fastify-dx-solidjs-example">Fastify DX with SolidJS</a></p>
<ul data-reactroot="">
<li></li>
</ul>
<h2 data-reactroot="">Overview of The Real World Application</h2>
<p data-reactroot="">Foreword: SolidJS looks a bit like React, but it really isn&#x27;t. Things like
<code>.map()</code> shouldn&#x27;t be done, as it&#x27;s not &quot;reactive&quot;. Make sure you read up on
<a href="https://www.solidjs.com/guides/reactivity">SolidJS Reactivity</a> and the basic
concepts, otherwise you&#x27;ll run into a lot of issues that are simple to avoid.</p>
<p data-reactroot="">Let&#x27;s start with data. We live in amazing times and it&#x27;s really easy and cheap
(or free) to get started with storing and working with data online. Take for
example a <a href="https://planetscale.com">PlanetScale</a> MySQL-compatible database,
<a href="https://fastify.io">Fastify</a> Node.js Server, <a href="https://www.prisma.io">Prisma</a>
database mapper and a <a href="https://www.prisma.io/graphql">GraphQL</a> connector like
<a href="https://github.com/mercurius-js/mercurius">Mercurius</a> and you have an entire
backend stack. For this example we assume you already have a backend or you want
to connect to a 3rd party database like the
<a href="https://docs.github.com/en/graphql">GitHub GraphQL API</a>.</p>
<p data-reactroot="">To retrieve data from your own or a 3rd party API, you most likely need access
tokens, this is where <a href="https://auth0.com">Auth0</a> comes in. You can either
retrieve JWT tokens for your own back-end and decrypt them on your API, or
retrieve access tokens for the social providers such as GitHub, Twitter, etc. to
interact with their API. In our example we decided to use the GitHub GraphQL API
to simply retrieve a list of popular repositories for the logged in user, and
let them browser to the repository detail page.</p>
<p data-reactroot="">To sum up the architecture:</p>
<ul data-reactroot="">
<li>Front-end: SolidJS</li>
<li>Server-side rendering &amp; streaming: Fastify DX</li>
<li>Data layer: GraphQL</li>
<li>Backend: GitHub GraphQL API</li>
<li>Authentication: Auth0 with GitHub Social Connection</li>
</ul>
<p data-reactroot="">Let&#x27;s get started with the authentication setup.</p>
<h2 data-reactroot="">Getting started with Auth0</h2>
<figure class="my-12 mx-12" data-reactroot=""><img src="https://zentered.co//images/articles/2022-07/unauthorized-fallback.png" width="643" height="473" alt="screenshot of our login screen"/><figcaption>Login Screen as fallback</figcaption></figure>
<p data-reactroot=""><em>Hint:</em> There&#x27;s a <code>env.example</code> file in the repository which you can rename to
<code>.env</code> and store the variables from the next few steps.</p>
<p data-reactroot="">Auth0 provides the <a href="https://github.com/auth0/auth0-spa-js">auth0-spa-js</a> package
which offers two ways to authenticate users:</p>
<ul data-reactroot="">
<li>login with popup</li>
<li>login with redirect</li>
</ul>
<p data-reactroot="">Usually the login with redirect is preferable, but there are some issues
retaining the state / session when coming back from the login, so we&#x27;re
currently using &quot;login with popup&quot;. The function opens &quot;Auth0 Lock&quot;, a
pre-defined UI that has all the different authentication mechanisms that are
enabled for the application, and can be customized in the Auth0 settings. In
order to retrieve GitHub access tokens, we need:</p>
<ol data-reactroot="">
<li>Auth0 Single Page Application to handle the login on our web app</li>
<li>GitHub OAuth App to communicate with Auth0 securely and issue GraphQL API
tokens</li>
<li>Auth0 Machine to Machine Application to retrieve access tokens for the GitHub
GraqhQL API through Auth0</li>
</ol>
<h3 data-reactroot="">Auth0 Single Page Application</h3>
<p data-reactroot="">This is the primary Auth0 application we&#x27;ll use. Create a new Single Page
Application in Auth0 and copy the client id and domain into <code>.env</code>, (variables
starting with <code>VITE_AUTH0_</code>). Make sure you add <code>http://localhost:8080</code> to the
<em>Allowed Callback URL</em>, <em>Allowed Web Origin</em> and <em>Allowed Origin</em>.</p>
<figure class="my-12 mx-12" data-reactroot=""><img src="https://zentered.co//images/articles/2022-07/auth0-app-settings.png" width="1046" height="563" alt="screenshot showing the auth0 application client id and secret"/><figcaption>Auth0 application client id and secret</figcaption></figure>
<h3 data-reactroot="">GitHub OAuth App</h3>
<p data-reactroot="">Go into your <a href="https://github.com/settings/developers">Developer Settings</a> and
create a new OAuth App. Name, homepage etc. are not important, but the
<code>Authorization callback URL</code> needs to point to your Auth0 Tenant. You can get
the domain in your Auth0 application settings:
<code>https://&lt;auth0-app-DOMAIN&gt;.auth0.com</code>.</p>
<figure class="my-12 mx-12" data-reactroot=""><img src="https://zentered.co//images/articles/2022-07/auth0-app-domain.png" width="576" height="94" alt="screenshot showing the auth0 domain"/><figcaption>where to find the Auth0 domain</figcaption></figure>
<figure class="my-12 mx-12" data-reactroot=""><img src="https://zentered.co//images/articles/2022-07/github-oauth-settings.png" width="1050" height="1394" alt="screenshot showing the github oauth app settings"/><figcaption>GitHub OAuth App settings</figcaption></figure>
<h3 data-reactroot="">Auth0 Social Connections</h3>
<p data-reactroot="">Go to <code>Authentication -&gt; Social</code> and <code>Create Connection</code>. Select GitHub. Pick a
name and copy the Client ID from your GitHub OAuth App. Go back to the GitHub
OAuth App settings and click <code>Create a new client secret</code>. Copy that into the
Client Secret in the Auth0 connection details. For this app we only need
<code>public_repo</code> as a permission. You should enable
<code>Sync user profile attribute at each login</code> to retrieve the user details from
GitHub.</p>
<img src="https://zentered.co//images/articles/2022-07/auth0-lock.png" width="512" height="712" alt="screenshot showing auth0 lock with github social login" data-reactroot=""/>
<p data-reactroot="">After saving, go back to your Single Page Application, to the tab <code>Connections</code>
and add the GitHub Social Connection. You should disable the Database connection
to avoid issues later.</p>
<h3 data-reactroot="">Auth0 Machine to Machine Application</h3>
<p data-reactroot="">We use a Machine to Machine Application to retrieve the Access Token for the
GitHub API through Auth0. This can&#x27;t be done directly on the client.</p>
<p data-reactroot="">Go back to Applications, <code>Create Application</code> and pick
<code>Machine to Machine Application</code>. Enter a name, and copy the client id, secret
and domain into <code>.env</code> (variables starting with <code>AUTH0_</code>). Go to <code>APIs</code> and
Authorize the application to the Auth0 Management API. The only permission
needed is <code>read:user_idp_tokens</code>.</p>
<h3 data-reactroot="">Auth Summary</h3>
<p data-reactroot="">The above steps look complicated, but they&#x27;re actually just a few clicks and
values to copy&amp;paste to have a complete and secure user authentication setup.
The UI and UX comes out of the box from Auth0, so there&#x27;s very little we need to
do for a great signup experience.</p>
<p data-reactroot="">We created two &quot;Auth0 Applications&quot;, the Single Page App to handle all things
login/user/etc. and the Machine to Machine App/Client to talk to the Management
API for the GitHub Access Tokens. We also created a GitHub OAuth App and
connected that with Auth0, so they can talk to each other and retrieve tokens.
This will allow us to:</p>
<ul data-reactroot="">
<li>handle user login and signup through &quot;Auth0 Lock&quot;</li>
<li>synchronize user data from GitHub to the Auth0 profile</li>
<li>issue JWT tokens for our own web app and back-end</li>
<li>retrieve GitHub access tokens for the GraphQL API on our web server</li>
</ul>
<p data-reactroot="">Now, to the code!</p>
<img src="https://zentered.co//images/articles/2022-07/lets-code.jpeg" width="591" height="591" alt="Let&#x27;s code hand dranw vector illustration in cartoon style. Happy programmer" data-reactroot=""/>
<h2 data-reactroot="">Fastify DX</h2>
<p data-reactroot="">Fastify DX is rad. You can use it with Vue, React, Svelte etc. and it runs on
our most beloved Node.js framework Fastify. Here&#x27;s all we need to start a
server:</p>
<pre data-reactroot=""><code class="language-js">import Fastify from &#x27;fastify&#x27;
import FastifyVite from &#x27;fastify-vite&#x27;
import FastifyDXSolid from &#x27;fastify-dx-solid&#x27;

const port = process.env.PORT ?? 5000
const logger = process.env.LOGGER ?? false

const server = Fastify({
  logger: logger
})

await server.register(FastifyVite, {
  root: import.meta.url,
  renderer: FastifyDXSolid
})

await server.vite.ready()
await server.listen({ host: &#x27;0.0.0.0&#x27;, port: port })
</code></pre>
<p data-reactroot="">There&#x27;s not much going on here; we&#x27;re starting up Fastify and use the
FastifyVite plugin to build and render the Vite app on the fly. But since this
is a fully functional web server, we can also add our code to retrieve the
GitHub Access Tokens for the UI that we&#x27;ll need later:</p>
<pre data-reactroot=""><code class="language-js">import { ManagementClient } from &#x27;auth0&#x27;

server.post(&#x27;/api/token&#x27;, async (request, reply) =&gt; {
  const userId = request.body.userId

  const management = new ManagementClient({
    domain: process.env.AUTH0_DOMAIN,
    clientId: process.env.AUTH0_CLIENT_ID,
    clientSecret: process.env.AUTH0_CLIENT_SECRET,
    scope: &#x27;read:user_idp_tokens&#x27;
  })

  const user = await management.getUser({ id: userId })
  if (user &amp;&amp; user.identities) {
    return reply.send({ token: user.identities[0].access_token })
  }
  return reply.send({ ok: true })
})
</code></pre>
<p data-reactroot="">In our <code>vite.config.js</code> we&#x27;ll add the SolidJS plugins and everything else needed
to render and build the SolidJS App. We&#x27;ll also add the
<a href="https://github.com/unocss/unocss">unocss</a> engine. Some modules such as
<code>solid-app-router</code> or <code>solid-headless</code> require special handling for SSR, so they
have to be added into the <code>noExternal</code> array in the config.</p>
<pre data-reactroot=""><code class="language-js">import { defineConfig } from &#x27;vite&#x27;
import viteSolid from &#x27;vite-plugin-solid&#x27;
import unocss from &#x27;unocss/vite&#x27;
import viteSolidFastifyDX from &#x27;fastify-dx-solid/plugin&#x27;
import { join, dirname } from &#x27;path&#x27;
import { presetAttributify, presetUno, presetTypography } from &#x27;unocss&#x27;
import { fileURLToPath } from &#x27;url&#x27;

const path = fileURLToPath(import.meta.url)
const root = join(dirname(path), &#x27;src&#x27;) // &#x27;client&#x27;, &#x27;src&#x27;, ...

export default defineConfig({
  root: root,
  plugins: [
    viteSolid({ ssr: true }),
    viteSolidFastifyDX(),
    unocss({
      presets: [presetUno(), presetAttributify(), presetTypography()]
    })
  ],
  ssr: {
    noExternal: [&#x27;solid-app-router&#x27;]
  }
})
</code></pre>
<p data-reactroot="">To get your app up and running, copy the <code>context.js</code>, <code>index.html</code>, <code>index.js</code>
and <code>root.jsx</code> from the <code>src/</code> folder or directly from the
<a href="https://github.com/fastify/fastify-dx/tree/main/starters/solid">Fastify DX Solid Starter Kit</a>
(or the TypeScript version here:
<a href="https://github.com/fastify/fastify-dx/tree/main/starters/solid-ts">Fastify DX Solid-TS</a>).</p>
<p data-reactroot="">When running <code>pnpm dev</code>, you should get a basic web server up and running
already, so let&#x27;s move forward to the SolidJS app. Any errors, refer to the
example code or the Fastify DX starter kit, there are a few tiny details which
are easy to miss (so best just copy &amp; paste).</p>
<h2 data-reactroot="">SolidJS</h2>
<p data-reactroot="">At the time of putting together this application, this was the documented
example for <code>renderToStream</code>:</p>
<pre data-reactroot=""><code class="language-js">// node
renderToStream(App).pipe(res)

// web stream
const { readable, writable } = new TransformStream()
renderToStream(App).pipeTo(writable)
</code></pre>
<p data-reactroot="">That&#x27;s not much to go with, so this has been quite a challenge, mostly solved by
Jonas with Fastify DX. With SSR and streaming, there come a few new issues that
require special attention; the fact that some parts are pre-rendered on the
server and have a different context and state. Fastify DX provides <code>context.js</code>,
a way to share state between client and server, which is super helpful. Using
the context providers properly and taking care of the reactivity chain is a
must.</p>
<p data-reactroot="">To make things slightly easier, we require auth for our entire app, so there are
no authenticated/unauthenticated routes to deal with. We also need the data
(GraphQL) context pretty much everywhere, so we&#x27;ll wrap our app in those two
contexts:</p>
<pre data-reactroot=""><code class="language-jsx">&lt;Auth&gt;
  &lt;GraphQL&gt;
    &lt;App /&gt;
  &lt;/GraphQL&gt;
&lt;/Auth&gt;
</code></pre>
<p data-reactroot="">The tricky part is that auth and GraphQL are both async, means we&#x27;ll have to
wait for the auth client to be initialized and authenticated, and then
initialize and authenticate the GraphQL client. This was very painful to figure
out and lucky for you I&#x27;ve gone through that ordeal so you can just copy&amp;paste
the solution:</p>
<pre data-reactroot=""><code class="language-jsx">return (
  &lt;Auth0
    domain={import.meta.env.VITE_AUTH0_DOMAIN}
    clientId={import.meta.env.VITE_AUTH0_CLIENT_ID}
  &gt;
    &lt;SiteRequiresAuth&gt;
      &lt;Router url={props.url}&gt;
        &lt;Routes&gt;
          {
            // eslint-disable-next-line solid/prefer-for
            props.payload.routes.map((route) =&gt; (
              &lt;Route
                path={route.path}
                element={
                  &lt;DXRoute
                    state={props.payload.serverRoute.state}
                    path={route.path}
                    payload={props.payload}
                    component={route.component}
                  /&gt;
                }
              /&gt;
            ))
          }
        &lt;/Routes&gt;
      &lt;/Router&gt;
    &lt;/SiteRequiresAuth&gt;
  &lt;/Auth0&gt;
)
</code></pre>
<p data-reactroot="">Since we&#x27;re on the front-end, <code>process.env</code> doens&#x27;t work; we use
<code>import.meta.env</code> instead. Vite automatically exposes all variables that are
prefixed with <code>VITE_</code>. The Auth0 context provides a client and [reactive
Signals](https://www.solidjs.com/guides/reactivity#introducing-primitives], but
those is only available <em>within</em> the Auth0 context provider, so we&#x27;ll need a
wrapper function <code>SiteRequiresAuth</code> to get the initialized Auth0 context, fetch
the GitHub access token and initialize the GraphQL client. Only then do we show
any content of the site.</p>
<pre data-reactroot=""><code class="language-jsx">function SiteRequiresAuth(props) {
  const auth0 = useAuth0()
  const [accessToken] = createResource(() =&gt; auth0.userId(), githubAuth)

  return (
    &lt;Show
      when={auth0.isInitialized() &amp;&amp; auth0.isAuthenticated()}
      fallback={&lt;Login auth0={auth0} /&gt;}
    &gt;
      &lt;Show when={accessToken()}&gt;
        &lt;GraphQLProvider accessToken={accessToken}&gt;
          {props.children}
        &lt;/GraphQLProvider&gt;
      &lt;/Show&gt;
    &lt;/Show&gt;
  )
}
</code></pre>
<p data-reactroot="">At this point I&#x27;d recommend to read up on
<a href="https://www.solidjs.com/docs/latest/api#basic-reactivity">Basic Reactivity</a> and
get familiar with the primitives.</p>
<p data-reactroot="">We use the control flow functions <code>&lt;Show&gt;</code>, as they update when the signals (ie.
<code>isInitialized()</code>) change or when the asynchronous resource <code>accessToken</code> is
ready. As long as the Auth0 client is not initialized or authenticated, we&#x27;ll
show the Login component. Once those are ready, we&#x27;ll have to wait again for the
<code>accessToken</code> and then finally show the rest of the site.</p>
<p data-reactroot="">The context providers are fairly useful now, as the variables are available
through the entire site. Anywhere, you can just <code>const auth = useAuth0()</code> and
run <code>auth.user()</code> to fetch the user details for example.</p>
<h3 data-reactroot="">Fetching Data</h3>
<img src="https://zentered.co//images/articles/2022-07/fetch.jpeg" width="662" height="527" alt="Playful male lion carrying stick" data-reactroot=""/>
<p data-reactroot="">Now that we have an authenticated GraphQL client, we&#x27;re able to run queries,
mutations etc. easily. Here&#x27;s an example to retrieve the 10 &quot;most starred&quot;
repos:</p>
<pre data-reactroot=""><code class="language-js">import { gql } from &#x27;@urql/core&#x27;
import { useGraphQL } from &#x27;../contexts/GraphQL&#x27;

export default async function repos() {
  const gqlClient = useGraphQL()
  return gqlClient()
    ?.query(
      gql`
        query ($number_of_repos: Int!) {
          viewer {
            name
            repositories(
              orderBy: { field: STARGAZERS, direction: DESC }
              first: $number_of_repos
            ) {
              nodes {
                name
                stargazerCount
              }
            }
            login
          }
        }
      `,
      {
        number_of_repos: 10
      }
    )
    .toPromise()
}
</code></pre>
<p data-reactroot="">We&#x27;re retrieving the (authenticated) client context and run the query, simple.</p>
<pre data-reactroot=""><code class="language-jsx">import dataGitHubRepos from &#x27;../data/github-repos.data&#x27;
const [repos] = createResource(dataGitHubRepos)
</code></pre>
<p data-reactroot=""><code>createResource</code> is a
<a href="https://www.solidjs.com/docs/latest/api#createresource">SolidJS reactive component</a>
that processes async data and hydrates the components. This can be
async/sync/streamed/....</p>
<h3 data-reactroot="">Pages &amp; Routing</h3>
<p data-reactroot="">Fastify DX follows the same routing principles as <a href="https://nextjs.com">Next.js</a>
and <a href="https://remix.run">Remix</a>. The first page is <code>/pages/index.{js|ts}</code> and
other pages can be linked to by using
<a href="https://github.com/solidjs/solid-app-router">solid-app-router</a>.
<code>&lt;Link href=&quot;/dashboard&quot;&gt;Dashboard&lt;/Link&gt;</code> would link to
<code>/pages/dashboard.{js|ts}</code> and
<code>&lt;Link href=&quot;/articles/awesome-solidjs&quot;&gt;SolidJS Article&lt;/Link&gt;</code> would link to
<code>/pages/articles/[id].{js|ts}</code>. SSR, Streaming etc. can be fine-tuned by
exporting variables in the page. Check out the examples for streaming, SSR, etc.
in the
<a href="https://github.com/fastify/fastify-dx/tree/main/starters/solid/client/pages">fastify dx starter kit</a></p>
<h3 data-reactroot="">Components</h3>
<p data-reactroot="">Components work fairly similar to React, you can either <code>import</code> or
<a href="https://www.solidjs.com/docs/latest/api#lazy"><code>lazy()</code> load them</a>. Make sure
you use reactive primitives such as Signals, Memos, Resources etc. and pass them
to the component. SolidJS re-renders the component when the values change.
&quot;Debugging&quot; with <code>console.log</code> is fairly useless, as most variables are only
updated when the reactive signals kick in. Sometimes just using
<code>&lt;div&gt;{val()}&lt;/div&gt;</code> helps to see what&#x27;s going on, but we&#x27;re definitely looking
for better ways to debug code in SolidJS.</p>
<h2 data-reactroot="">Useful Links, Caveats and TODOs</h2>
<ul data-reactroot="">
<li><a href="https://github.com/thetarnav/solid-devtools">SolidJS Developer Tools</a></li>
<li><a href="https://www.solidjs.com/guides/reactivity">SolidJS Guide to Reactivity</a></li>
</ul>
<p data-reactroot="">One issue at the moment is the hard refresh. The state is lost, the cookies gone
and the application shows our login screen. That&#x27;s fairly annoying during
development.</p>
<p data-reactroot="">Another dent in the developer experience are unhandled client-side errors. They
break the reload/fresh cycle and the app becomes unresponsive. A full reload is
needed, causing the login screen to show again.</p>
<p data-reactroot="">Fastify DX also offers data function and a really amazing shared <code>context</code>,
which we&#x27;d like to add some more examples for.</p>
<h2 data-reactroot="">Questions, discussion and feedback</h2>
<img src="https://zentered.co//images/articles/2022-07/thank-you.jpeg" width="699" height="499" alt="Cartoon Computer With the words Thank You!" data-reactroot=""/>
<p data-reactroot="">Everything looks simple after the fact, but this was a lot of work and effort to
piece together all the components and figure out how things fit together. We&#x27;re
hoping to resolve the last few issues and have a boilerplate or starting point
for your next SolidJS application, that you can use and not go through the same
issues again.</p>
<p data-reactroot="">Special Thanks to <a href="https://github.com/galvez">Jonas Galvez</a> for the amazing work
at Fastify DX and the long, late night discussions we had.</p>
<p data-reactroot="">Thanks to the entire SolidJS team for this new exciting framework. Feels great
to work with and we&#x27;re looking forward to build more apps with SolidJS.</p>
<p data-reactroot="">You can find the source code here:
<a href="https://github.com/zentered/fastify-dx-solidjs-example">Fastify DX and SolidJS Example on GitHub</a>.</p>
<p data-reactroot=""><strong>If you have any questions or comments</strong>, please
<a href="https://twitter.com/zenteredco">reach out on Twitter</a> or
<a href="https://github.com/zentered/feedback/discussions/4">join the discussion on GitHub</a>.</p>