Lets quickly define some services with this new system
we do this with zod, I think ill add the default convex validator in eventually but ill need to do some thorough refactoring
We will define the schema, triggers, rls, for a basic user/identity system (with some other things thrown in to show off the api)
//schema.ts
export const IdentityTypes = z.enum([
'password',
'oauth',
'magic_link',
'otp',
'saml',
])
const IdentitySchema = z.object({
type: IdentityTypes,
isActive: z.boolean(),
providerId: z.string().uuid(),
userId: zid('users'),
// Refine Test
password: z.string(),
confirmPassword: z.string(),
email: z.string().email(),
passwordHash: z.string().optional(),
tokenHash: z.string().optional(),
metadata: z.record(z.any()).optional(),
lastUsedAt: z.number().optional(),
})
export const IdentitiesService = defineService({
name: 'identities',
schema: IdentitySchema,
})
.default('isActive', true)
.default('providerId', () => crypto.randomUUID())
.default('type', 'password')
.unique(['providerId', 'email'])
.rls({
read: async (ctx, doc) => {
const identity = await ctx.auth.getUserIdentity()
if (!identity) return false
return identity.subject === doc.userId
},
insert: async (ctx, doc) => {
const identity = await ctx.auth.getUserIdentity()
if (!identity) return false
return identity.subject === doc.userId
},
modify: async (ctx, doc) => {
const identity = await ctx.auth.getUserIdentity()
if (!identity) return false
return identity.subject === doc.userId
},
})
.index('by_user', ['userId'])
.searchIndex('email', {
searchField: 'email',
filterFields: ['type'],
})
.validate(
IdentitySchema.refine((data) => data.password === data.confirmPassword, {
message: 'Passwords do not match',
path: ['confirmPassword'],
})
)