Skip to content
Merged
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
9980684
Improve session docs
amirhhashemi Feb 13, 2025
ac3aad3
Merge branch 'main' into improve-session-docs
kodiakhq[bot] Feb 13, 2025
a826687
Merge branch 'main' into improve-session-docs
kodiakhq[bot] Feb 13, 2025
9e15094
Merge branch 'main' into improve-session-docs
kodiakhq[bot] Feb 16, 2025
9124606
Merge branch 'main' into improve-session-docs
kodiakhq[bot] Feb 16, 2025
45e64d2
Update src/routes/solid-start/advanced/session.mdx
amirhhashemi Feb 25, 2025
148286a
Update src/routes/solid-start/advanced/session.mdx
amirhhashemi Feb 25, 2025
028e66b
Update src/routes/solid-start/advanced/session.mdx
amirhhashemi Feb 25, 2025
6b09f31
Update src/routes/solid-start/advanced/session.mdx
amirhhashemi Feb 25, 2025
b3c701c
Update src/routes/solid-start/advanced/session.mdx
amirhhashemi Feb 25, 2025
8794ee1
Update src/routes/solid-start/advanced/session.mdx
amirhhashemi Feb 25, 2025
6ff489f
Update src/routes/solid-start/advanced/session.mdx
amirhhashemi Feb 25, 2025
a6a5bc4
Update src/routes/solid-start/advanced/session.mdx
amirhhashemi Feb 25, 2025
93d50fe
Update src/routes/solid-start/advanced/session.mdx
amirhhashemi Feb 25, 2025
924aa81
Update src/routes/solid-start/advanced/session.mdx
amirhhashemi Feb 25, 2025
83b8ff7
update
amirhhashemi Feb 25, 2025
be782d2
Merge branch 'main' into improve-session-docs
LadyBluenotes Feb 25, 2025
59cc003
Merge branch 'main' into improve-session-docs
kodiakhq[bot] Feb 26, 2025
1c6c6e9
Merge branch 'main' into improve-session-docs
kodiakhq[bot] Feb 26, 2025
c8279d5
Merge branch 'main' into improve-session-docs
kodiakhq[bot] Feb 26, 2025
a7d15af
Merge branch 'main' into improve-session-docs
kodiakhq[bot] Mar 4, 2025
0c0f769
Merge branch 'main' into improve-session-docs
kodiakhq[bot] Mar 4, 2025
c5a1c20
Merge branch 'main' into improve-session-docs
kodiakhq[bot] Mar 6, 2025
85328a1
Merge branch 'main' into improve-session-docs
kodiakhq[bot] Mar 7, 2025
03f347a
Merge branch 'main' into improve-session-docs
kodiakhq[bot] Mar 7, 2025
96a4990
Merge branch 'main' into improve-session-docs
kodiakhq[bot] Mar 7, 2025
586162b
Merge branch 'main' into improve-session-docs
kodiakhq[bot] Mar 7, 2025
638ee46
Merge branch 'main' into improve-session-docs
kodiakhq[bot] Mar 7, 2025
c18d80c
Merge branch 'main' into improve-session-docs
kodiakhq[bot] Mar 8, 2025
5a3f91f
Merge branch 'main' into improve-session-docs
kodiakhq[bot] Mar 8, 2025
139324d
Update src/routes/solid-start/advanced/session.mdx
amirhhashemi Mar 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 101 additions & 70 deletions src/routes/solid-start/advanced/session.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,98 +2,129 @@
title: "Sessions"
---

When user information is required, it is usually done by checking the request for information.
The best way for the client and server to do that is using cookies.
Sessions allow web applications to maintain state between user requests.
Since HTTP is stateless, each request is treated independently.
Sessions address this by allowing the server to recognize multiple requests from the same user, which is helpful for tracking authentication and preferences.

The `Request` object can be used to access the `Cookie` Headers, which can then be parsed to get the value for that specific cookie.
For example, `"session"` can be used to identify the session.
Fortunately, Nitro comes ready with helpers that enable this.
## How sessions work

For example, if you wanted to use a cookie to identify a user, you can use the `useSession` helper from `vinxi/http`:
A session typically involves:

```tsx title="/lib/session.ts"
1. **Session creation**: When tracking is needed (e.g., upon login or first visit), the server creates a session.
This involves generating a unique **session ID** and storing the session data, _encrypted and signed_, within a cookie.
2. **Session cookie transmission**: The server sends a `Set-Cookie` HTTP header.
This instructs the browser to store the session cookie.
3. **Subsequent requests**: The browser automatically includes the session cookie in the `Cookie` HTTP header for requests to the server.
4. **Session retrieval and data access**: For each request, the server checks for the session cookie, retrieves the session data if a cookie is present, then decrypts and verifies the signature of the session data for the application to access and use this data.
5. **Session expiration and destruction**: Sessions typically expire after a period of time or upon user sign-out and the data is removed.
This is done by setting a `Max-Age` attribute on the cookie or by sending a `Set-Cookie` HTTP header with an expired date.

Most of these steps are automatically managed by the [session helpers](#session-helpers).

### Database sessions

For larger applications or when more robust session management is required, SolidStart also supports storing session data in a database.
This approach is similar to the cookie-based approach, but with some key differences:

- The session data is stored in the database, associated with the session ID.
- Only the session ID is stored in the cookie, not the session data.
- The session data is retrieved from the database using the session ID, instead of being retrieved directly from the cookie.
- Upon expiration, in addition to the session cookie, the database record containing the session data is also removed.

SolidStart does not automatically handle interactions with a database; you need to implement this yourself.

## Session helpers

[Vinxi](https://vinxi.vercel.app), the underlying server toolkit powering SolidStart, provides helpers to simplify working with sessions.
It provides a few key session helpers:

- [`useSession`](https://vinxi.vercel.app/api/server/session.html#usesession): Initializes a session or retrieves the existing session and returns a session object.
- [`getSession`](https://vinxi.vercel.app/api/server/session.html#getsession): Retrieves the current session or initializes a new session.
- [`updateSession`](https://vinxi.vercel.app/api/server/session.html#updatesession): Updates data within the current session.
- [`clearSession`](https://vinxi.vercel.app/api/server/session.html#clearsession): Clears the current session.

These helpers work _only_ in server-side contexts, such as within server functions and API routes.
This is because session management requires access to server-side resources as well as the ability to get and set HTTP headers.

For more information, see the [Cookies documentation in the Vinxi docs](https://vinxi.vercel.app/api/server/session.html).

## Creating a session

The `useSession` helper is the primary way to create and manage sessions.
It provides a comprehensive interface for all session operations.

```ts title="src/lib/session.ts"
import { useSession } from "vinxi/http";
export async function getUser(request: Request) {
const session = await useSession({
password: process.env.SESSION_SECRET
});

type SessionData = {
theme: "light" | "dark";
};

export async function useThemeSession() {
"use server";
const session = await useSession<SessionData>({
password: process.env.SESSION_SECRET as string,
name: "theme",
});

if (!session.data.theme) {
await session.update({
theme: "light",
});
}

return session;
}
```

The session cookie can be used to get the session data about the request.
How the session data is stored and retrieved, however, is up to the implementation of the `useSession`.
In this example, the `useThemeSession` server function creates a session that stores a user's theme preference.

Typically, `userId` will be saved in the session data and if it is not found, it indicates that the request was not authenticated.
The `getUser` function returns a `null` when it does not find a user and if a user is found, it will be used to get the user from the database:
`useSession` requires a strong password for encrypting and signing the session cookie.
This password must be at least 32 characters long and should be kept highly secure.
It is strongly recommended to store this password in a [private environment variable](/configuration/environment-variables#private-environment-variables), as shown in the example above, rather than hardcoding it in your source code.

```tsx title="/lib/session.ts"
import { useSession } from "vinxi/http";
A password can be generated using the following command:

export async function getUser(): Promise<User | null> {
const session = await useSession({
password: process.env.SESSION_SECRET
});
const userId = session.data.userId;
if (!userId) return null;
return await store.getUser(userId);
}
```sh frame="none"
openssl rand -base64 32
```

This helper can be used wherever you want to authenticate the request, including in server functions and [API routes](/solid-start/building-your-application/api-routes).
`useSession` adds a `Set-Cookie` HTTP header to the current server response.
By default, the cookie is named `h3`, but can be customized with the `name` option, as shown in the example above.

Additionally, you can use it with [`query`](/solid-router/reference/data-apis/query) from `solid-router` to make sure that only authenticated users can access the data.
That way if the user is not authenticated, the request will be redirected to the login page.
## Getting the session data

```tsx title="/routes/api/store/admin.ts"
import { query, createAsync, redirect } from "@solidjs/router";
The `useSession` helper provides access to the session data from the current request with the `data` property.

const getUsers = query(async (id: string) => {
"use server";
const user = await getUser();
if (!user) throw redirect("/login");
return store.getUsers(id, "*");
}, "users");
```ts title="src/lib/session.ts"
export async function getThemeSession() {
"use server";
const session = await useThemeSession();

// page component
export default function Users() {
const users = createAsync(() => getUsers());
return session.data.theme;
}
```

This also allows logging in and out of the session in a similar manner:
## Updating the session data

```tsx title="/routes/session.server.ts"
import { redirect } from "@solidjs/router";
import { useSession } from "vinxi/http";
The `useSession` helper provides the `update` method to update the session data from the current request.

type UserSession = {
userId?: number;
};

function getSession() {
return useSession({
password: process.env.SESSION_SECRET
});
```ts title="src/lib/session.ts"
export async function updateThemeSession(data: SessionData) {
"use server";
const session = await useThemeSession();
await session.update(data);
}
```

export async function login(formData: FormData) {
const username = String(formData.get("username"));
const password = String(formData.get("password"));
// do validation
try {
const session = await getSession();
const user = await db.user.findUnique({ where: { username } });
if (!user || password !== user.password) return new Error("Invalid login");
await session.update((d: UserSession) => (d.userId = user!.id));
} catch (err) {
return err as Error;
}
throw redirect("/");
}
## Clearing the session data

The `useSession` helper provides the `clear` method to clear the session data from the current request.

export async function logout() {
const session = await getSession();
await session.update((d: UserSession) => (d.userId = undefined));
throw redirect("/login");
```ts title="src/lib/session.ts"
export async function clearThemeSession() {
"use server";
const session = await useThemeSession();
await session.clear();
}
```
```
Loading