Skip to main content

Overview

The Timeback SDK provides a SvelteKit adapter using hooks.

Installation

npm install @timeback/sdk

Server Setup

Create a Timeback instance:
src/lib/timeback.ts
import { createTimeback } from '@timeback/sdk'

export const timeback = await createTimeback({
	env: 'staging',
	api: {
		clientId: process.env.TIMEBACK_API_CLIENT_ID!,
		clientSecret: process.env.TIMEBACK_API_CLIENT_SECRET!,
	},
	identity: {
		mode: 'sso',
		clientId: process.env.AWS_COGNITO_CLIENT_ID!,
		clientSecret: process.env.AWS_COGNITO_CLIENT_SECRET!,
		redirectUri: 'http://localhost:3000/api/timeback/identity/callback',
		onCallbackSuccess: async ({ user, state, redirect }) => {
			await setSession({ id: user.id, email: user.email })
			return redirect(state?.returnTo ?? '/')
		},
		onCallbackError: ({ error, redirect }) => {
			return redirect('/?error=sso_failed')
		},
		getUser: () => getSession(),
	},
})

Server Hooks

Add the Timeback handler to your hooks:
src/hooks.server.ts
import { svelteKitHandler } from '@timeback/sdk/svelte-kit'
import { building } from '$app/environment'
import { timeback } from '$lib/timeback'

import type { Handle } from '@sveltejs/kit'

export const handle: Handle = ({ event, resolve }) => {
	return svelteKitHandler({
		timeback,
		event,
		resolve,
		building,
	})
}

Alternative: Route-Based Handler

For more control, you can use a route-based approach instead of hooks:
src/routes/api/timeback/[...path]/+server.ts
import { toSvelteKitHandler } from '@timeback/sdk/svelte-kit'
import { timeback } from '$lib/timeback'

const handlers = toSvelteKitHandler(timeback)

export const GET = handlers.GET
export const POST = handlers.POST

Client Setup

Initialize Timeback in your root layout:
src/routes/+layout.svelte
<script>
  import { initTimeback } from '@timeback/sdk/svelte'

  initTimeback()

  let { children } = $props()
</script>

{@render children()}

Usage

For the full client-side API, see the Svelte client adapter.
Use stores in components:
src/routes/+page.svelte
<script>
  import { onMount, onDestroy } from 'svelte'
  import { SignInButton, timeback } from '@timeback/sdk/svelte'

  let activity

  onMount(() => {
    if ($timeback) {
      activity = $timeback.activity.start({
        id: 'lesson-1',
        name: 'Introduction',
        course: { subject: 'Math', grade: 3 },
      })
    }
  })

  onDestroy(() => activity?.end())
</script>

{#if !$timeback}
  <SignInButton size="lg">Sign In</SignInButton>
{:else}
  <div>Welcome!</div>
{/if}

Profile Store

<script>
  import {
    timebackProfile,
    timebackProfileCanFetch,
    fetchTimebackProfile
  } from '@timeback/sdk/svelte'
</script>

<button onclick={fetchTimebackProfile} disabled={!$timebackProfileCanFetch}>
  {$timebackProfile.status === 'loading' ? 'Loading...' : 'Load Profile'}
</button>

{#if $timebackProfile.status === 'loaded'}
  <div>XP: {$timebackProfile.profile.xp.today}</div>
{/if}

Next Steps