W
Wasp3w ago
marcoeg

signup() not accepting custom fields in Typescript

I am having hard time to successfully write a custom signup page in Typescript. I followed the details on the Customizing the Signup Process and the Defining Extra Fields documents. I have defined my own custom component for the signup form, defined the userSignupFields object, all as described. However, unlike the example provided when I try to call the signup() function with any additional fields other than email and password, for instance firstName, I get the error:
[ Wasp ] ext-src/auth/SignupPage.tsx(83,9): error TS2353: Object literal may only specify known properties, and 'firstName' does not exist in type '{ email: string; password: string; }'.
[ Wasp ] ext-src/auth/SignupPage.tsx(83,9): error TS2353: Object literal may only specify known properties, and 'firstName' does not exist in type '{ email: string; password: string; }'.
If I bypass type checking, I don't get the error but the additional field is not stored in the database:
const onSubmit = async (data: any) => {
try {
await signup({
email: data.email,
password: data.password,
firstName: data.firstName,
} as any )
...
const onSubmit = async (data: any) => {
try {
await signup({
email: data.email,
password: data.password,
firstName: data.firstName,
} as any )
...
Please advise.
4 Replies
kapa.ai
kapa.ai3w ago
Hi @marcoeg. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Thank you for your question about customizing the signup process in Wasp using TypeScript. I understand you're having difficulties adding custom fields to the signup function. Let's address this issue based on the information provided in the Wasp documentation. The error you're encountering suggests that the signup() function is not expecting the firstName field. This is because by default, Wasp's signup() function only accepts email and password fields. To add custom fields, you need to follow a specific process. Here's how you can resolve this: 1. First, make sure you've defined the extra fields in your main.wasp file: API Reference shows how to do this:
app myApp {
// ...
auth: {
userEntity: User,
methods: {
usernameAndPassword: {
userSignupFields: import { userSignupFields } from "@src/auth/signup",
},
},
onAuthFailedRedirectTo: "/login",
},
}
app myApp {
// ...
auth: {
userEntity: User,
methods: {
usernameAndPassword: {
userSignupFields: import { userSignupFields } from "@src/auth/signup",
},
},
onAuthFailedRedirectTo: "/login",
},
}
2. Then, in your src/auth/signup.ts file, define the userSignupFields object: API Reference provides this example:
import { defineUserSignupFields } from 'wasp/server/auth'

export const userSignupFields = defineUserSignupFields({
firstName: async (data) => {
const firstName = data.firstName
if (typeof firstName !== 'string') {
throw new Error('First name is required')
}
if (firstName.length < 2) {
throw new Error('First name must be at least 2 characters long')
}
return firstName
},
})
import { defineUserSignupFields } from 'wasp/server/auth'

export const userSignupFields = defineUserSignupFields({
firstName: async (data) => {
const firstName = data.firstName
if (typeof firstName !== 'string') {
throw new Error('First name is required')
}
if (firstName.length < 2) {
throw new Error('First name must be at least 2 characters long')
}
return firstName
},
})
3. Make sure your User entity in main.wasp includes the firstName field:
entity User {=psl
id Int @id @default(autoincrement())
firstName String?
psl=}
entity User {=psl
id Int @id @default(autoincrement())
firstName String?
psl=}
4. In your custom signup component, you should now be able to include the firstName field when calling signup():
const onSubmit = async (data: SignupFormData) => {
try {
await signup({
email: data.email,
password: data.password,
firstName: data.firstName,
})
// ...
} catch (error) {
// Handle error
}
}
const onSubmit = async (data: SignupFormData) => {
try {
await signup({
email: data.email,
password: data.password,
firstName: data.firstName,
})
// ...
} catch (error) {
// Handle error
}
}
If you're still encountering issues after following these steps, it's possible that there might be a mismatch between the TypeScript types and the actual implementation. In this case, you might need to extend the type definition for the signup function. If the problem persists, you may want to try using a custom signup action as described in the Username & Password Authentication documentation. This approach gives you more control over the signup process and allows you to handle custom fields more easily. Remember to run wasp clean and restart your development server after making changes to the Wasp configuration files. If you're still facing issues after trying these solutions, please provide more details about your main.wasp file, User entity definition, and the complete userSignupFields implementation. This will help in providing a more specific solution to your problem. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @marcoeg, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
marcoeg
marcoegOP3w ago
@kapa.ai how do I check the signup() return value for trying to signup an existing user? It seems it always return success even when trying to signup the same username and email twice. I am using Email authentication.
Vinny (@Wasp)
Vinny (@Wasp)3w ago
Hey @marcoeg did the bot answer your first question? For your second question, wouldn't you have to set that yourself in your custom signup function? Check if the user exists already and, if so, return an HttpError @kapa.ai can you show how to check if the user exists already and, if so, return an HttpError in a custom signup function?
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Certainly! I'd be happy to show you how to check if a user already exists and return an HttpError in a custom signup function. This is indeed a good practice to handle duplicate user registrations more gracefully. Here's an example of how you can implement this in a custom signup function:
import {
ensurePasswordIsPresent,
ensureValidPassword,
ensureValidEmail,
createProviderId,
sanitizeAndSerializeProviderData,
findAuthIdentity,
createUser,
HttpError,
} from 'wasp/server/auth'
import type { CustomSignup } from 'wasp/server/operations'

type CustomSignupInput = {
email: string
password: string
}
type CustomSignupOutput = {
success: boolean
message: string
}

export const signup: CustomSignup<CustomSignupInput, CustomSignupOutput> = async (args, _context) => {
ensureValidEmail(args)
ensurePasswordIsPresent(args)
ensureValidPassword(args)

try {
const providerId = createProviderId('email', args.email)
const existingAuthIdentity = await findAuthIdentity(providerId)

if (existingAuthIdentity) {
// User already exists, throw an HttpError
throw new HttpError(422, 'User with this email already exists')
}

const providerData = await sanitizeAndSerializeProviderData<'email'>({
hashedPassword: args.password,
})

await createUser(
providerId,
providerData,
// Any additional data you want to store on the User entity
{},
)

return {
success: true,
message: 'User created successfully',
}
} catch (e) {
if (e instanceof HttpError) {
// Re-throw HttpErrors as they are
throw e
}
// For other errors, you might want to log them and return a generic error
console.error('Signup error:', e)
return {
success: false,
message: 'An error occurred during signup',
}
}
}
import {
ensurePasswordIsPresent,
ensureValidPassword,
ensureValidEmail,
createProviderId,
sanitizeAndSerializeProviderData,
findAuthIdentity,
createUser,
HttpError,
} from 'wasp/server/auth'
import type { CustomSignup } from 'wasp/server/operations'

type CustomSignupInput = {
email: string
password: string
}
type CustomSignupOutput = {
success: boolean
message: string
}

export const signup: CustomSignup<CustomSignupInput, CustomSignupOutput> = async (args, _context) => {
ensureValidEmail(args)
ensurePasswordIsPresent(args)
ensureValidPassword(args)

try {
const providerId = createProviderId('email', args.email)
const existingAuthIdentity = await findAuthIdentity(providerId)

if (existingAuthIdentity) {
// User already exists, throw an HttpError
throw new HttpError(422, 'User with this email already exists')
}

const providerData = await sanitizeAndSerializeProviderData<'email'>({
hashedPassword: args.password,
})

await createUser(
providerId,
providerData,
// Any additional data you want to store on the User entity
{},
)

return {
success: true,
message: 'User created successfully',
}
} catch (e) {
if (e instanceof HttpError) {
// Re-throw HttpErrors as they are
throw e
}
// For other errors, you might want to log them and return a generic error
console.error('Signup error:', e)
return {
success: false,
message: 'An error occurred during signup',
}
}
}
This example is based on the custom signup action documentation from Wasp's Email Authentication docs and Username & Password Authentication docs. Here's what this code does: 1. It uses findAuthIdentity to check if a user with the given email already exists. 2. If a user is found, it throws an HttpError with a 422 status code (Unprocessable Entity) and a descriptive message. 3. If no existing user is found, it proceeds with creating the new user. 4. Any HttpError thrown (including our custom one) is re-thrown, while other errors are caught and returned as a generic error message. Remember to define this custom signup action in your main.wasp file:
action customSignup {
fn: import { signup } from "@src/auth/signup.js",
entities: [User]
}
action customSignup {
fn: import { signup } from "@src/auth/signup.js",
entities: [User]
}
This approach allows you to provide more meaningful error messages to your frontend, which can then display appropriate feedback to the user attempting to sign up. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @Vinny (@Wasp), I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?

Did you find this page helpful?