Cookies for Meteor
April 24, 2026 Β· View on GitHub
Cookies for Meteor
Isomorphic and bulletproof πͺ cookie management for Meteor applications with support for Client, Server, Browser, Cordova, Meteor-Desktop, and other Meteor environments.
- π¨βπ» Stable codebase
- π 400,000+ downloads
- π¨βπ¬ 99.95% tests coverage / TDD
- π¦ No external dependencies (no
underscore,jQuery, orBlaze) - π₯ Consistent API across Server and Client environments
- π± Compatible with Cordova, Browser, Meteor-Desktop, and other client platforms
- γοΈ Full Unicode support for cookie values
- π¨βπ» Supports
String,Number,Array,Object,Boolean, andnullas cookie value types - βΏ IE support, thanks to @derwok
- π¦ Shipped with TypeScript types
- π¦ Looking for persistent Client (Browser) storage? Try the
ClientStoragepackage.
ToC:
- Installation
- Import
- FAQ
- API
new Cookies()constructor β Create a newCookiesinstance.get()β Read a cookie.set()β Set a cookie.remove()β Remove one or all cookies.keys()β List all cookie keys.send()β Sync cookies with the server.sendAsync()β Sync cookies asynchronously.middleware()β Register cookie middleware manuallynew CookiesCore()constructor β Low-level class that can be used to directly parse and manage cookies
- Examples
- Running Tests
- Support Our Open Source Contributions
Installation
meteor add ostrio:cookies
ES6 Import
import { Cookies } from 'meteor/ostrio:cookies';
FAQ
- Cordova Usage: This recommendation applies only to outgoing cookies from Client β Server. Cookies set by the server work out-of-the-box on the client:
- Enable withCredentials
- Set
{ allowQueryStringCookies: true }and{ allowedCordovaOrigins: true }on both Client and Server - When
allowQueryStringCookiesis enabled, cookies are transferred to the server via a query string (GET parameters) - For security, this is allowed only when the
Originheader matches the regular expression^http://localhost:12[0-9]{3}$(Meteor/Cordova connects throughlocalhost:12XXX)
- Cookies Missing on Server? In most cases, this is due to Meteor's HTTP callback-chain ordering. Ensure that
new Cookies()is called before routes are registered: - Meteor-Desktop Compatibility:
ostrio:cookiescan be used inmeteor-desktopprojects. Since Meteor-Desktop works similarly to Cordova, all Cordova recommendations from above apply
API
Note
On the server, cookies are set only after headers are sent (i.e. on the next route or page reload)
To sync cookies from Client to Server without a page reload, use sendAsync() or send()
Tip
On the Server: cookies are implemented as middleware that attaches a CookiesCore instance to the incoming request (accessible as req.Cookies). Ensure that the Cookies middleware is registered before other middleware and routes
In .meteor/packages: Place the ostrio:cookies package above all community packages, order of packages does matter in this file
See FAQ for more tips
Important
On the Server: it's possible to create many new Cookies() instances with handler callbacks and onCookies hooks, then later each instance can get destroyed calling .destroy() method.
Note: Only one middleware will be registered and passed into WebApp.connectHandlers.use() at the time! All subsequent handler and onCookies callbacks and hooks will be added to shared Map and called as expected within the first registered middleware. Invoking .middleware() method manually will result in warning and will return "blank" middleware handler which will instantly call NextFunc()
new Cookies() Constructor
Create a new instance of Cookies (available on both Client and Server).
Arguments:
opts{CookiesOptions} - Config object
Available CookiesOptions:
opts.auto{boolean} β [Server] Auto-bind asreq.Cookies(default:true)opts.handler{function} β [Server] Custom middleware handler; receives aCookiesCoreinstanceopts.onCookies{function} β [Server] Callback triggered after.send()or.sendAsync()is called and the cookies are received by the server. (Note: available only ifautoistrue.)opts.TTL{number | boolean} β Default expiration time (max-age) in milliseconds. Set tofalsefor session cookiesopts.runOnServer{boolean} β Set tofalseto disable server usage (default:true)opts.allowQueryStringCookies{boolean} β Allow passing cookies via query string (primarily for Cordova)opts.allowedCordovaOrigins{RegExp | boolean} β [Server] Allow setting cookies from specific origins (defaults to^http:\/\/localhost:12[0-9]{3}$iftrue)opts.name{string} - Sets.NAMEproperty of Cookies & CookiesCore instances, use it for instance identification, defaultCOOKIES
Example:
import { Cookies } from 'meteor/ostrio:cookies';
const cookies = new Cookies({
TTL: 31557600000 // One year TTL
});
.get()
(Anywhere) Read a cookie. Returns undefined if the cookie is not found
Arguments:
key{string} β The name of the cookie.
cookies.get('age'); // undefined if not found
cookies.set('age', 25); // returns true
cookies.get('age'); // returns 25
.set()
(Anywhere) Create or update a cookie
Arguments:
key{string} β The cookie namevalue{string | number | boolean | object | array} β The cookie valueopts{CookieOptions} β Optional settings
Supported CookieOptions:
opts.expires{number | Date | Infinity}: Cookie expirationopts.maxAge{number}: Maximum age in secondsopts.path{string}: Cookie path (default:/)opts.domain{string}: Cookie domainopts.secure{boolean}: Transmit only over HTTPSopts.httpOnly{boolean}: Inaccessible to client-side JavaScriptopts.sameSite{boolean | 'None' | 'Strict' | 'Lax'}: Cross-site cookie policyopts.partitioned{boolean}: SpecifiesPartitionedattribute inSet-Cookieheader. When enabled, clients will only send the cookie back when the current domain and top-level domain matchesopts.priority{'Low' | 'Medium' | 'High'}: Specifies the value for thePriorityattribute inSet-Cookieheaderopts.firstPartyOnly{boolean}: Deprecated (usesameSiteinstead)
cookies.set('age', 25, {
path: '/',
secure: true
});
.remove()
(Anywhere) Remove cookie(s)
remove()β Removes all cookies on the current domainremove(key)β Removes the specified cookieremove(key, path, domain)β Removes a cookie with the given key, path, and domain
Arguments:
key{string} - The name of the cookie to create/overwritepath{string} - [Optional] The path from where the cookie was readable. E.g., "/", "/mydir"; if not specified, defaults to/. The path must be absolute (see RFC 2965). For more information on how to use relative paths in this argument, read moredomain{string} - [Optional] The domain from where the cookie was readable. E.g., "example.com", ".example.com" (includes all subdomains) or "subdomain.example.com"; if not specified, defaults to the host portion of the current document location (string or null)
const isRemoved = cookies.remove(key, path, domain); // boolean
const isRemoved = cookies.remove('age', '/'); // boolean
const isRemoved = cookies.remove(key, '/', 'example.com'); // boolean
.has()
(Anywhere) Check if a cookie exists
Arguments:
key{string} β The name of the cookie
const hasKey = cookies.has(key); // boolean
const hasKey = cookies.has('age'); // boolean
.keys()
(Anywhere) Returns an array of all cookie names
const cookieKeys = cookies.keys(); // string[] (e.g., ['locale', 'country', 'gender'])
.send()
(Client only) Send all current cookies to the server via fetch and callback
Arguments:
callback{function} β Callback with signature(error, response).
cookies.send((error, response) => {
if (error) {
console.error(error);
} else {
console.log('Cookies synced:', response);
}
});
.sendAsync()
(Client only) Send all current cookies to the server via fetch and Promise
const response = await cookies.sendAsync();
console.log('Cookies synced:', response);
.middleware()
(Server only) Returns a middleware function to integrate cookies into your serverβs request pipeline.
Usage: Register this middleware with your Meteor server (e.g., via WebApp.connectHandlers.use).
import { WebApp } from 'meteor/webapp';
import { Cookies } from 'meteor/ostrio:cookies';
const cookies = new Cookies({
auto: false,
handler(cookiesInstance) {
// Custom processing with cookiesInstance (of type CookiesCore)
}
});
WebApp.connectHandlers.use(cookies.middleware());
.destroy()
(Server only) Unregisters hooks, callbacks, and middleware
cookies.isDestroyed // false
cookies.destroy(); // true
cookies.isDestroyed // true
cookies.destroy(); // false β returns `false` as instance was already destroyed
new CookiesCore() constructor
CookiesCore is low-level constructor that can be used to directly parse and manage cookies
Arguments:
opts{CookiesCoreOptions} β Optional settings
Supported CookiesCoreOptions:
_cookies{string | CookieDict} - Cookies string fromdocument.cookie,Set-Cookieheader, or{ [key: string]: unknown }ObjectsetCookie{boolean} - Set totruewhen_cookiesoption derives fromSet-Cookieheaderresponse{ServerResponse} - HTTP server response objectTTL{number | false} - Default cookies expiration time (max-age) in milliseconds. If false, the cookie lasts for the sessionrunOnServer{boolean} - Client only. Iftrueβ enablessendandsendAsyncfrom clientallowQueryStringCookies{boolean} - If true, allow passing cookies via query string (used primarily in Cordova)allowedCordovaOrigins{RegExp | boolean} - A regular expression or boolean to allow cookies from specific originsopts.name{string} - Sets.NAMEproperty of CookiesCore instances, use it for instance identification, defaultCOOKIES_CORE
Note
CookiesCore instance has the same methods as Cookies class except .destroy() and .middleware()
import { Meteor } from 'meteor/meteor';
import { WebApp } from 'meteor/webapp';
import { CookiesCore } from 'meteor/ostrio:cookies';
if (Meteor.isServer) {
// EXAMPLE SERVER USAGE
WebApp.connectHandlers.use((request, response, next) => {
const cookies = new CookiesCore({
_cookies: request.headers.cookie || '',
response,
});
// FOR EXAMPLE: CHECK SESSION EXPIRATION
if (cookies.has('session-exp')) {
if (cookies.get('session-exp') < Date.now()) {
// .remove() WILL ADD `Set-Cookie` HEADER WITH expires=0 OPTION
cookies.remove('session-id');
cookies.remove('session-exp');
}
} else {
// MARK USER AS NEW
cookies.set('session-type', 'new-user');
}
next();
});
}
if (Meteor.isClient) {
const cookies = new CookiesCore({
// {runOnServer: true} Enables syncing cookies between client and server
// Requires `new Cookies({auto: true})` on server
runOnServer: true,
_cookies: { // <- Set default cookies
key: 'name',
theme: 'dark',
isNew: true,
'agreed-with-gdpr': false,
}
});
// SET OR CHANGE COOKIES IN RUNTIME
cookies.set('ab-test', 42);
cookies.set('isNew', false);
cookies.set('agreed-with-gdpr', true);
// SYNC COOKIES
await cookies.sendAsync();
}
Examples
Use new Cookies() on Client and Server separately or in the same file
Example: Client Usage
import { Cookies } from 'meteor/ostrio:cookies';
const cookies = new Cookies();
cookies.set('locale', 'en');
cookies.set('country', 'usa');
cookies.set('gender', 'male');
console.log(cookies.get('gender')); // "male"
console.log(cookies.has('locale')); // true
console.log(cookies.keys()); // ['locale', 'country', 'gender']
cookies.remove('locale');
console.log(cookies.get('locale')); // undefined
Example: Server Usage
import { Cookies } from 'meteor/ostrio:cookies';
import { WebApp } from 'meteor/webapp';
new Cookies();
WebApp.connectHandlers.use((req, res, next) => {
const cookiesInstance = req.Cookies;
cookiesInstance.set('locale', 'en');
cookiesInstance.set('country', 'usa');
cookiesInstance.set('gender', 'male');
console.log(cookiesInstance.get('gender')); // "male"
next();
});
Example: Server with multiple cookie handlers
Sometimes it is required to build temporary or separate logic based on Client's cookies. And to split logic between different modules and files
import { Cookies } from 'meteor/ostrio:cookies';
import { WebApp } from 'meteor/webapp';
// register default middleware that will handle requests and req.Cookies extension
const globalCookies = new Cookies();
// In checkout module/file
WebApp.connectHandlers.use((req, res, next) => {
if (req.Cookies.has('checkout-session')) {
const sessionId = req.Cookies.get('checkout-session');
// CHECK IF CHECKOUT SESSION IS VALID
if (isCheckoutSessionValid(sessionId)) {
// FORCE-REDIRECT USER TO CHECKOUT IF SESSION IS VALID
res.statusCode = 302;
res.setHeader('Location', `https://example.com?chsessid=${sessionId}`);
res.end();
return;
}
// REMOVE CHECKOUT COOKIE IF NOT VALID OR EXPIRED
req.Cookies.remove('checkout-session');
}
next();
});
// In session module/file
const sessionCookies = new Cookies({
auto: false,
async handler(cookies) {
// FOR EXAMPLE: CHECK SESSION EXPIRATION
if (cookies.has('session-exp')) {
if (cookies.get('session-exp') < Date.now()) {
// .remove() WILL ADD `Set-Cookie` HEADER WITH expires=0 OPTION
cookies.remove('session-id');
cookies.remove('session-exp');
}
} else {
// MARK USER AS NEW
cookies.set('session-type', 'new-user');
}
}
});
// unregister handler when it isn't needed
sessionCookies.destroy();
Example: Set and read cookies based on URL
Often cookies logic depends on URL it was called from. Access request details on handler callback using cookies.response.req.url {IncomingMessage} object:
import { Meteor } from 'meteor/meteor';
import { Random } from 'meteor/random';
import { Cookies } from 'meteor/ostrio:cookies';
new Cookies({
auto: false,
async handler(cookies) {
const url = new URL(cookies.response.req.url, Meteor.absoluteUrl());
switch (url.pathname) {
case '/signup/create':
// GET USER'S SELECTED PLAN ON SIGNUP
const plan = url.searchParams.get('plan') || 'default-plan';
cookies.set('selected-tariff', plan);
break;
case '/shopping-cart/new':
// CREATE NEW CHECKOUT SESSION ID
cookies.set('checkout-session', Random.id());
break;
}
}
});
Example: Alternative Usage
import { Meteor } from 'meteor/meteor';
import { Cookies } from 'meteor/ostrio:cookies';
if (Meteor.isClient) {
const cookies = new Cookies();
cookies.set('gender', 'male');
console.log(cookies.get('gender')); // "male"
console.log(cookies.keys()); // ['gender']
}
if (Meteor.isServer) {
const { WebApp } = require('meteor/webapp');
const cookiesInstance = new Cookies({
auto: false, // Disable auto-binding (optional)
handler(cookies) {
console.log(cookies.get('gender')); // "male"
}
});
WebApp.connectHandlers.use(cookiesInstance.middleware());
}
Running Tests
- Clone the package repository.
- Open a terminal in the cloned directory.
- Run tests using:
Meteor/Tinytest
# Default, headless Tinytest runner
npm test
# Direct command
mtest --package ./ --port=8888 --once
# Type definitions
npm run test:types
# Browser fallback
meteor test-packages ./ --once --driver-package test-in-console
Support Our Open Source Contributions
- Upload and share files using βοΈ meteor-files.com β Continue interrupted file uploads without losing any progress. There is nothing that will stop Meteor from delivering your file to the desired destination
- Use β² ostr.io for Server Monitoring, Web Analytics, WebSec, Web-CRON and SEO Pre-rendering of a website
- Star on GitHub
- Star on Atmosphere
- Sponsor via GitHub
- Support via PayPal