-
Hi there ! It's the first time I'm using Adonis and so far I love it, thanks for this awesome framework !! I'm building an API for a small project and I would like to have all the model fields returned when doing serialization even if the field is empty. Here's my model : export interface UserInterface {
id: number
email: string
password: string
firstName?: string
lastName?: string
dateOfBirth?: DateTime
phoneNumber?: string
createdAt: DateTime
updatedAt: DateTime
}
export default class User extends BaseModel implements UserInterface {
@column({ isPrimary: true })
public id: number
@column()
public email: string
@column({ serializeAs: null })
public password: string
@column({ serializeAs: 'firstName' })
public firstName?: string
@column({ serializeAs: 'lastName' })
public lastName?: string
@column.date({ serializeAs: 'dateOfBirth' })
public dateOfBirth?: DateTime
@column({ serializeAs: 'phoneNumber' })
public phoneNumber?: string
@column.dateTime({ autoCreate: true, serializeAs: 'createdAt' })
public createdAt: DateTime
@column.dateTime({ autoCreate: true, autoUpdate: true, serializeAs: 'updatedAt' })
public updatedAt: DateTime
@beforeSave()
public static async hashPassword(user: User) {
if (user.$dirty.password) {
user.password = await Hash.make(user.password)
}
}
} And here's my controller : export default class AuthController {
public async register({ auth, logger, request, response }: HttpContextContract) {
const validationSchema = schema.create({
email: schema.string({ trim: true }, [
rules.email(),
rules.maxLength(255),
rules.unique({ table: 'users', column: 'email' }),
rules.required(),
]),
password: schema.string({ trim: true }, [rules.maxLength(180), rules.required()]),
firstName: schema.string.optional({ trim: true }, [rules.maxLength(150)]),
lastName: schema.string.optional({ trim: true }, [rules.maxLength(150)]),
phoneNumber: schema.string.optional({ trim: true }, [rules.maxLength(50)]),
dateOfBirth: schema.date.optional(),
})
const validationResult = await handleControllerError<{
email: string
password: string
firstName?: string
lastName?: string
phoneNumber?: string
dateOfBirth?: DateTime
}>(() => request.validate({ schema: validationSchema }), PossibleErrors.VALIDATION_ERROR)
if (isControllerError(validationResult)) {
logger.error('register/validationResult', validationResult)
return response.internalServerError(validationResult)
}
const userResult = await handleControllerError<User>(
() => User.create(validationResult),
PossibleErrors.DATABASE_ERROR
)
if (isControllerError(userResult)) {
logger.error('register/userResult', userResult)
return response.internalServerError(userResult)
}
const tokenResult = await handleControllerError<Promise<any>>(
() => auth.login(userResult),
PossibleErrors.AUTHENTICATION_ERROR
)
if (isControllerError(tokenResult)) {
logger.error('register/tokenResult', tokenResult)
return response.internalServerError(tokenResult)
}
return response.created({
user: userResult.serialize(),
// Tried including all the fields manually
// user: userResult.serialize({
// fields: {
// pick: [
// 'id',
// 'email',
// 'firstName',
// 'lastName',
// 'dateOfBirth',
// 'phoneNumber',
// 'createdAt',
// 'updatedAt',
// ],
// },
// }),
token: tokenResult,
})
}
} When I register a user by sending a POST request to the register endpoint with these informations : I would expect to have all the fields, but with I have two questions concerning this code :
Also, if I can suggest one thing, if returning empty fields is not feasible right now, what about implementing a parameter to Thanks in advance for any future answer !! |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 1 reply
-
return response.created({
user: Object.entries(userResult.serialize()).reduce(
(prev, [property, value]) => ({
...prev,
[property]: value ?? null
}),
{}
)
}) This will check every serializable property (from
|
Beta Was this translation helpful? Give feedback.
-
When I go through the code of the |
Beta Was this translation helpful? Give feedback.
-
Okay, I found out the reason of my issue. This is due to the import { DateTime } from 'luxon'
import Hash from '@ioc:Adonis/Core/Hash'
import { column, beforeSave, BaseModel } from '@ioc:Adonis/Lucid/Orm'
export interface UserInterface {
id: number
email: string
password: string
firstName?: string
lastName?: string
dateOfBirth?: DateTime
phoneNumber?: string
createdAt: DateTime
updatedAt: DateTime
}
export default class User extends BaseModel implements UserInterface {
@column({ isPrimary: true })
public id: number
@column()
public email: string
@column({ serializeAs: null })
public password: string
@column({
serializeAs: 'firstName',
serialize: (value?: string) => {
return value ?? null
},
})
public firstName?: string
@column({
serializeAs: 'lastName',
serialize: (value?: string) => {
return value ?? null
},
})
public lastName?: string
@column.date({
serializeAs: 'dateOfBirth',
serialize: (value?: DateTime) => {
return value ?? null
},
})
public dateOfBirth?: DateTime
@column({
serializeAs: 'phoneNumber',
serialize: (value?: string) => {
return value ?? null
},
})
public phoneNumber?: string
@column.dateTime({ autoCreate: true, serializeAs: 'createdAt' })
public createdAt: DateTime
@column.dateTime({ autoCreate: true, autoUpdate: true, serializeAs: 'updatedAt' })
public updatedAt: DateTime
@beforeSave()
public static async hashPassword(user: User) {
if (user.$dirty.password) {
user.password = await Hash.make(user.password)
}
}
} Even by doing so, my attributes are not getting serialized to |
Beta Was this translation helpful? Give feedback.
-
Okay, I finally fixed my issue. Here's the full explanation of the issue. Imagine that you have a User model like the following : class User extends BaseModel {
@column({ isPrimary: true })
public id: number
@column()
public email: string
@column({ serializeAs: null })
public password: string
@column({ serializeAs: 'firstName' })
public firstName: string
} Now, imagine that I allow my user to create an account without indicating the export default class AuthController {
public async register({ auth, logger, request, response }: HttpContextContract) {
const validationSchema = schema.create({
email: schema.string({ trim: true }, [
rules.email(),
rules.maxLength(255),
rules.unique({ table: 'users', column: 'email' }),
rules.required(),
]),
password: schema.string({ trim: true }, [rules.maxLength(180), rules.required()]),
firstName: schema.string.optional({ trim: true }, [rules.maxLength(150)]),
})
const validatedData = await request.validate(validationSchema)
const user = await User.create(validatedData)
return response.created(user.serialize()) Now, if I send a request to this controller, with this body : {
"email": "[email protected]",
"password": "testPassword"
} What's going to happen is that the To make the field appear in the serialization, we need to re-fetch from the database, as the value in the DB will be const user = await User.create(validatedData)
user.refresh()
return response.created(user.serialize()) And now, in the response, you will see the I think I will create a PR on the documentation of Adonis to explain that behaviour in the Model documentation. |
Beta Was this translation helpful? Give feedback.
Okay, I finally fixed my issue. Here's the full explanation of the issue.
Imagine that you have a User model like the following :
Now, imagine that I allow my user to create an account without indicating the
firstName
. For this, I would have something along those lines in my controller :