
Research
Security News
Malicious PyPI Package Exploits Deezer API for Coordinated Music Piracy
Socket researchers uncovered a malicious PyPI package exploiting Deezer’s API to enable coordinated music piracy through API abuse and C2 server control.
typeorm-transactional-cls-hooked
Advanced tools
A Transactional Method Decorator for typeorm that uses cls-hooked to handle and propagate transactions between different repositories and service methods. Inpired by Spring Trasnactional Annotation and Sequelize CLS
A Transactional
Method Decorator for typeorm that uses cls-hooked to handle and propagate transactions between different repositories and service methods.
Inspired by Spring Transactional Annotation and Sequelize CLS
See Changelog
yarn add typeorm-transactional-cls-hooked
## Needed dependencies
yarn add typeorm reflect-metadata
Or
npm install --save typeorm-transactional-cls-hooked
## Needed dependencies
npm install --save typeorm reflect-metadata
Note: You will need to import
reflect-metadata
somewhere in the global place of your app - https://github.com/typeorm/typeorm#installation
In order to use it, you will first need to initialize the cls-hooked namespace before your application is started
import { initializeTransactionalContext } from 'typeorm-transactional-cls-hooked';
initializeTransactionalContext() // Initialize cls-hooked
...
app = express()
...
Since this is an external library, all your typeorm repositories will need to be a custom repository extending the BaseRepository
class.
// Post.entity.ts
@Entity()
export class Post{
@PrimaryGeneratedColumn()
id: number
@Column
message: string
...
}
// Post.repository.ts
import { EntityRepository } from 'typeorm';
import { BaseRepository } from 'typeorm-transactional-cls-hooked';
@EntityRepository(Post)
export class PostRepository extends BaseRepository<Post> {}
The only purpose of the BaseRepository
class is to make sure the manager
property of the repository will always be the right one. In cases inheritance is not possible, you can always use the same code from BaseRepository
within your own repository code.
import { getEntityManagerOrTransactionManager } from 'typeorm-transactional-cls-hooked';
class MyRepository<Entity extends ObjectLiteral> extends Repository<Entity> {
private _connectionName: string = 'default'
private _manager: EntityManager | undefined
set manager(manager: EntityManager) {
this._manager = manager
this._connectionName = manager.connection.name
}
// Always get the entityManager from the cls namespace if active, otherwise, use the original or getManager(connectionName)
get manager(): EntityManager {
return getEntityManagerOrTransactionManager(this._connectionName, this._manager)
}
}
Sometimes there is a need to keep using the TypeORM Repository instead of using the BaseRepository
.
For this cases, you will need to "mixin/patch" the original Repository
with the BaseRepository
.
By doing so, you will be able to use the original Repository
and not change the code or use BaseRepository
.
This method was taken from https://gist.github.com/Diluka/87efbd9169cae96a012a43d1e5695667 (Thanks @Diluka)
In order to do that, the following should be done during initialization:
import { initializeTransactionalContext, patchTypeORMRepositoryWithBaseRepository } from 'typeorm-transactional-cls-hooked';
initializeTransactionalContext() // Initialize cls-hooked
patchTypeORMRepositoryWithBaseRepository() // patch Repository with BaseRepository.
@Transactional()
decoratorconnectionName
as argument (by default it is default
)propagation
as argument to define the propagation behaviourisolationLevel
as argument to define the isolation level (by default it will use your database driver's default isolation level.)export class PostService {
constructor(readonly repository: PostRepsitory)
@Transactional() // Will open a transaction if one doesn't already exist
async createPost(id, message): Promise<Post> {
const post = this.repository.create({ id, message })
return this.repository.save(post)
}
}
The following propagation options can be specified:
MANDATORY
- Support a current transaction, throw an exception if none exists.NESTED
- Execute within a nested transaction if a current transaction exists, behave like REQUIRED
else.NEVER
- Execute non-transactionally, throw an exception if a transaction exists.NOT_SUPPORTED
- Execute non-transactionally, suspend the current transaction if one exists.REQUIRED
(default behaviour) - Support a current transaction, create a new one if none exists.REQUIRES_NEW
- Create a new transaction, and suspend the current transaction if one exists.SUPPORTS
- Support a current transaction, execute non-transactionally if none exists.The following isolation level options can be specified:
READ_UNCOMMITTED
- A constant indicating that dirty reads, non-repeatable reads and phantom reads can occur.READ_COMMITTED
- A constant indicating that dirty reads are prevented; non-repeatable reads and phantom reads can occur.REPEATABLE_READ
- A constant indicating that dirty reads and non-repeatable reads are prevented; phantom reads can occur.SERIALIZABLE
= A constant indicating that dirty reads, non-repeatable reads and phantom reads are prevented.NOTE: If a transaction already exist and a method is decorated with @Transactional
and propagation
does not equal to REQUIRES_NEW
, then the declared isolationLevel
value will not be taken into account.
Because you hand over control of the transaction creation to this library, there is no way for you to know whether or not the current transaction was sucessfully persisted to the database.
To circumvent that, we expose three helper methods that allow you to hook into the transaction lifecycle and take appropriate action after a commit/rollback.
runOnTransactionCommit(cb)
takes a callback to be executed after the current transaction was sucessfully committedrunOnTransactionRollback(cb)
takes a callback to be executed after the current transaction rolls back. The callback gets the error that initiated the roolback as a parameter.runOnTransactionEnd(cb)
takes a callback to be executed at the end of the current transactional context. If there was an error, it gets passed as an argument.export class PostService {
constructor(readonly repository: PostRepsitory, readonly events EventService)
@Transactional()
async createPost(id, message): Promise<Post> {
const post = this.repository.create({ id, message })
const result = await this.repository.save(post)
runOnTransactionCommit(() => this.events.emit('post created'))
return result
}
}
0.1.7
FAQs
A Transactional Method Decorator for typeorm that uses cls-hooked to handle and propagate transactions between different repositories and service methods. Inpired by Spring Trasnactional Annotation and Sequelize CLS
The npm package typeorm-transactional-cls-hooked receives a total of 13,140 weekly downloads. As such, typeorm-transactional-cls-hooked popularity was classified as popular.
We found that typeorm-transactional-cls-hooked demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncovered a malicious PyPI package exploiting Deezer’s API to enable coordinated music piracy through API abuse and C2 server control.
Research
The Socket Research Team discovered a malicious npm package, '@ton-wallet/create', stealing cryptocurrency wallet keys from developers and users in the TON ecosystem.
Security News
Newly introduced telemetry in devenv 1.4 sparked a backlash over privacy concerns, leading to the removal of its AI-powered feature after strong community pushback.