Cookies and Sessions
Finch provides two mechanisms for persisting data between HTTP requests: cookies (client-side) and sessions (server-side). Understanding the difference helps you choose the right tool:
- Cookies are stored in the browser and sent with every request. They are suitable for lightweight user preferences (theme, language) that can be safely stored client-side.
- Sessions are stored on the server. Only a session ID is sent to the browser. They are suitable for sensitive data such as the currently logged-in user.
Both are accessible through the rq object inside any controller or route handler.
Cookies
Cookies are key-value string pairs stored in the browser. Finch reads them from incoming Cookie headers and writes them via Set-Cookie response headers.
Read a Cookie
Use rq.getCookie(key, def: defaultValue) to read a cookie. The def parameter specifies what to return if the cookie does not exist.
safedefaults totrueon bothgetCookieandaddCookie. Unless you say otherwise, every cookie Finch reads or writes is transparently encrypted/decrypted. To store or read a genuinely plain-text value (readable in the browser's dev tools), you must passsafe: falseexplicitly:
// Encrypted (default) — decrypted automatically when read back with getCookie
var token = rq.getCookie('auth_token', def: '');
// Plain — value is stored and returned exactly as given
var theme = rq.getCookie('theme', def: 'light', safe: false);
Write a Cookie
Use rq.addCookie(key, value) to set a cookie. The cookie is attached to the response:
// Encrypted (default)
rq.addCookie('auth_token', 'abc123');
// Plain
rq.addCookie('theme', 'dark', safe: false);
addCookie also accepts finer-grained options for the underlying Set-Cookie header:
rq.addCookie(
'session_pref',
'compact',
safe: false,
duration: Duration(days: 30), // sets Max-Age
path: '/', // default is '/'
domain: '.example.com',
secure: true, // only send over HTTPS
httpOnly: true, // not readable from JavaScript
sameSite: SameSite.lax,
);
Remove a Cookie
To remove a cookie, Finch re-sets it with an expired max-age:
rq.removeCookie('theme');
Complete Example
class CookieController extends Controller {
Future<String> showCookie() async {
var value = rq.getCookie('test', def: 'not set', safe: false);
rq.addParam('cookieValue', value);
return rq.renderView(path: 'example/cookie');
}
Future<String> addCookie() async {
var key = rq.get<String>('key', def: '');
var value = rq.get<String>('value', def: '');
var safe = rq.get<bool>('safe', def: false);
if (key.isNotEmpty) {
rq.addCookie(key, value, safe: safe);
}
return rq.redirect('/example/cookie');
}
}
Sessions
Sessions are managed by Dart's built-in HttpSession. Each visitor receives a unique session ID stored in a browser cookie, but the actual data lives on the server in memory.
Important: The default Dart
HttpSessionis memory-backed. Session data is lost when the server restarts, and is not shared across multiple server instances behind a load balancer. For production use at scale, consider a persistent/shared session store or store critical data in the database instead.
Read a Session Value
Use rq.getSession(key, def: defaultValue) to read a value. It returns def if the key does not exist. Note the return type is Object, not String — cast it if you need a specific type:
var userEmail = rq.getSession('user', def: '') as String;
Write a Session Value
Use rq.addSession(key, value) to store a value in the current session. The value can be any JSON-friendly type (String, num, bool, Map, List), not just strings:
rq.addSession('user', '[email protected]');
rq.addSession('cart', {'items': 3, 'total': 42.5});
Remove a Session Value
rq.session.remove('user');
Complete Example
A typical login/logout/dashboard flow using sessions:
class SessionController extends Controller {
Future<String> login() async {
var email = rq.get<String>('email', def: '');
var password = rq.get<String>('password', def: '');
if (isValidUser(email, password)) {
// Store the user's email in the session after successful authentication
rq.addSession('user', email);
return rq.redirect('/dashboard');
}
return rq.renderError(401);
}
Future<String> logout() async {
// Remove the user from the session to log them out
rq.session.remove('user');
return rq.redirect('/login');
}
Future<String> dashboard() async {
var email = rq.getSession('user', def: '') as String;
// If no session, the user is not logged in — redirect to login
if (email.isEmpty) return rq.redirect('/login');
rq.addParam('email', email);
return rq.renderView(path: 'pages/dashboard');
}
}
For a full authentication flow with permissions and route guarding, see Auth Controller — it builds on the same getSession/addSession calls shown here.
How Cookie Encryption Works
The encryption key is FinchConfigs.cookiePassword:
FinchConfigs configs = FinchConfigs(
cookiePassword: env.get('COOKIE_SECRET', 'change-this-in-production'),
);
// Writing — stored encrypted in the browser
rq.addCookie('prefs', '{"theme":"dark"}');
// Reading back — Finch decrypts it automatically
var prefs = rq.getCookie('prefs', def: '{}');
Because encryption is keyed to cookiePassword, changing that value invalidates every cookie already stored in visitors' browsers — getCookie will fail to decrypt them and silently fall back to def.
Security: Use a long, random
cookiePassword. Store it as an environment variable. Never hardcode it or commit it to version control.
Reading Cookies in Templates
You can also read a cookie value directly inside a Jinja template:
{{ $e.getCookie('theme', 'light') }}
See Template for everything else available on $e inside a template.