Reference · Cookies and sessions

set_session

signature
set_session(map | null, map | null=) -> null

What it does

Replaces the visitor's session with the map and sends it to the browser as a cookie signed with HMAC-SHA256: the browser can read it but not change it. It lasts 30 days unless the option max_age says otherwise, in seconds or a duration ("12h"). set_session(null) ends the session, as end_session does. It gives null. See Headers, cookies and sessions.

Examples

A login: the page with the form, the route it posts to, a page that reads the session, and a logout button. The users rows were made at signup, with crypto.hash_password:

in a program
route GET /login =    <form method="post" action="/login">        <input type="email" name="email" required/>        <input type="password" name="password" required/>        <button>Log in</button>    </form>route POST /login { form: { email: str, password: str } } {    let user = db.one("select * from users where email = ?", email)    let hash = user.password_hash ?? crypto.hash_password("not anyone's")   // same work, user or not    if user == null || !crypto.verify_password(password, hash) { reply 401 "Wrong email or password" }    set_session({ user: user.id })    redirect("/")}route GET / {    let id = session().user ?? return redirect("/login")    let me = db.one("select name from users where id = ?", id)    <main>        <p>Hello, {me.name}</p>        <form method="post" action="/logout"><button>Log out</button></form>    </main>}route POST /logout { set_session(null); redirect("/login") }

From a component, a server fn sets it, and the answer to the call carries the cookie:

in a program
server fn sign_in(email: str, password: str) {    let user = check_password(email, password) ?? return err("Wrong email or password")    set_session({ user: user.id }, { max_age: "12h" })    { name: user.name }}component SignIn() {    state email = ""    state password = ""    state problem = null    <form @submit={() => {        let r = sign_in(email, password)        if is_err(r) { problem = r.error } else { navigate("/") }    }}>        <input type="email" value={email}/>        <input type="password" value={password}/>        <button>Sign in</button>        {problem && <p class="error">{problem}</p>}    </form>}
print(set_session({ user: 7 }))print(session())
output
null{ user: 7 }

Edge cases

set_session({ user: 7, name: "Ann" })set_session({ user: 7 })print(session())set_session({ ...session(), at: 1700000000000 }, { max_age: "30d" })print(session())set_session(null)print(session())
output
{ user: 7 }{ user: 7, at: 1700000000000 }{}

Errors

A first argument that is not a map or null, a max_age that is not seconds or a duration or is 0 or less, and options that are not a map are runtime errors, not error values. So is a session over about 3,900 bytes once signed, more than a cookie holds: keep an id in it and the rest in the database; and one nested deeper than 128 levels, as deep as its JSON is read back.

this one fails
set_session("user 7")
output
line 1: `set_session` expects a map, got str
this one fails
set_session({ user: 7 }, { max_age: 0 })
output
line 1: `set_session`'s max_age is seconds or a duration like "30d", got 0

Chaining

A sliding session is set_session called again on the requests that count as use, once a day, from a helper every such route calls:

in a program
fn touch() {    let s = session()    let day = 24 * 60 * 60 * 1000                      // now() is milliseconds    if s.user != null && now() - (s.at ?? 0) > day {        set_session({ ...s, at: now() }, { max_age: "30d" })    }}route GET /dashboard { touch(); <Dashboard me={me_or_login("/dashboard")}/> }

See also

session, end_session, set_cookie, crypto.verify_password, crypto.token

Thread
8Useful? Like it, or add to its thread below.

Thread 0 posts

On set_session(): questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.

    Edit this page on GitHub · every example's output was made by running it (tools/refcheck.mjs) · questions on Discord or r/quillo