@daiso-tech/core
Version:
The library offers flexible, framework-agnostic solutions for modern web applications, built on adaptable components that integrate seamlessly with popular frameworks like Next Js.
141 lines • 4.62 kB
JavaScript
/**
* @module RateLimiter
*/
import {} from "mongodb";
import {} from "../../../../execution-context/contracts/_module.js";
import {} from "../../../../rate-limiter/contracts/_module.js";
import {} from "../../../../serde/contracts/_module.js";
import {} from "../../../../utilities/_module.js";
/**
* IMPORT_PATH: `"@daiso-tech/core/rate-limiter/mongodb-rate-limiter-storage-adapter"`
* @group Adapters
*/
export class MongodbRateLimiterStorageAdapter {
client;
collection;
serde;
enableTransactions;
/**
* @example
* ```ts
* import { MongodbRateLimiterStorageAdapter } from "@daiso-tech/core/rate-limiter/mongodb-rate-limiter-storage-adapter";
* import { MongoClient } from "mongodb";
* import { Serde } from "@daiso-tech/core/serde";
* import { SuperJsonSerdeAdapter } from "@daiso-tech/core/serde/super-json-serde-adapter"
*
* const client = await MongoClient.connect("YOUR_MONGODB_CONNECTION_STRING");
* const database = client.db("database");
* const serde = new Serde(new SuperJsonSerdeAdapter());
* const rateLimiterStorageAdapter = new MongodbRateLimiterStorageAdapter({
* client,
* database,
* serde
* });
* // You need initialize the adapter once before using it.
* await rateLimiterStorageAdapter.init()
* ```
*/
constructor(settings) {
const { client, collectionName = "rateLimiter", collectionSettings, database, serde, enableTransactions = true, } = settings;
this.client = client;
this.collection = database.collection(collectionName, collectionSettings);
this.serde = serde;
this.enableTransactions = enableTransactions;
}
/**
* Removes the collection where the rate limiter keys are stored and all it's related indexes.
* Note all rate limiter data will be removed.
*/
async deInit() {
// Should throw if the collection already does not exists thats why the try catch is used.
try {
await this.collection.dropIndexes();
}
catch {
/* EMPTY */
}
// Should throw if the collection already does not exists thats why the try catch is used.
try {
await this.collection.drop();
}
catch {
/* EMPTY */
}
}
/**
* Creates all related indexes.
* Note the `init` method needs to be called once before using the adapter.
*/
async init() {
// Should throw if the index already exists thats why the try catch is used.
try {
await this.collection.createIndex({
key: 1,
}, {
unique: true,
});
}
catch {
/* EMPTY */
}
// Should throw if the index already exists thats why the try catch is used.
try {
await this.collection.createIndex("expiration", {
expireAfterSeconds: 0,
});
}
catch {
/* EMPTY */
}
}
async upsert(_context, key, state, expiration, session) {
await this.collection.updateOne({
key,
}, {
$set: {
state: this.serde.serialize(state),
expiration,
},
}, { session, upsert: true });
}
async _transaction(trxFn) {
if (this.enableTransactions) {
return await this.client.withSession(async (session) => {
return await session.withTransaction(async () => {
return await trxFn(session);
});
});
}
return trxFn();
}
async transaction(_context, fn) {
return await this._transaction(async (session) => {
return await fn({
upsert: (context, key, state, exiration) => this.upsert(context, key, state, exiration, session),
find: (context, key) => this.find(context, key, session),
});
});
}
async find(_context, key, session) {
const doc = await this.collection.findOne({
key,
}, {
session,
});
if (doc === null) {
return null;
}
return {
state: this.serde.deserialize(doc.state),
expiration: doc.expiration,
};
}
async remove(_context, key, session) {
await this.collection.deleteOne({
key,
}, {
session,
});
}
}
//# sourceMappingURL=mongodb-rate-limiter-storage-adapter.js.map