Session & cookies

Ts.ED provide two decorators to get @@Session@@ and @@Cookies@@ values in your controller.

Installation

Before using the Session and Cookies, we need to install the a module like express-session but you can use another module which follow the same convention.

npm install --save express-session
1

::: warn The default server-side session storage, MemoryStore, is purposely not designed for a production environment. It will leak memory under most conditions, does not scale past a single process, and is meant for debugging and developing.

For a list of stores, see compatible session stores. :::

Configuration

Edit your Server and add these lines:

import {ServerLoader, ServerSettings} from "@tsed/common";

const cookieParser = require("cookie-parser");
const bodyParser = require("body-parser");
const compress = require("compression");
const methodOverride = require("method-override");
const session = require("express-session");

@ServerSettings({})
class Server extends ServerLoader {

  /**
   * This method let you configure the express middleware required by your application to works.
   * @returns {Server}
   */
  public $onMountingMiddlewares(): void | Promise<any> {

    this
      .use(cookieParser())
      .use(compress({}))
      .use(methodOverride())
      .use(bodyParser.json())
      .use(bodyParser.urlencoded({
        extended: true
      }));

    this.set("trust proxy", 1); // trust first proxy
    this.use(session({
      secret: "keyboard cat",
      resave: false,
      saveUninitialized: true,
      cookie: {secure: true}
    }));
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

Usage

Session

import {BodyParams, Controller, Get, Post, Session, Status} from "@tsed/common";

@Controller("/")
export class MyCtrl {

  @Get("/whoami")
  whoAmI(@Session() session: any) {
    console.log("User in session =>", session.user);

    return session.user && session.user.id ? `Hello user ${session.user.name}` : "Hello world";
  }

  @Post("/login")
  @Status(204)
  login(@BodyParams("name") name: string, @Session("user") user: any) {
    user.id = "1";
    user.name = name;
  }

  @Post("/logout")
  @Status(204)
  logout(@Session("user") user: any) {
    user.id = null;
    delete user.name;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

Cookies

import {Controller, Cookies, Post} from "@tsed/common";
import {IUser} from "./interfaces/IUser";

@Controller("/")
class MyCtrl {
  @Post("/")
  getCookies(@Cookies() cookies: any) {
    console.log("Entire cookies", cookies);
  }

  @Post("/")
  getIdInCookies(@Cookies("id") id: string) {
    console.log("ID", id);
  }

  @Post("/")
  getObjectInCookies(@Cookies("user") user: IUser) {
    console.log("user", user);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Initialize session

Sometime we want to be sure that the session is correctly initialized with the right values.

Let's start, by creating a middleware CreateRequestSessionMiddleware in middlewares directory:

import {Middleware, Req} from "@tsed/common";

@Middleware()
export class CreateRequestSessionMiddleware {
  use(@Req() request: Req) {
    if (request.session) {
      request.session.user = request.session.user || {
        id: null
      };
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

Then, add this middleware on the server:

import {ServerLoader, ServerSettings} from "@tsed/common";
import {CreateRequestSessionMiddleware} from "./middlewares/CreateRequestSessionMiddleware";

const cookieParser = require("cookie-parser");
const bodyParser = require("body-parser");
const compress = require("compression");
const methodOverride = require("method-override");
const session = require("express-session");

@ServerSettings({})
class Server extends ServerLoader {

  /**
   * This method let you configure the express middleware required by your application to works.
   * @returns {Server}
   */
  public $onMountingMiddlewares(): void | Promise<any> {

    this
      .use(cookieParser())
      .use(compress({}))
      .use(methodOverride())
      .use(bodyParser.json())
      .use(bodyParser.urlencoded({
        extended: true
      }));

    this.set("trust proxy", 1); // trust first proxy
    this.use(session({
      secret: "keyboard cat", // change secret key
      resave: false,
      saveUninitialized: true,
      cookie: {
        secure: false // set true if HTTPS is enabled
      }
    }));

    this.use(CreateRequestSessionMiddleware);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

Finally, you can read and write values in your controller:

import {BodyParams, Controller, Get, Post, Session, Status} from "@tsed/common";

@Controller("/")
export class MyCtrl {

  @Get("/whoami")
  whoAmI(@Session() session: any) {
    console.log("User in session =>", session.user);

    return session.user && session.user.id ? `Hello user ${session.user.name}` : "Hello world";
  }

  @Post("/login")
  @Status(204)
  login(@BodyParams("name") name: string, @Session("user") user: any) {
    user.id = "1";
    user.name = name;
  }

  @Post("/logout")
  @Status(204)
  logout(@Session("user") user: any) {
    user.id = null;
    delete user.name;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

In addiction, you can add integration test with SuperTest and @tsed/testing package. Here an example of Rest API test:

import {ExpressApplication} from "@tsed/common";
import {TestContext} from "@tsed/testing";
import {expect} from "chai";
import * as SuperTest from "supertest";
import {Server} from "../../../src/Server";

describe("Session", () => {
  let agent: any;

  before(TestContext.bootstrap(Server));
  before(TestContext.inject([ExpressApplication], async (expressApplication: ExpressApplication) => {
    agent = SuperTest.agent(expressApplication);
  }));
  after(TestContext.reset);

  describe("Login / Logout", () => {
    it("should create session return hello world and connect a fake user", async () => {
      // WHEN
      const response1 = await agent.get("/rest/whoami").expect(200);

      await agent.post("/rest/login").send({name: "UserName"}).expect(204);

      const response2 = await agent.get("/rest/whoami").expect(200);

      await agent.post("/rest/logout").expect(204);

      const response3 = await agent.get("/rest/whoami").expect(200);

      // THEN
      expect(response1.text).to.eq("Hello world");
      expect(response2.text).to.eq("Hello user UserName");
      expect(response3.text).to.eq("Hello world");
    });
  });
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

::: tip You can find a working example on Express Session here. :::