I have a problem when trying to save an entity to the DB with a primary column with a default value using TypeORM (in NestJs).
TL;DR How on earth do you set default column values with TypeORM when default just doesn't seem to do anything?
EDIT 1: Okay, so default seems to be DB level, kind of missed that before, but still @BeforeInsert() should do the trick then (yet doesn't).
EDIT 2: So apparently @BeforeInsert() hook works but only if .save() is called with .save(new UserSession(userId)) and not with .save({ userId: userId }) (which of course requires the appropriate constructor in UserSession).
The setup
I have the entity UserSession, note the sessionId field, which is supposed to have a default value of whatever cryptoRandomString({ length: 128, type: 'alphanumeric' }) returns.
@Entity('user_session')
export class UserSession {
@PrimaryColumn({
name: 'session_id',
default: () => cryptoRandomString({ length: 128, type: 'alphanumeric' })
})
sessionId: string
@Column({
name: 'user_id',
})
userId: string
}
I then use the TypeORM repository .save() function to write the object to the database like so:
@Injectable()
export class AuthService {
constructor(
@InjectRepository(UserSession)
private readonly userSessionRepository: Repository<UserSession>
) {}
async createNewSession(userId: string): Promise<void> {
// Create new session
const session = await this.userSessionRepository.save({
userId: userId
})
// If only it got that far...
}
}
The error
At this point I kind of expect TypeORM to set a default value for sessionId if nothing is set 'manually'. However, it just throws an error:
[Nest] 23088 - 04/06/2021, 23:18:44 [ExceptionsHandler] ER_NO_DEFAULT_FOR_FIELD: Field 'session_id' doesn't have a default value +4032ms
QueryFailedError: ER_NO_DEFAULT_FOR_FIELD: Field 'session_id' doesn't have a default value
at new QueryFailedError (E:\SAC\projects\api-flexrent\node_modules\typeorm\error\QueryFailedError.js:12:28)
at Query.<anonymous> (E:\SAC\projects\api-flexrent\node_modules\typeorm\driver\mysql\MysqlQueryRunner.js:217:45)
at Query.<anonymous> (E:\SAC\projects\api-flexrent\node_modules\mysql\lib\Connection.js:526:10)
at Query._callback (E:\SAC\projects\api-flexrent\node_modules\mysql\lib\Connection.js:488:16)
at Query.Sequence.end (E:\SAC\projects\api-flexrent\node_modules\mysql\lib\protocol\sequences\Sequence.js:83:24)
at Query.ErrorPacket (E:\SAC\projects\api-flexrent\node_modules\mysql\lib\protocol\sequences\Query.js:92:8)
at Protocol._parsePacket (E:\SAC\projects\api-flexrent\node_modules\mysql\lib\protocol\Protocol.js:291:23)
at Parser._parsePacket (E:\SAC\projects\api-flexrent\node_modules\mysql\lib\protocol\Parser.js:433:10)
at Parser.write (E:\SAC\projects\api-flexrent\node_modules\mysql\lib\protocol\Parser.js:43:10)
at Protocol.write (E:\SAC\projects\api-flexrent\node_modules\mysql\lib\protocol\Protocol.js:38:16)
Despair
After spending some time on good old google I tried several variations, which all did not work (and throw the same error):
@PrimaryColumn({
name: 'session_id',
default: cryptoRandomString({ length: 128, type: 'alphanumeric' })
})
sessionId: string
or
@PrimaryColumn({
name: 'session_id',
default: () => 'cryptoRandomString({ length: 128, type: "alphanumeric" })' // Why would that even work
})
sessionId: string
or
@PrimaryColumn({
name: 'session_id',
default: 'cryptoRandomString({ length: 128, type: "alphanumeric" })' // Why would that even work
})
sessionId: string
or
@PrimaryColumn({
name: 'session_id',
default: 'please work'
})
sessionId: string
or not setting default in @PrimaryColumn() but rather using @BeforeInsert(), which apparently isn't called when using .save()
@BeforeInsert()
beforeInsertAction(): void {
this.sessionId = cryptoRandomString({ length: 128, type: 'alphanumeric' })
}
The only thing that did work was using a constructor for the UserSession class and setting the sessionId in there (which I thought kind of defeats the purpose of default values in TypeORM?):
constructor(
userId: string
) {
this.userId = userId
this.sessionId = cryptoRandomString({ length: 128, type: 'alphanumeric' })
}
and calling it with
const session = await this.userSessionRepository.save(new UserSession(userId))
So, did I miss anything? Am I approaching the thing completely wrong, to begin with? I don't know anymore.