alphamikle / nest_transact Goto Github PK
View Code? Open in Web Editor NEWSimplest transactions support for Nestjs with Typeorm
License: MIT License
Simplest transactions support for Nestjs with Typeorm
License: MIT License
The version 1.1.2 throws me the error:
Error: Cannot find module '/var/server/node_modules/nest-transact/index.js'. Please verify that the package.json has a valid "main" entry
at tryPackage (internal/modules/cjs/loader.js:295:19)
at Function.Module._findPath (internal/modules/cjs/loader.js:508:18)
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:802:27)
at Function.Module._load (internal/modules/cjs/loader.js:667:27)
at Module.require (internal/modules/cjs/loader.js:887:19)
at require (internal/modules/cjs/helpers.js:74:18)
at Object.<anonymous> (/var/server/dist/modules/benefits/benefits.service.js:34:25)
at Module._compile (internal/modules/cjs/loader.js:999:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
at Module.load (internal/modules/cjs/loader.js:863:32) {
code: 'MODULE_NOT_FOUND',
path: '/var/server/node_modules/nest-transact/package.json',
r
Reverting to 1.0.2 got rid of the problem, the problem seems to be that there isn't any index.js in that published version of the library
Hi, when I try to use it with custom repostiories, I get error saying cannot get undefined of whichever method I tried to call with custom repository
I decorated my custom repository with @entityrepository and imported with TypeOrmModule.forFeature in my module where I declared custom repository
Is there anything more I should do to make it work?
I made repository for reproducing errors here
I have a service that depends on a service that uses the HttpModule for external API communication. This caused a problem with this library.
The problem is that the repository detection is a bit naive. Currently it decides the dependency is a Repository if the it is a string, but other dependencies may be string at this point as well, like the HttpModule
.
private getArgument(param: string | ClassType, manager: EntityManager): any {
let argument: Repository<any>;
const id = typeof param === 'string' ? param : param.name;
if (this.cache.has(id)) {
return this.cache.get(id);
}
if (typeof param === 'string') {
const repository: Repository<any> = this.moduleRef.get(param, { strict: false });
const entity: any = repository.metadata.target; // crash! target is not a property of undefined
argument = manager.getRepository(entity);
} else {
argument = this.findArgumentsForProvider(param as ClassType, manager);
}
this.cache.set(id, argument);
return argument;
}
I was able to fix this by adding a instanceof Repository
check, to make sure that we have an actual TypeORM before we access it's metadata that might not be there.
private getArgument(param: string | ClassType, manager: EntityManager): any {
let argument: Repository<any>
const id = typeof param === 'string' ? param : param.name
if (this.cache.has(id)) {
return this.cache.get(id)
}
if (typeof param === 'string') {
// Fetch the depedency.
const depedency: Repository<any> = this.moduleRef.get(param, { strict: false })
if (depedency instanceof Repository) {
// If the dependency is a repository, make a new repository with the desired transaction manager.
const entity: any = depedency.metadata.target
argument = manager.getRepository(entity)
} else {
// The dependency is not a repository, use it directly.
argument = depedency
}
} else {
argument = this.findArgumentsForProvider(param as ClassType, manager)
}
this.cache.set(id, argument)
return argument
}
And now I believe it seems to working as intended.
Thanks for this code. It's an elegant solution to a tricky problem and this has been super helpful!
Hello, how do I install this package?
Thanks!
I cannot find a way to make custom repositories work inside a transaction.
Since v9.0.0 of @nestjs/typeorm and v0.3.0 of typeorm you can not create custom repositories with class style, like it showed below:
this notice says it's not possible I think - but I can't be sure if it's not be done elsewhere? Do you know of any solution?
Hi @alphamikle
Is there an example for performing unit tests using nest-transact?
thanks for your help
Thanks ๐
Hi :)
Thanks for this useful repo in order to improve the experience of transactions in NestJS application. Your solution is very nice in order to avoid transmitting entityManager
in each services' call and also in order to avoid using Transactional
Typeorm decorators which will be depreciated in the future.
However is your solution forwarding entityManager
through a chain of services ?
class Controller {
someAction() {
return getManager().transaction((entityManager) => {
return this.parentService.withTransaction(entityManager).someParentMethod();
}
}
class ParentService {
someParentMethod() {
return this.childService.someChildMethod();
// Do we have to use `withTransaction` here ?
// How to access `entityManager` ?
}
}
class ChildService {
someChildMethod() {
return this.childRepo.someRepoMethod();
}
}
Hi,
I just had a case where I injected a custom repository into a service. There also is another injected class which also uses this repository, like this:
//repository.ts
@EntityRepository(MyEntity)
export class MyRepository extends Repository<MyEntity> {
//...custom methods
}
//service.ts
export class MyService extends TransactionFor<MyService> {
constructor(
private myRepository: MyRepository,
private myOtherClass: MyOtherClass,
moduleRef: ModuleRef
) {
super(moduleRef);
}
//...methods using myRepository
}
//my-other-class.ts
@Injectable()
export class MyOtherClass {
constructor(
private myRepository: MyRepository
)
}
When nest-transact 1.1.3 creates new repositories, it actually sees that there is a custom repository, but does not generate a new repository with the custom methods:
//line 64
argument = manager.getRepository(entity);
If this line is changed to
argument = isCustomRepository ? manager.getCustomRepository(param as any) : manager.getRepository(entity);
then it seems to actually generate a new repository with the custom method.
Please see the code below, I've tried to debug this problem, but I was unsuccessful. Any help is much appreciated
UnknownElementException [Error]: Nest could not find UsersService element (this provider does not exist in the current context)
at InstanceLinksHost.get (/Users/repos/api/node_modules/@nestjs/core/injector/instance-links-host.js:15:19)
at Object.find (/Users/repos/api/node_modules/@nestjs/core/injector/module-ref.js:39:55)
at Object.get (/Users/repos/api/node_modules/@nestjs/core/injector/module.js:350:28)
at UsersService.getArgument (/Users/repos/api/node_modules/nest-transact/dist/lib/with-transaction.js:31:35)
at /Users/repos/api/node_modules/nest-transact/dist/lib/with-transaction.js:83:43
at Array.forEach (<anonymous>)
at UsersService.findArgumentsForProvider (/Users/repos/api/node_modules/nest-transact/dist/lib/with-transaction.js:79:14)
at UsersService.getArgument (/Users/repos/api/node_modules/nest-transact/dist/lib/with-transaction.js:71:29)
at /Users/repos/api/node_modules/nest-transact/dist/lib/with-transaction.js:83:43
at Array.forEach (<anonymous>)
@ApiTags('Users')
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService, private readonly dataSource: DataSource) {}
@Post('create-user')
@ApiOperation({
summary: 'Role: open. Creates: User, User.stripe, User.inventory for all itemTypes, User.address',
description: 'Creates user. For address please enter both types of address: bill_to and ship_to.',
})
@ApiBody({ type: CreateUserDto, required: true })
@ApiResponse({ type: Boolean, status: 201 })
async create(@Body() createUserDto: CreateUserDto): Promise<boolean> {
return await this.dataSource.transaction(manager => this.usersService.withTransaction(manager).create({ createUserDto }));
}
}
@Injectable()
export class UsersService extends TransactionFor<UsersService> {
private readonly logger = new Logger(UsersService.name);
constructor(
private readonly eventEmitter: EventEmitter2,
private readonly paymentService: PaymentService,
private readonly eventStore: EventStore,
private readonly inventoryService: InventoryService,
private readonly encryptionService: EncryptionService,
private readonly itemService: ItemService,
@InjectRepository(UserEntity)
private readonly userRepository: Repository<UserEntity>,
moduleRef: ModuleRef,
) {
super(moduleRef);
this.registerAdmin();
}
async create({ createUserDto }: { createUserDto: CreateUserDto }): Promise<boolean> {
let stripeId: string;
try {
const newUser = new UserEntity();
for (const key in createUserDto) {
newUser[key] = createUserDto[key];
}
newUser.password = await this.encryptionService.encrypt({ password: createUserDto.password });
newUser.stripe = await this.paymentService.createStripeAccount({ newUser });
stripeId = newUser.stripe.id;
newUser.magicToken = [];
newUser.magicToken.push(crypto.randomBytes(30).toString('hex'));
const inventories = await this.inventoryService.addInventoryToEntity({ user: newUser });
newUser.inventories = [];
for (const inventory of inventories) {
newUser.inventories.push(inventory);
}
await this.userRepository.save(newUser);
this.logger.log(`New User ${newUser.id} created.`);
this.eventEmitter.emit(Events.UserRegistered, new UserRegisteredEvent(newUser.email, newUser.magicToken[0]));
await this.eventStore.appendEvent(newUser, new CreateUserEvent({ id: newUser.id }, createUserDto));
return true;
} catch (err) {
this.logger.error(err);
await this.paymentService.deleteStripeAccount({ stripeId });
throw err;
}
}
}
When a service has a forwardRef in its constructor, the id is undefined. Attempting to create a transactional service with a forwardRef results in an error when the id is scanned if it is a repository:
TypeError: Cannot read property 'includes' of undefined
at SomeService.getArgument (/path/to/application/node_modules/nest-transact/lib/with-transaction.ts:47:32)
//... rest of the stack trace
Example for a service with forwardRefs:
@Injectable()
export class SomeService extends TransactionFor<SomeService> {
constructor(
@InjectRepository(SomeRepository)
private readonly someRepository: SomeRepository,
@Inject(forwardRef(() => SomeOtherClass))
private someOtherClass: SomeOtherClass,
moduleRef: ModuleRef,
) {
super(moduleRef);
}
}
The object in this case is a ForwardReference
(provided by @nestjs/common
), so one could get the forwardRef
function of that object, execute it, and get the id by its name, for example.
QUESTION: Can I use this lib the same way with GrapqhQL resolvers instead of REST controllers?
There is a config service that I have configured to use @nestjs/config module underneath. Whenever this config service is injected, I no longer can get the config variables from the service when one of the services is inheriting from the TransactionFor class.
Thanks for providing this library.
I have a project that I can not yet upgrade to v9.0.1
and I am blocked using the v8.0.0
dues to the "null dependency" issue fixed by #14.
Is it possible to back port the fix and publish it as v8.0.1
?
If you create a branch v8.0.x
branch (from the v8.0.0
tag) I could create a PR with the fix (from #14) for you to check and publish from the v8.0.x
branch).
FYI: The issue occurs when I use @Inject('SOME_STRING') someThing: SomeClass
. In this case the param
is a string that is not canBeRepository
so dependency
is never set.
After a service is wrapped with TransactionFor. @InjectDataSource can no longer resolve, giving error:
"message": "Cannot read properties of undefined (reading 'name')"
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.