First published 2024.11.18

Building an App With the Data Collective

Question: How do we structure an application to use the Stucco Data Collective?

The in-house application archicture for Stucco Software projects is an edge-rendered application built with SvelteKit. Each page is rendered server-side as a function call, and delivered to the client with a minimum of JS required. Often, these pages are then themselves quite interactive. This is a model of app that Rich Harris calls a “Hybrid” or “Transitional” application. I’ve found that it allows for a rich variety of project – from HTTP APIs to static content sites with the flexibillity to grow in any direction needed.

An Overview of the Request Flow

  1. The client makes a request to a url.
  2. The server function receives the request, which has an object called locals
  3. The locals get passed back to the client.
  4. Client side code sets cookie

This is how data round-trips a session data between a persitent client and ephemeral server functions. If it’s a new session, the server hook geberates a new session id, and passes that to the client, who persists it as a cookie, which gets passed back to the server hooks.

Logging In

By default, we don’t want critical path javascript running in the client. If we were writing a completely static client-side app, this would be a different story but thats a somewhat different architecture which we’ll address in the future.

This means we log in on the server as an action from a form post!

/src/routes/auth/+page.server.js

import { redirect } from '@sveltejs/kit'
import {
  getSessionFromStorage,
  Session
} from "@inrupt/solid-client-authn-node"

const redirectToSolidIdentityProvider = (url) => {
  redirect(302, url)
}

export const actions = {
  login: async ({cookies, request}) => {
    const session = new Session({ keepAlive: false })
    cookies.set('session', session.info.sessionId, {path: '/'})
    await session.login({
      redirectUrl: `http://localhost:5174/`,
      oidcIssuer: "https://login.stucco.software",
      clientName: "Stucco Data Collective",
      handleRedirect: redirectToSolidIdentityProvider,
    })
  },
  logout: async({cookies}) => {
    const sessionId = cookies.get('session')
    const session = await getSessionFromStorage(sessionId)
    await session.logout()
    cookies.delete('session', {path: '/'})
    redirect(302, 'http://localhost:5174/')
  }
}
/src/hooks.server.js

import { getSessionFromStorage } from "@inrupt/solid-client-authn-node"

export async function handle({ event, resolve }) {
  let sessionId = event.cookies.get('session')
  if (!sessionId) {
    event.locals.session = null
  } else {
    const session = await getSessionFromStorage(sessionId)
    await session.handleIncomingRedirect(event.url.href)
    event.locals.session = session.info
  }
  const response = await resolve(event)
  return response
}

And the login button itself:

  <form
    action="/auth?/login"
    method="POST">
    <button>loggin</button>
  </form>