#create a user
9 messages · Page 1 of 1 (latest)
Hi @pliant finch. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
You can’t directly call signIn/signUp from a mutation to create users with Convex Auth, but you can programmatically create a user + auth account from server code using the createAccount helper from @convex-dev/auth/server. [createAccount; Discord: Sign up users from backend]
The pattern (shown in the Discord thread) is:
// convex/users.ts
import { createAccount } from "@convex-dev/auth/server";
import { mutation } from "./_generated/server";
import { v } from "convex/values";
export const createUserWithPhone = mutation({
args: {
phone: v.string(),
// optionally any other profile fields your "users" schema allows
// e.g. name: v.optional(v.string()),
},
handler: async (ctx, args) => {
const providerId = "phone"; // or whatever ConvexCredentials / provider id you use
// `account.id` is the unique external id — for phone flows this is usually the phone number
const account = {
id: args.phone,
// If you’re storing a secret (e.g. OTP, password), put it here as `secret`
// secret: "...",
};
// This must match your "users" table schema
const profile = {
phone: args.phone,
// name: args.name ?? null,
};
const userId = await createAccount(ctx, {
provider: providerId,
account,
profile,
// Only set these to true if the phone is verified before sign‑in is allowed
shouldLinkViaEmail: false,
shouldLinkViaPhone: false,
});
// `userId` is the Id<"users"> of the created (or linked) user
return userId;
},
});
createAccount:
- Inserts into
"users"withprofile, and - Inserts into
authAccountswith the givenproviderandaccount.id, - Returns the created user’s ID. [createAccount]
If you also want automatic linking rules (e.g. by email/phone) or more control, you can alternatively implement a callbacks.createOrUpdateUser in your convex/auth.ts, but that callback is invoked as part of the normal sign‑in / verification flow, not arbitrarily from any mutation. [createOrUpdateUser]
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@pliant finch Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@distant terrace import { createAccount } from "@convex-dev/auth/server"; this function requires an action context
Let me check the knowledge base for you again. One moment! :books:
You’re right: createAccount currently requires an ActionCtx, not a MutationCtx. [createAccount]
From the docs:
ctx|GenericActionCtx<DataModel>
So you cannot call createAccount directly inside a mutation handler.
To create a user given a phone number and get its ID, you need to:
- Define an internal action that calls
createAccount. - Call that action from your mutation using the scheduler (since mutations can’t directly call actions).
1. Internal action that creates the user
// convex/authInternal.ts
import { internalAction } from "./_generated/server";
import { v } from "convex/values";
import { createAccount } from "@convex-dev/auth/server";
export const createUserWithPhoneInternal = internalAction({
args: {
phone: v.string(),
},
handler: async (ctx, { phone }) => {
const account = {
id: phone, // unique external ID for this provider, usually the phone number
// secret: "...", // optional, if you store a secret
};
const profile = {
phone,
// add any other fields required by your `users` schema
};
const userId = await createAccount(ctx, {
provider: "phone", // your provider id
account,
profile,
shouldLinkViaPhone: false, // or true if you know it's verified
});
return userId;
},
});
createAccount returns the user ID. [createAccount]
2. Mutation that schedules this action
You can then expose a mutation that schedules this internal action:
// convex/users.ts
import { mutation } from "./_generated/server";
import { internal } from "./_generated/api";
import { v } from "convex/values";
export const createUserWithPhone = mutation({
args: { phone: v.string() },
handler: async (ctx, { phone }) => {
// Run immediately (delay 0). Returns a job id; the action result itself
// is not returned to this mutation.
await ctx.scheduler.runAfter(
0,
internal.authInternal.createUserWithPhoneInternal,
{ phone }
);
// If you need the userId synchronously in the client, call the action
// directly from the client instead of via a mutation.
return null;
},
});
Mutations can only trigger actions via `ctx.scheduler.runAfter`, they cannot call them directly. [[Mutations & scheduler](<https://docs.convex.dev/functions/mutation-functions#the-mutation-constructor>); [Calling actions from clients](<https://docs.convex.dev/functions/actions#calling-actions-from-clients>)]
If you truly need the `userId` **immediately** on the client, call the **action** directly from the client (via `useAction` / HTTP) and return `userId` from that action.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------