Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/nestjsplus/cookies
Decorators for Managing Cookies with NestJS
https://github.com/nestjsplus/cookies
Last synced: about 2 months ago
JSON representation
Decorators for Managing Cookies with NestJS
- Host: GitHub
- URL: https://github.com/nestjsplus/cookies
- Owner: nestjsplus
- License: mit
- Created: 2019-08-03T22:10:46.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2023-01-23T23:49:33.000Z (almost 2 years ago)
- Last Synced: 2024-11-07T10:55:11.512Z (about 2 months ago)
- Language: TypeScript
- Homepage:
- Size: 1 MB
- Stars: 35
- Watchers: 2
- Forks: 12
- Open Issues: 21
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
Decorators for Managing Cookies with NestJS
### Version Compatibility - Nest v7
For the time being at least (until I can invest more time in this), Nest v7 will be supported in the `@nestjsplus/[email protected]` code line (sorry if that's not correct SEMVER; what I mean is version `1.1.x` is built on Nest 7). Nest 6 will be supported on the `1.0.x` version line.
The incompatibility is due to a change in Nest's `createParamDecorator()` function, and at the moment I haven't tried to figure out how to maintain compatibility across the two.
Please file an issue if you have difficulties with `v1.1.x` and Nest v7.
### Installation
```bash
npm install @nestjsplus/cookies
```### Motivation
NestJS doesn't currently have decorators for getting and setting cookies. While it's not
too hard to read cookies, it's convenient to have a parameter decorator to do so.```typescript
@Post('login')
login(@Cookies() cookies) {
console.log('Got cookies:', cookies);
}
```Setting cookies is a little less straightforward. You either need to utilize the platform-specific
response (`res`) object, or write an interceptor. The former is pretty straightforward, though
takes a non-Nest-like imperative style. It also puts you into
[manual response mode](https://docs.nestjs.com/controllers#routing),
meaning you can no longer rely on features like `@Render()`, `@HttpCode()` or [interceptors that modify the response](https://docs.nestjs.com/interceptors#response-mapping),
and makes testing harder (you'll have to mock the response
object, etc.). The `@SetCookies()` decorator from this package wraps an interceptor
in a declarative decorator that solves these issues.Collectively, the `@Cookies()`, `@SignedCookies()`, `@SetCookies()` and `@ClearCookies()` decorators in this package
provide a convenient set of features that make it easier to manage cookies in a standard and declarative way,
and minimize boilerplate code.### See Also
> **Note**: While still maintained, the `@nestsplus/redirect` package is essentially superceded by built-in support (I made the contribution) in `@nestjs/core`.
If you like these decorators, you may also be interested in the
[NestJS Redirect decorator](https://github.com/nestjsplus/redirect).### Importing the Decorators
Import the decorators, just as you would other Nest decorators, in the controllers
that use them as shown below:```typescript
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
import { Cookies, SignedCookies } from '@nestjsplus/cookies';@Controller()
export class AppController {
...
```### Reading Cookies
Reading cookies requires the [cookie-parser](https://github.com/expressjs/cookie-parser#readme)
package to be installed. [See here for details](#cookie-parser).
Reading **signed cookies** requires that the `CookieParser` be configured with a
[signing secret](https://github.com/expressjs/cookie-parser#cookieparsersecret-options).#### Regular (non-signed) Cookies
Use the `@Cookies()` route parameter decorator to get "regular" cookies.
```typescript
@Get('get')
get(@Cookies() cookies): string {
console.log('cookies: ', cookies);
return this.appService.getHello();
}
```This will bind an array of **all** (non-signed) cookies to the `cookies` parameter.
See [below](#accessing-specific-named-cookies) to access a named cookie.#### Signed Cookies
Use the `@SignedCookies()` route parameter decorator to get signed cookies.
```typescript
@Get('getSigned')
getSigned(@SignedCookies() cookies) {
console.log('signed cookies: ', cookies);
}
```As with `@Cookies()`, this will bind an array of **all** signed cookies to the `cookies`
parameter. Access individual signed cookies [as described below](#accessing-specific-named-cookies).#### Accessing Specific (Named) Cookies
Pass the name of a specific cookie in the `@Cookies()` or `@SignedCookies()` decorator
to access a specific cookie:```typescript
get(@SignedCookies('cookie1') cookie1) { ... }
```### Setting Cookies
Use the `@SetCookies()` route handler *method decorator* to set cookies.Here's the API:
```typescript
@SetCookies(
options?: CookieOptions,
cookies?: CookieSettings | CookieSettings[]
)
```Here's how it works. You have two options, depending on whether the cookie settings
are static or dynamic.
1. For *static* cookies, where the cookie name and/or value are known at compile time,
you can set them in the `@SetCookies()` decorator by passing a [CookieSettings](#cookie-settings)
object.
For example:
```typescript
@SetCookies({name: 'cookie1', value: 'cookie 1 value'})
@Get('set')
set() {
...
}
```2. For *dynamic* cookies, where the cookie name and/or value are computed at run-time,
you can provide the cookie name/value pairs to be set when the
route handler method runs. Provide these values by passing them on the `req._cookies`
array property. (The decorator creates the `_cookies` property automatically for you).
**Note:** Of course if you are using this technique, you are de facto accessing
the `request` object, so you must bind `@Request()` to a route parameter.
For example:```typescript
set(@Request() req) {
const cookie1Value = 'chocoloate chip';
req._cookies = [
{
name: 'cookie1',
value: cookie1Value,
options: {
signed: true,
sameSite: true,
},
},
{ name: 'cookie2', value: 'oatmeal raisin' },
];
...
```#### Defaults and overriding
You can mix and match `CookieOptions` and `CookieSettings` in the decorator and
in the method body as needed. This example
shows *dynamic* cookies with defaults inherited from the decorator, and
overrides in the body:
```typescript
@SetCookies({httpOnly: true},
[
{name: 'cookie1', value: 'cookie 1 value'},
{name: 'cookie2', value: 'cookie 2 value', {httpOnly: false}}
]
)
```
As a result of the above, `cookie1` will be set as `HttpOnly`, but `cookie2` will not.- Set default [cookie options](#cookieoptions) by passing a
`CookieOptions` object in the decorator. Options set on individual cookies,
if provided, override these defaults.#### Cookie Settings
As shown above, each cookie you set has the shape:
```typescript
interface CookieSettings {
/**
* name of the cookie.
*/
name: string;
/**
* value of the cookie.
*/
value?: string;
/**
* cookie options.
*/
options?: CookieOptions;
}
```
If `options` are provided for a cookie, they completely replace any options
specified in the `@SetCookies()` decorator. If omitted for a cookie, they default
to options specified on the `@SetCookies()` decorator, or [Express's default cookie settings](https://expressjs.com/en/api.html#res.cookie)
if none were set.#### CookieOptions
Cookie options may be set at the method level (`@SetCookies()`), providing a set of
defaults, or for individual cookies. In either case, they have the following shape:
```typescript
interface CookieOptions {
/**
* Domain name for the cookie.
*/
domain?: string;
/**
* A synchronous function used for cookie value encoding. Defaults to encodeURIComponent.
*/
encode?: (val: string) => string;
/**
* Expiry date of the cookie in GMT. If not specified or set to 0, creates a session cookie.
*/
expires?: Date;
/**
* Flags the cookie to be accessible only by the web server.
*/
httpOnly?: boolean;
/**
* Convenient option for setting the expiry time relative to the current time in milliseconds.
*/
maxAge?: number;
/**
* Path for the cookie. Defaults to “/”.
*/
path?: string;
/**
* Marks the cookie to be used with HTTPS only.
*/
secure?: boolean;
/**
* Indicates if the cookie should be signed.
*/
signed?: boolean;
/**
* Value of the “SameSite” Set-Cookie attribute. More information at
* https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00#section-4.1.1.
*/
sameSite?: boolean | string;
}
```
#### Route Handler Results and Behavior
The route handler otherwise proceeds as normal. It can return values, and it can
use other route handler method decorators (such as `@Render()`) and other route
parameter decorators (such as `@Headers()`, `@Query()`).#### Example
Setting cookies isn't hard! See a [full example here in the test folder](https://github.com/nestjsplus/cookies/blob/master/test/src/app.controller.ts).### Clearing (deleting) Cookies
Delete cookies in one of two ways:
1. Use `@SetCookies()` and pass in **only** the cookie name (leave the value property
off of the object).
2. Use `@ClearCookies()`, passing in a comma separated list of cookies to clear.
```typescript
@ClearCookies('cookie1', 'cookie2')
@Get('kill')
@Render('clear')
kill() {
return { message: 'cookies killed!' };
}
```### Restrictions
#### Express Only
These decorators currently only work with Nest applications running on `@platform-express`. Fastify support is not
currently available.#### Cookie Parser
Note that reading cookies depends on the standard Express [cookie-parser]() package. Be sure to install it
and configure it in your app. For example:```bash
npm install cookie-parser
```
and in your `main.ts` file:
```typescript
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { AppModule } from './app.module';import * as CookieParser from 'cookie-parser';
async function bootstrap() {
const app = await NestFactory.create(AppModule);app.use(CookieParser('secret'));
await app.listen(3000);
}
bootstrap();
```#### Decorators Can't Access `this`
Note that decorators have access to the `class` (Controller), but not the instance. This means that, for example,
if you want to pass a variable to a `SetCookies()` decorator, you should pass a variable set in the outer scope of
the file (e.g., a `const` above the controller class definition), as opposed to a property on the controller class.See [the controller in the test folder](https://github.com/nestjsplus/cookies/blob/master/test/src/app.controller.ts) for an example.
## Change Log
See [Changelog](CHANGELOG.md) for more information.
## Contributing
Contributions welcome! See [Contributing](CONTRIBUTING.md).
## Author
- **John Biundo (Y Prospect on [Discord](https://discord.gg/G7Qnnhy))**
## License
Licensed under the MIT License - see the [LICENSE](LICENSE) file for details.