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
- π 320.000+ downloads
- π¨βπ¬ 99.9% 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
,Array
,Object
, andBoolean
as cookie value types - βΏ IE support, thanks to @derwok
- π¦ Shipped with TypeScript types
- π¦ Looking for persistent Client (Browser) storage? Try the
ClientStorage
package.
ToC:
- Installation
- Import
- FAQ
- API
new Cookies()
constructor β Create a newCookies
instance.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 CookieCore()
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
1import { 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
allowQueryStringCookies
is enabled, cookies are transferred to the server via a query string (GET parameters) - For security, this is allowed only when the
Origin
header 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:cookies
can be used inmeteor-desktop
projects. 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()
orsend()
[!TIP] On the Server: cookies are implemented as middleware that attaches a
CookiesCore
instance to the incoming request (accessible asreq.Cookies
). Ensure that the Cookies middleware is registered before other middleware and routesIn
.meteor/packages
: Place theostrio: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 withhandler
callbacks andonCookies
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 consequenthandler
andonCookies
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 callNextFunc()
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 theCookies
instanceopts.onCookies
{function} β [Server] Callback triggered after.send()
or.sendAsync()
is called and the cookies are received by the server. (Note: available only ifauto
istrue
.)opts.TTL
{number | boolean} β Default expiration time (max-age) in milliseconds. Set tofalse
for session cookiesopts.runOnServer
{boolean} β Set tofalse
to 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.NAME
property of Cookies & CookiesCore instances, use it for instance identification, defaultCOOKIES
Example:
1import { Cookies } from 'meteor/ostrio:cookies'; 2 3const cookies = new Cookies({ 4 TTL: 31557600000 // One year TTL 5});
.get()
(Anywhere) Read a cookie. Returns undefined
if the cookie is not found
Arguments:
key
{string} β The name of the cookie.
1cookies.get('age'); // undefined if not found 2cookies.set('age', 25); // returns true 3cookies.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: current path)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}: SpecifiesPartitioned
attribute inSet-Cookie
header. 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 thePriority
attribute in `Set-Cookie`` headeropts.firstPartyOnly
{boolean}: Deprecated (usesameSite
instead)
1cookies.set('age', 25, { 2 path: '/', 3 secure: true 4});
.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 current path of the current document location (string or null). 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)
1const isRemoved = cookies.remove(key, path, domain); // boolean 2const isRemoved = cookies.remove('age', '/'); // boolean 3const isRemoved = cookies.remove(key, '/', 'example.com'); // boolean
.has()
(Anywhere) Check if a cookie exists
Arguments:
key
{string} β The name of the cookie
1const hasKey = cookies.has(key); // boolean 2const hasKey = cookies.has('age'); // boolean
.keys()
(Anywhere) Returns an array of all cookie names
1const cookieKeys = cookies.keys(); // string[] (e.g., ['locale', 'country', 'gender'])
.send()
(Client only) Synchronously send all current cookies to the server via XHR
Arguments:
callback
{function} β Callback with signature(error, response)
.
1cookies.send((error, response) => { 2 if (error) { 3 console.error(error); 4 } else { 5 console.log('Cookies synced:', response); 6 } 7});
.sendAsync()
(Client only) Asynchronously send all current cookies to the server via XHR
1const response = await cookies.sendAsync(); 2console.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
).
1import { WebApp } from 'meteor/webapp'; 2import { Cookies } from 'meteor/ostrio:cookies'; 3 4const cookies = new Cookies({ 5 auto: false, 6 handler(cookiesInstance) { 7 // Custom processing with cookiesInstance (of type Cookies) 8 } 9}); 10 11WebApp.connectHandlers.use(cookies.middleware());
.destroy()
(Server only) Unregisters hooks, callbacks, and middleware
1cookies.isDestroyed // false 2cookies.destroy(); // true 3cookies.isDestroyed // true 4cookies.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-Cookie
header, or{ [key: string]: unknown }
ObjectsetCookie
{boolean} - Set totrue
when_cookies
option derivative ofSet-Cookie
headerresponse
{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
β enablessend
andsendAsync
from 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.NAME
property of CookiesCore instances, use it for instance identification, defaultCOOKIES_CORE
[!NOTE]
CookiesCore
instance has the same methods asCookies
class except.destroy()
and.middleware()
1import { CookiesCore } from 'meteor/ostrio:cookies'; 2 3if (Meteor.isServer) { 4 // EXAMPLE SERVER USAGE 5 WebApp.connectHandlers.use((request, response, next) => { 6 const headerCookies = response.headers.get('set-cookie'); 7 const cookies = new CookiesCore({ 8 _cookies: headerCookies, 9 setCookie: true, // <- Switch cookie-parser to header mode 10 response: response, 11 }); 12 13 // FOR EXAMPLE: CHECK SESSION EXPIRATION 14 if (cookies.has('session-exp')) { 15 if (cookies.get('session-exp') < Date.now()) { 16 // .remove() WILL ADD `Set-Cookie` HEADER WITH expires=0 OPTION 17 cookies.remove('session-id'); 18 cookies.remove('session-exp'); 19 } 20 } else { 21 // MARK USER AS NEW 22 cookies.set('session-type', 'new-user'); 23 } 24 }); 25} 26 27if (Meteor.isClient) { 28 const cookies = new CookiesCore({ 29 // {runOnServer: true} Enables syncing cookies between client and server 30 // Requires `new Cookies({auto: true})` on server 31 runOnServer: true, 32 _cookies: { // <- Set default cookies 33 key: 'name', 34 theme: 'dark', 35 isNew: true, 36 'agreed-with-gdpr': false, 37 } 38 }); 39 40 // SET OR CHANGE COOKIES IN RUNTIME 41 cookies.set('ab-test', 42); 42 cookies.set('isNew', false); 43 cookies.set('agreed-with-gdpr', true); 44 45 // SYNC COOKIES 46 await cookies.sendAsync(); 47}
Examples
Use new Cookies()
on Client and Server separately or in the same file
Example: Client Usage
1import { Meteor } from 'meteor/meteor'; 2import { Cookies } from 'meteor/ostrio:cookies'; 3const cookies = new Cookies(); 4 5cookies.set('locale', 'en'); 6cookies.set('country', 'usa'); 7cookies.set('gender', 'male'); 8 9console.log(cookies.get('gender')); // "male" 10console.log(cookies.has('locale')); // true 11console.log(cookies.keys()); // ['locale', 'country', 'gender'] 12 13cookies.remove('locale'); 14console.log(cookies.get('locale')); // undefined
Example: Server Usage
1import { Meteor } from 'meteor/meteor'; 2import { Cookies } from 'meteor/ostrio:cookies'; 3import { WebApp } from 'meteor/webapp'; 4 5new Cookies(); 6WebApp.connectHandlers.use((req, res, next) => { 7 const cookiesInstance = req.Cookies; 8 9 cookiesInstance.set('locale', 'en'); 10 cookiesInstance.set('country', 'usa'); 11 cookiesInstance.set('gender', 'male'); 12 13 console.log(cookiesInstance.get('gender')); // "male" 14 next(); 15});
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
1import { Meteor } from 'meteor/meteor'; 2import { Cookies } from 'meteor/ostrio:cookies'; 3 4// register default middleware that will handle requests and req.Cookies extension 5const globalCookies = new Cookies(); 6 7// In checkout module/file 8WebApp.connectHandlers.use((req, res, next) => { 9 if (req.Cookies.has('checkout-session')) { 10 const sessionId = req.Cookies.get('checkout-session'); 11 // CHECK IF CHECKOUT SESSION IS VALID 12 if (isCheoutSessionValid(sessionId)) { 13 // FORCE-REDIRECT USER TO CHECKOUT IF SESSION IS VALID 14 res.statusCode = 302; 15 res.setHeader('Location', `https://example.com?chsessid=${sessionId}`); 16 res.end(); 17 return; 18 } 19 20 // REMOVE CHECKOUT COOKIE IF NOT VALID OR EXPIRED 21 req.Cookies.remove('checkout-session'); 22 } 23 24 next(); 25}); 26 27// In session module/file 28const sessionCookies = new Cookies({ 29 auto: false, 30 async handler(cookies) { 31 // FOR EXAMPLE: CHECK SESSION EXPIRATION 32 if (cookies.has('session-exp')) { 33 if (cookies.get('session-exp') < Date.now()) { 34 // .remove() WILL ADD `Set-Cookie` HEADER WITH expires=0 OPTION 35 cookies.remove('session-id'); 36 cookies.remove('session-exp'); 37 } 38 } else { 39 // MARK USER AS NEW 40 cookies.set('session-type', 'new-user'); 41 } 42 } 43}); 44// unregister handler when it isn't needed 45sessionCookies.destroy();
Example: Set and read cookies based on URL
Often cookies logic depends from URL it was called from. Access request details on handler
callback using cookies.response.req.url
{IncomingMessage} object:
1import { Cookies } from 'meteor/ostrio:cookies'; 2 3new Cookies({ 4 auto: false, 5 async handler(cookies) { 6 const url = new URL(cookies.response.req.url); 7 switch (url.pathname) { 8 case '/signup/create': 9 // GET USER'S SELECTED PLAN ON SIGNUP 10 const plan = url.searchParams.get('plan') || 'default-plan'; 11 cookies.set('selected-tariff', plan); 12 break; 13 case '/shopping-cart/new': 14 // CREATE NEW CHECKOUT SESSION ID 15 cookies.set('checkout-session', Random.id()); 16 break; 17 } 18 } 19});
Example: Alternative Usage
1import { Meteor } from 'meteor/meteor'; 2import { Cookies } from 'meteor/ostrio:cookies'; 3 4if (Meteor.isClient) { 5 const cookies = new Cookies(); 6 cookies.set('gender', 'male'); 7 console.log(cookies.get('gender')); // "male" 8 console.log(cookies.keys()); // ['gender'] 9} 10 11if (Meteor.isServer) { 12 const { WebApp } = require('meteor/webapp'); 13 const cookiesInstance = new Cookies({ 14 auto: false, // Disable auto-binding (optional) 15 handler(cookies) { 16 console.log(cookies.get('gender')); // "male" 17 } 18 }); 19 WebApp.connectHandlers.use(cookiesInstance.middleware()); 20}
Running Tests
- Clone the package repository.
- Open a terminal in the cloned directory.
- Run tests using:
Meteor/Tinytest
# Default meteor test-packages ./ # With a custom port meteor test-packages ./ --port 8888
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