Getting data

We’ve put some data into our database. Now we need to get it back out.

Create a new remote function in auth.remote.ts. This time we’ll create a query called getUser:

export const getUser = query(async () => {
	const { cookies } = getRequestEvent();

	const session_id = cookies.get('session_id');
	if (!session_id) return null;

	const [result] = await db
		.select({
			id: user.id,
			handle: user.handle,
			display_name: user.display_name,
			avatar_url: user.avatar_url,
			created_at: user.created_at,
			updated_at: user.updated_at
		})
		.from(session)
		.innerJoin(user, eq(session.user_id, user.id))
		.where(eq(session.id, session_id))
		.limit(1);

	return result ?? null;
});

This will look for a session matching session_id, and return the data for the associated user.

We can handle our test user here as well:

export const getUser = query(async () => {
	const { cookies } = getRequestEvent();

	const session_id = cookies.get('session_id');
	if (!session_id) return null;

+	if (dev && session_id === 'test') {
+		const [result] = await db.select().from(user).where(eq(user.id, 'test'));
+		return result;
+	}

	const [result] = await db
		.select({
			id: user.id,
			handle: user.handle,
			display_name: user.display_name,
			avatar_url: user.avatar_url,
			created_at: user.created_at,
			updated_at: user.updated_at
		})
		.from(session)
		.innerJoin(user, eq(session.user_id, user.id))
		.where(eq(session.id, session_id))
		.limit(1);

	return result ?? null;
});

But it’s not enough to get a user or null — on this page we need to guarantee that the user exists. We can do that by creating a companion query:

export const requireUser = query(async () => {
	const user = await getUser();
	if (!user) redirect(302, '/login');

	return user;
});

If user is null, this will redirect to the /login page. We can use this anywhere we need to know that the user is logged in — because redirect throws, requireUser will always return a user object.

One our homepage, add some structure, and an avatar for the logged-in user:

<header></header>

<main></main>

<footer>
	<div class="left">
		<img alt="{user.display_name}'s avatar" src={user.avatar_url} />
	</div>
	<div class="right"></div>
</footer>

<style>
	footer {
		position: fixed;
		bottom: 0;
		height: 6rem;
		padding: 1rem;

		.left {
			height: 100%;
			display: flex;
			gap: 1rem;
		}

		img {
			height: 100%;
			aspect-ratio: 1;
			border-radius: 9999px;
		}
	}
</style>

We need a way to log out. Add a logout form…

export const logout = form(async () => {
	const { cookies } = getRequestEvent();

	const session_id = cookies.get('session_id');
	if (!session_id) return;

	if (dev && session_id === 'test') {
		cookies.delete('session_id', { path: '/' });
		return;
	}

	const [data] = await db.select().from(session).where(eq(session.id, session_id));
	if (!data) return;

	const client = createClient();
	await client.revoke(data.user_id);

	await db.delete(session).where(eq(session.id, session_id));

	cookies.delete('session_id', { path: '/' });
});

…and wire up a button:

<form {...logout}>
	<button>log out</button>
</form>