Simplify document cookies management
Tags: webdev, javascript, learning, tutorial
Published: 2024-02-25 03:13:50

Cookies are tiny text files that websites store on your computer or mobile device when you visit them. These files contain information about your activity on the website, such as your login details, shopping cart contents, and browsing history. Websites use cookies to customize your experience, remember your preferences, and track your behavior.

A cookie looks like a key-value pair separated by an equals sign (=). You can store multiple keys and values in a single cookie by separating them with semicolons (;). Cookies also have attributes that determine when they expire, who can access them, and whether they should only be sent over secure connections.

In this post, we'll explore how to use JavaScript Proxy to manage cookies. But before we dive into that, let's go over some common tasks you can do with cookies.

## Getting a cookie item

To retrieve a specific cookie item, you can use the `document.cookie` property. This property returns all cookies associated with the current document as a string separated by semicolons. To extract a specific value, you can use string manipulation methods such as `split()` and `indexOf()`.

For instance, let's take a look at the following example cookie:

```shell
user=John; email=john@example.com; expires=Thu, 01 Jan 2022 00:00:00 UTC; path=/; domain=example.com
```

To retrieve the value associated with the `email` key, simply use the following line of code:

```js
const cookies = document.cookie.split('; ');
const emailCookie = cookies.find(cookie => cookie.startsWith('email='));
const emailValue = emailCookie.split('=')[1];
console.log(emailValue);    // "john@example.com"
```

This code does a couple of things. First, it takes the `document.cookie` string and turns it into an array of separate cookies using the semicolon and space separator. Then, it finds the cookie that starts with the key `"email"`. Once it finds that cookie, it separates out the value using another `split` operation and logs it to the console for you to see.

## Setting a cookie item

To create or update a cookie, simply assign a string in the `key=value` format to the `document.cookie` property. You can also include additional attributes by appending semicolon-separated key-value pairs.

For instance, if you want to set a cookie with the key `"username"` and value `"johndoe"`, you can use the following code:

```js
document.cookie = "username=johndoe; expires=Thu, 01 Jan 2022 00:00:00 UTC; path=/";
```

This code creates a cookie named `"username"` and gives it the value `"johndoe"`. The cookie will expire on January 1st, 2022, and it will be accessible on any page with the path `"/"`.

## Deleting a cookie item

If you want to delete a cookie, simply set its expiration date to a date in the past. This will cause the cookie to expire immediately and be deleted by the browser.

For instance, if you want to delete the cookie with the key `"username"`, you can use the following code:

```js
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/";
```

This code deletes a cookie named `"username"` from the user's browser. It does this by setting the value of the cookie to an empty string, setting its expiration date to January 1st, 1970 (which means it has already expired), and setting its path to `"/"`.

It's important to note that if the cookie you want to delete has additional attributes, like `domain` or `secure`, you'll need to include those attributes in your deletion code as well.

## Updating an existing cookie item

When you need to update an existing cookie, you can set the `document.cookie` property with the same name and path as before, but with a new value and expiration date if necessary. But let's face it, this approach can be repetitive and error-prone. The good news is that you can simplify the process by defining a reusable function that takes in the key, value, and expiration date as arguments and updates the corresponding cookie for you.

Here's an example implementation of such a function:

```js
const updateCookie = (key, value, expires) => {
    const cookies = document.cookie.split('; ');
    const targetCookie = cookies.find(cookie => cookie.startsWith(`${name}=`));
    if (targetCookie) {
        const [key, value] = targetCookie.split('=');
        const attributes = targetCookie.slice(key.length + 1).split(';');
        const newAttributes = attributes.filter(attr => !attr.trim().startsWith('expires='));
        newAttributes.push(`expires=${expires.toUTCString()}`);
        document.cookie = `${key}=${value}; ${newAttributes.join('; ')}`;
    }
}
```

This function creates a new string in the `key=value` format using the given parameters and sets it as the value of `document.cookie`. It's important to note that to update an existing cookie, you need to specify the same path as before. By calling this function, you can easily update a cookie without repeating the same code over and over again.

For instance, let's say you have a cookie named `"theme"` that stores the user's preferred color scheme for your website. If the user decides to switch from a `"light"` to a `"dark"` theme, you can update the corresponding cookie by calling this function.

```js
// Expires in one day
updateCookie('theme', 'dark', new Date(Date.now() + 1000 * 60 * 60 * 24));
```

This code sets the expiration of a cookie called `"username"` to one day from the current time.

## Simplify cookie management with a custom wrapper

Managing cookies can be a hassle. To make it easier to interact with cookies, we'll create a custom cookie object that offers simple functions for managing cookies.

```js
const cookies = {
  // The wrapper ...
};

// Get a cookie item
cookies.theme;

// Set a cookie
cookies.theme = 'dark';

// Remove a cookie item
delete cookies.theme;
```

We'll be using JavaScript to create a wrapper for the document cookie using a proxy. A proxy lets you intercept and customize operations performed on an object, in this case, the document cookie. Here are the steps to use a proxy for managing cookies:

-   **Create a Proxy object.** Start by creating a Proxy object for the document cookie. This allows you to intercept and customize cookie operations.
-   **Define a handler object.** Next, define a handler object with methods for getting, setting, and deleting cookies.
-   **Set the Proxy.** Finally, set the Proxy to use the handler object. Now you can use the document cookie as usual, but with the added benefit of being able to customize cookie operations.

Here's a simple implementation of the Proxy traps handler:

```js
const cookieHandler = {
    get: function(obj, prop) {
        ...
    },
    set: function(obj, prop, value) {
        ...
    },
    deleteProperty: function(obj, prop) {
        ...
    },
};
```

This code creates a `cookieHandler` object that has three handy methods for working with cookies: getting, setting, and deleting. The `get()` method does a quick search through the `document.cookie` string to find the cookie with the name you specify, and then returns the decoded value of that cookie.

```js
const cookieHandler = {
    get: function(obj, prop) {
        const cookies = document.cookie.split('; ');
        const cookie = cookies.find(cookie => cookie.startsWith(`${prop}=`));
        return cookie ? decodeURIComponent(cookie.split('=')[1]) : undefined;
    },
};
```

The `set()` method allows you to create a fresh cookie with a specific name and encoded value.

```js
const cookieHandler = {
    set: function(obj, prop, value) {
        document.cookie = `${prop}=${encodeURIComponent(value)}`;
        return true;
    },
};
```

The `deleteProperty()` method is used to delete a cookie with a specific name. This is done by setting the cookie's expiration date to January 1st, 1970.

```js
const cookieHandler = {
    deleteProperty: function(obj, prop) {
        document.cookie = `${prop}=; expires=Thu, 01 Jan 1970 00:00:00 UTC`;
        return true;
    },
};
```

This line of code creates a new Proxy object that uses `cookieHandler` to handle an empty object.

```js
const cookies = new Proxy({}, cookieHandler);
```

You can now treat cookies like they're properties of an object, and interact with them accordingly.

```js
// Get the value of a cookie item
cookies.theme;  // "dark"

// Set the value
cookies.theme = "light";

// Delete a cookie item
delete cookies.theme;
```

This implementation utilizes the power of JavaScript Proxy to simplify and abstract away some of the complexities involved in managing cookies.

## Adding expiration time support

To enhance our implementation and add support for expiration time for each cookie, we can modify the `set()` method of the `cookieHandler` object. To do this, we'll add an optional third argument for the expiration time. If this argument is provided, the method will convert it to a UTC string and append it to the cookie attributes before setting it.

Here's the updated implementation of the `cookieHandler` object with support for expiration times.

```js
const cookieHandler = {
    // ...
    set: function(obj, prop, value) {
        const v = typeof value === "object" ? value.value : value;
        let attributes = [];
        if (typeof value === "object") {
            attributes.push(`expires=${value.expires.toUTCString()}`);
        }
        document.cookie = `${prop}=${encodeURIComponent(v)}; ${attributes.join('; ')}`;
        return true;
    },
};
```

The updated version of the `set()` trap handler now accepts an optional third argument for the expiration time. If this argument is provided and it is an object, the method extracts the `value` property from the object and pushes an additional attribute to the cookie string that specifies its expiration date in UTC format.

To set a cookie with an expiration time, you can pass an object as the value parameter with two properties: `value` (the actual value of the cookie) and `expires` (a Date object representing when the cookie should expire). Here's how you would set a cookie named `theme` that expires in 30 days:

```js
const expirationDate = new Date(Date.now() + 1000 * 60 * 60 * 24 * 30);
cookies.theme = {
    value: "dark",
    expires: expirationDate,
};
```

This code creates a new cookie called `theme` with a value of `dark`. It also sets an expiration date of 30 days from now by passing in an object with both `value` and `expires` properties as the second parameter.

To create a cookie that is deleted when the user closes their browser, we simply set it as a string, just like before.

```js
cookies.username = 'johndoe';
```

## Conclusion

JavaScript Proxy is a powerful tool that simplifies the process of handling cookies in web development. With the `cookieHandler` object, you can easily get, set, and delete cookies without worrying about all the technical details involved in working with `document.cookie`.

By treating cookies as object properties, your code becomes more readable and easier to manage. You can even set expiration times for your cookies, making it possible to create cookies that expire at a specific time or never expire at all.

These tools are essential for building robust web applications that store user preferences and other data using browser cookies. Whether you're building a simple blog or a complex e-commerce site, managing cookies is a crucial part of web development. Using JavaScript Proxy to handle cookies simplifies the process and allows you to focus on delivering great user experiences.

---

If you found this series helpful, please consider giving the [repository](https://github.com/phuocng/javascript-proxy) a star on GitHub or sharing the post on your favorite social networks 😍. Your support would mean a lot to me!

If you want more helpful content like this, feel free to follow me:

-   [DEV](https://dev.to/phuocng)
-   [GitHub](https://github.com/phuocng)
-   [Website](https://phuoc.ng)