User Signup Example
A robust user registration flow with account creation, email verification, and profile setup.
The Scenario
- Create Identity: Create a record in the auth system (e.g., Auth0, Firebase, or internal DB).
- Create Profile: Create a user profile in the main database.
- Send Verification: Send a verification email.
- Analytics: Track the signup event.
If creating the profile fails, we should delete the identity from the auth system (compensation).
The Code
ts
import { create } from "@eddiecbrl/orchestrix";
const signupFlow = create("user-signup")
.step("create-identity", async (ctx) => {
const { email, password } = ctx.input;
const authId = await authProvider.createUser(email, password);
ctx.set("authId", authId);
}, {
compensate: async (ctx) => {
const authId = ctx.get<string>("authId");
await authProvider.deleteUser(authId);
}
})
.step("create-profile", async (ctx) => {
const authId = ctx.get<string>("authId");
const { name, email } = ctx.input;
await db.users.create({ authId, name, email });
})
.step("send-verification", async (ctx) => {
const { email } = ctx.input;
await emailService.sendVerification(email);
}, {
retries: 3,
retryDelayMs: 5000 // Give the email service some room
})
.step("track-analytics", async (ctx) => {
await analytics.track("user_signed_up", { email: ctx.input.email });
});
// Run with idempotency to prevent duplicate accounts if user clicks twice
const result = await signupFlow.run(userData, {
key: `signup_${userData.email}`
});Highlights
- External Consistency: Ensures that we don't end up with an orphan identity in our auth provider if our local database fails.
- Idempotent Signup: Using the email as part of the idempotency key prevents multiple accounts being created for the same user during simultaneous requests.
- Retry Logic: Sending emails can be flaky; retries ensure a better user experience.