Skip to content

Commit 93a0b2c

Browse files
amirhhashemikodiakhq[bot]LadyBluenotes
authored
Improve session docs (#1072)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: Sarah <gerrardsarah@gmail.com>
1 parent e9fb8de commit 93a0b2c

File tree

1 file changed

+101
-70
lines changed

1 file changed

+101
-70
lines changed

src/routes/solid-start/advanced/session.mdx

Lines changed: 101 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -2,98 +2,129 @@
22
title: "Sessions"
33
---
44

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

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

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

14-
```tsx title="/lib/session.ts"
13+
1. **Session creation**: When tracking is needed (e.g., upon login or first visit), the server creates a session.
14+
This involves generating a unique **session ID** and storing the session data, _encrypted and signed_, within a cookie.
15+
2. **Session cookie transmission**: The server sends a `Set-Cookie` HTTP header.
16+
This instructs the browser to store the session cookie.
17+
3. **Subsequent requests**: The browser automatically includes the session cookie in the `Cookie` HTTP header for requests to the server.
18+
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.
19+
5. **Session expiration and destruction**: Sessions typically expire after a period of time or upon user sign-out and the data is removed.
20+
This is done by setting a `Max-Age` attribute on the cookie or by sending a `Set-Cookie` HTTP header with an expired date.
21+
22+
Most of these steps are automatically managed by the [session helpers](#session-helpers).
23+
24+
### Database sessions
25+
26+
For larger applications or when more robust session management is required, SolidStart also supports storing session data in a database.
27+
This approach is similar to the cookie-based approach, but with some key differences:
28+
29+
- The session data is stored in the database, associated with the session ID.
30+
- Only the session ID is stored in the cookie, not the session data.
31+
- The session data is retrieved from the database using the session ID, instead of being retrieved directly from the cookie.
32+
- Upon expiration, in addition to the session cookie, the database record containing the session data is also removed.
33+
34+
SolidStart does not automatically handle interactions with a database; you need to implement this yourself.
35+
36+
## Session helpers
37+
38+
[Vinxi](https://vinxi.vercel.app), the underlying server toolkit powering SolidStart, provides helpers to simplify working with sessions.
39+
It provides a few key session helpers:
40+
41+
- [`useSession`](https://vinxi.vercel.app/api/server/session.html#usesession): Initializes a session or retrieves the existing session and returns a session object.
42+
- [`getSession`](https://vinxi.vercel.app/api/server/session.html#getsession): Retrieves the current session or initializes a new session.
43+
- [`updateSession`](https://vinxi.vercel.app/api/server/session.html#updatesession): Updates data within the current session.
44+
- [`clearSession`](https://vinxi.vercel.app/api/server/session.html#clearsession): Clears the current session.
45+
46+
These helpers work _only_ in server-side contexts, such as within server functions and API routes.
47+
This is because session management requires access to server-side resources as well as the ability to get and set HTTP headers.
48+
49+
For more information, see the [Cookies documentation in the Vinxi docs](https://vinxi.vercel.app/api/server/session.html).
50+
51+
## Creating a session
52+
53+
The `useSession` helper is the primary way to create and manage sessions.
54+
It provides a comprehensive interface for all session operations.
55+
56+
```ts title="src/lib/session.ts"
1557
import { useSession } from "vinxi/http";
16-
export async function getUser(request: Request) {
17-
const session = await useSession({
18-
password: process.env.SESSION_SECRET
19-
});
58+
59+
type SessionData = {
60+
theme: "light" | "dark";
61+
};
62+
63+
export async function useThemeSession() {
64+
"use server";
65+
const session = await useSession<SessionData>({
66+
password: process.env.SESSION_SECRET as string,
67+
name: "theme",
68+
});
69+
70+
if (!session.data.theme) {
71+
await session.update({
72+
theme: "light",
73+
});
74+
}
75+
76+
return session;
2077
}
2178
```
2279

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

26-
Typically, `userId` will be saved in the session data and if it is not found, it indicates that the request was not authenticated.
27-
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:
82+
`useSession` requires a strong password for encrypting and signing the session cookie.
83+
This password must be at least 32 characters long and should be kept highly secure.
84+
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.
2885

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

32-
export async function getUser(): Promise<User | null> {
33-
const session = await useSession({
34-
password: process.env.SESSION_SECRET
35-
});
36-
const userId = session.data.userId;
37-
if (!userId) return null;
38-
return await store.getUser(userId);
39-
}
88+
```sh frame="none"
89+
openssl rand -base64 32
4090
```
4191

42-
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).
92+
`useSession` adds a `Set-Cookie` HTTP header to the current server response.
93+
By default, the cookie is named `h3`, but can be customized with the `name` option, as shown in the example above.
4394

44-
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.
45-
That way if the user is not authenticated, the request will be redirected to the login page.
95+
## Getting the session data
4696

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

50-
const getUsers = query(async (id: string) => {
51-
"use server";
52-
const user = await getUser();
53-
if (!user) throw redirect("/login");
54-
return store.getUsers(id, "*");
55-
}, "users");
99+
```ts title="src/lib/session.ts"
100+
export async function getThemeSession() {
101+
"use server";
102+
const session = await useThemeSession();
56103

57-
// page component
58-
export default function Users() {
59-
const users = createAsync(() => getUsers());
104+
return session.data.theme;
60105
}
61106
```
62107

63-
This also allows logging in and out of the session in a similar manner:
108+
## Updating the session data
64109

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

69-
type UserSession = {
70-
userId?: number;
71-
};
72-
73-
function getSession() {
74-
return useSession({
75-
password: process.env.SESSION_SECRET
76-
});
112+
```ts title="src/lib/session.ts"
113+
export async function updateThemeSession(data: SessionData) {
114+
"use server";
115+
const session = await useThemeSession();
116+
await session.update(data);
77117
}
118+
```
78119

79-
export async function login(formData: FormData) {
80-
const username = String(formData.get("username"));
81-
const password = String(formData.get("password"));
82-
// do validation
83-
try {
84-
const session = await getSession();
85-
const user = await db.user.findUnique({ where: { username } });
86-
if (!user || password !== user.password) return new Error("Invalid login");
87-
await session.update((d: UserSession) => (d.userId = user!.id));
88-
} catch (err) {
89-
return err as Error;
90-
}
91-
throw redirect("/");
92-
}
120+
## Clearing the session data
121+
122+
The `useSession` helper provides the `clear` method to clear the session data from the current request.
93123

94-
export async function logout() {
95-
const session = await getSession();
96-
await session.update((d: UserSession) => (d.userId = undefined));
97-
throw redirect("/login");
124+
```ts title="src/lib/session.ts"
125+
export async function clearThemeSession() {
126+
"use server";
127+
const session = await useThemeSession();
128+
await session.clear();
98129
}
99-
```
130+
```

0 commit comments

Comments
 (0)