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
- The client makes a request to a url.
- The server function receives the request, which has an object called locals
- The locals get passed back to the client.
- 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>