Skip to content

add context to external store .get() and .set() options params - #201

Merged
dead-horse merged 7 commits into
koajs:masterfrom
Ngorror:context
Oct 8, 2020
Merged

add context to external store .get() and .set() options params#201
dead-horse merged 7 commits into
koajs:masterfrom
Ngorror:context

Conversation

@Ngorror

@Ngorror Ngorror commented Sep 15, 2020

Copy link
Copy Markdown
Contributor

Due to some weird reasons¹ I need access to the ctx.state in the external store get/set methods.
I'm not sure if it's a good idea to pollute the external store with the context.
From the official documentation:

You can store the session content in external stores (Redis, MongoDB or other DBs) by passing options.store with three methods (these need to be async functions):

  • get(key, maxAge, { rolling }): get session object by key
  • set(key, sess, maxAge, { rolling, changed }): set session object for key, with a maxAge (in ms)

my proposal is to send the context (or ctx.state only) to these methods as an option property:

  • get(key, maxAge, { ctx, rolling }): get session object by key
  • set(key, sess, maxAge, { ctx, rolling, changed }): set session object for key, with a maxAge (in ms)

If this is quite radical and unnecessary and can introduce memory leaks I can upate my pull request to send only the ctx.state:

  • get(key, maxAge, { state, rolling }): get session object by key
  • set(key, sess, maxAge, { state, rolling, changed }): set session object for key, with a maxAge (in ms)

¹The reason why I need the context state in my external store:
I have a "generic" RESTfull API koa site for a backend.
I run few instances of this backend and all of them are responsible to handle requests for few different domains at any given time. So each instance can process request from one domain/db then the next request is to another domain/db etc..

The code is exactly the same. I use ctx.request.hostname as a database name. I execute my "domain name to database name" middleware before koa session and then I need the db name in my external store provider.
My external store provider is a mongodb replicaset.

One way to achieve this is to have a central "session" db that is responsible for all sites sessions. In this case I don't need to mess with koa-session. The name of this special db can be hardcoded in my external store provider
But I prefer to keep each project sessions in the project db.

@Ngorror Ngorror changed the title add context to external store .get() and .set() params add context to external store .get() and .set() options params Sep 15, 2020
@coveralls

coveralls commented Sep 15, 2020

Copy link
Copy Markdown

Coverage Status

Coverage remained the same at 100.0% when pulling 3d2c89a on Ngorror:context into f765595 on koajs:master.

…added as options param to store.destroy method too
Comment thread lib/context.js Outdated
}

const json = await this.store.get(externalKey, opts.maxAge, { ctx, rolling: opts.rolling });
const json = await this.store.get(externalKey, opts.maxAge, { state: ctx.state, rolling: opts.rolling });

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can get state from ctx, can you just don't change store.get and add ctx to store.destroy?

@Ngorror Ngorror Sep 16, 2020

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, my first version sends ctx. Then I decided to minimize the pollution of the store methods by sending only the state. I can roll it back to use the ctx.

Each request to my node instance can be to a different db. That's why I need ctx.state.dbName in each store method.

Here is a simplified example of my store using ctx:

const TWO_WEEKS = 14 * 24 * 60 * 60 * 1000;

// Ignore the initialisation of MongoClient at this point 
const MongoClient = new mongodb.MongoClient(mongoURI, mongoOptions);

export get = async (key, maxAge, { rolling, ctx}) => {
    const db = MongoClient.db(ctx.state.dbName);
    const collection = await db.collection('sessions');
    return await collection.findOne({ _id: key });
};

const set = async (key, sess, maxAge, { changed, rolling, ctx}) => {
    const db = MongoClient.db(ctx.state.dbName);
    const collection = await db.collection('sessions');
    const ttl = new Date((typeof maxAge === 'number' ? maxAge : TWO_WEEKS) + Date.now());
    const document = { ...sess, _id: key, ttl };
    await collection.findOneAndUpdate({ _id: key }, { $set: document }, { upsert: true });
};

const destroy = async (key, { ctx}) => {
    const db = MongoClient.db(ctx.state.dbName);
    const collection = await db.collection('sessions');
    await collection.findOneAndDelete({ _id: key });
};

Comment thread .prettierignore Outdated

@dead-horse dead-horse left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@dead-horse
dead-horse merged commit 32e3526 into koajs:master Oct 8, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants