#help custom validator

27 messages · Page 1 of 1 (latest)

fiery quest
#

I have a custom validator

export const propertyAssetsValidator = v.array(
v.object({
property_id: schema.tables.property_assets.validator.fields.property_id,
asset_url: schema.tables.property_assets.validator.fields.asset_url,
asset_type: v.union(v.literal('document'), v.literal('image'), v.literal('video')),
is_primary: schema.tables.property_assets.validator.fields.is_primary,
display_order: schema.tables.property_assets.validator.fields.display_order,
asset_category: v.optional(
v.union(
v.literal('property_image'),
v.literal('floor_plan'),
v.literal('brochure'),
v.literal('other')
)
),
description: schema.tables.property_assets.validator.fields.description
})
);

I want to use it to type my function
import propertyAssetsValidator from '../convex/addPropertyAssets';

function generateItems(
    propertyId: propertyAssetsValidator,
    listingName: string,
    documents: string[],
    floorPlans: string[],
    images: number
)
drowsy rockBOT
#

Hi @fiery quest. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

To use the propertyAssetsValidator to type your function parameters, you need to use the Infer utility provided by Convex. The Infer utility allows you to extract TypeScript types from your validators. Here's how you can do it:

First, you need to import Infer from convex/values:

import { Infer } from "convex/values";

Then, you can use Infer to create a type from your propertyAssetsValidator:

type PropertyAssetsType = Infer<typeof propertyAssetsValidator>;

Now, you can use PropertyAssetsType to type your propertyId parameter in your generateItems function:

function generateItems(
    propertyId: PropertyAssetsType,
    listingName: string,
    documents: string[],
    floorPlans: string[],
    images: number
) {
    // function body
}

Please note that Infer will infer the type of the validated value, not the validator itself. In your case, PropertyAssetsType will be an array of objects with the structure defined in propertyAssetsValidator.

For more information, you can refer to the Convex documentation on Inferring types from validators (https://docs.convex.dev/production/best-practices/typescript#inferring-types-from-validators) and Extracting TypeScript types (https://docs.convex.dev/functions/args-validation#extracting-typescript-types).

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@fiery quest kapa.ai is still learning and improving, please let us know how it did by reacting below

fiery quest
#

@drowsy rock import { mutation } from './_generated/server';
import schema from './schema';
import { v } from 'convex/values';

export const propertyAssetsValidator = v.array(
v.object({
property_id: schema.tables.property_assets.validator.fields.property_id,
asset_url: schema.tables.property_assets.validator.fields.asset_url,
asset_type: v.union(v.literal('document'), v.literal('image'), v.literal('video')),
is_primary: schema.tables.property_assets.validator.fields.is_primary,
display_order: schema.tables.property_assets.validator.fields.display_order,
asset_category: v.optional(
v.union(
v.literal('property_image'),
v.literal('floor_plan'),
v.literal('brochure'),
v.literal('other')
)
),
description: schema.tables.property_assets.validator.fields.description
})
);

function generateItems(
propertyId: string,
listingName: string,
documents: string[],
floorPlans: string[],
imageCount: number
): Array<{
property_id: string;
asset_url: string;
asset_type: typeof propertyAssetsValidator;

I want to use the type from my validator in asset_type to ensure that a valid string option is being passed

drowsy rockBOT
#

Let me check the knowledge base for you. One moment! :books:

#

To use the type from your propertyAssetsValidator for the asset_type field in your generateItems function, you can use the Infer utility from Convex to extract the TypeScript type from your validator.

First, you need to import Infer from convex/values:

import { Infer } from "convex/values";

Then, you can use Infer to create a type from your propertyAssetsValidator:

type PropertyAssetsType = Infer<typeof propertyAssetsValidator>;

Now, you can use PropertyAssetsType to type your asset_type field in your generateItems function. However, since PropertyAssetsType is an array of objects, you need to extract the asset_type field from the object. You can do this by creating a new type:

type AssetType = PropertyAssetsType[0]['asset_type'];

Now, you can use AssetType to type your asset_type field:

function generateItems(
    propertyId: string,
    listingName: string,
    documents: string[],
    floorPlans: string[],
    imageCount: number
): Array<{
    property_id: string;
    asset_url: string;
    asset_type: AssetType;
    // other fields
}> {
    // function body
}

Please note that Infer will infer the type of the validated value, not the validator itself. In your case, AssetType will be a union of the string literals 'document', 'image', and 'video'.

For more information, you can refer to the Convex documentation on Inferring types from validators (https://docs.convex.dev/production/best-practices/typescript#inferring-types-from-validators) and Extracting TypeScript types (https://docs.convex.dev/functions/args-validation#extracting-typescript-types).

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@fiery quest kapa.ai is still learning and improving, please let us know how it did by reacting below

fiery quest
#

@drowsy rock import { mutation } from './_generated/server';
import schema from './schema';
import { v, Infer } from 'convex/values';

const propertyAssetsValidator = v.array(
v.object({
property_id: schema.tables.property_assets.validator.fields.property_id,
asset_url: schema.tables.property_assets.validator.fields.asset_url,
asset_type: v.union(v.literal('document'), v.literal('image'), v.literal('video')),
asset_category: v.optional(
v.union(
v.literal('property_image'),
v.literal('floor_plan'),
v.literal('brochure'),
v.literal('other')
)
),
})
);

function generateItems(
propertyId: Infer<typeof propertyAssetsValidator>[0]['property_id'];
): Array<{
property_id: Infer<typeof propertyAssetsValidator>[0]['property_id'];

return [

]
}

Diagnostics:

  1. Type '{ property_id: string; asset_url: string; asset_type: "document" | "image" | "video"; asset_category: "property_image" | "floor_plan" | "brochure" | "other" | undefined; }[]' is not assignable to type '{ property_id: Id<"properties">; asset_url: string; asset_type: "document" | "image" | "video"; asset_category: "property_image" | "floor_plan" | "brochure" | "other" | undefined; }[]'.
    Type '{ property_id: string; asset_url: string; asset_type: "document" | "image" | "video"; asset_category: "property_image" | "floor_plan" | "brochure" | "other" | undefined; }' is not assignable to type '{ property_id: Id<"properties">; asset_url: string; asset_type: "document" | "image" | "video"; asset_category: "property_image" | "floor_plan" | "brochure" | "other" | undefined; }'.
    Types of property 'property_id' are incompatible.
    Type 'string' is not assignable to type 'Id<"properties">'.
    Type 'string' is not assignable to type '{ __tableName: "properties"; }'. [2322]
drowsy rockBOT
#

Let me check the knowledge base for you. One moment! :books:

#

The error message you're seeing is due to a type mismatch. The property_id field in your propertyAssetsValidator is expected to be of type Id<"properties">, but you're trying to assign a string to it.

The Id<"properties"> type is a special type used by Convex to represent the ID of a record in a specific table. It's not just a string, but an object that contains additional metadata.

To fix this issue, you need to ensure that the property_id you're passing to generateItems function is of type Id<"properties">.

Here's how you can do it:

import { Id } from "convex/server";

function generateItems(
    propertyId: Id<"properties">,
    // other parameters
): Array<{
    property_id: Id<"properties">;
    // other fields
}> {
    // function body
}

Now, propertyId is expected to be of type Id<"properties">, which should match the type expected by your propertyAssetsValidator.

For more information, you can refer to the Convex documentation on Building data structure types (https://stack.convex.dev/end-to-end-ts#v) and Using branded types in validators (https://stack.convex.dev/using-branded-types-in-validators).

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@fiery quest kapa.ai is still learning and improving, please let us know how it did by reacting below

fiery quest
#

@drowsy rock

        const getPropertyRecordById = async (id: string) => {
            const record = await ctx.db
                .query('properties')
                .withIndex('by_id', (q) => q.eq('_id', id))
                .unique();

            if (!record) throw new Error(`Property type "${name}" does not exist.`);

            return record._id;
        };

Diagnostics:

  1. Argument of type 'string' is not assignable to parameter of type 'Id<"properties">'.
    Type 'string' is not assignable to type '{ __tableName: "properties"; }'. [2345]
drowsy rockBOT
#

Let me check the knowledge base for you. One moment! :books:

#

The error message you're seeing is due to a type mismatch. The id parameter in your getPropertyRecordById function is expected to be of type Id<"properties">, but you're passing a string to it.

The Id<"properties"> type is a special type used by Convex to represent the ID of a record in a specific table. It's not just a string, but an object that contains additional metadata.

To fix this issue, you need to ensure that the id you're passing to getPropertyRecordById function is of type Id<"properties">.

Here's how you can do it:

import { Id } from "convex/server";

const getPropertyRecordById = async (id: Id<"properties">) => {
    const record = await ctx.db
        .query('properties')
        .withIndex('by_id', (q) => q.eq('_id', id))
        .unique();

    if (!record) throw new Error(`Property type "${name}" does not exist.`);

    return record._id;
};

Now, id is expected to be of type Id<"properties">, which should match the type expected by your query.

For more information, you can refer to the Convex documentation on Building data structure types (https://stack.convex.dev/end-to-end-ts#v) and Using branded types in validators (https://stack.convex.dev/using-branded-types-in-validators).

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@fiery quest kapa.ai is still learning and improving, please let us know how it did by reacting below

fiery quest
#

@drowsy rock i just want to pass the id string to the function

drowsy rockBOT
#

Let me check the knowledge base for you. One moment! :books:

#

If you want to pass a string ID to the function and then use it in a Convex query, you can use the normalizeId method provided by Convex. This method returns the string ID format for the ID in a given table, or null if the ID is from a different table or is not a valid ID.

Here's how you can modify your getPropertyRecordById function:

const getPropertyRecordById = async (id: string) => {
    const normalizedId = ctx.db.normalizeId("properties", id);

    if (!normalizedId) {
        throw new Error(`Invalid ID: ${id}`);
    }

    const record = await ctx.db
        .query('properties')
        .withIndex('by_id', (q) => q.eq('_id', normalizedId))
        .unique();

    if (!record) throw new Error(`Property with ID "${id}" does not exist.`);

    return record._id;
};

In this code, ctx.db.normalizeId("properties", id) will return the string ID format for the ID in the "properties" table, or null if the ID is from a different table or is not a valid ID. If the ID is not valid, an error is thrown.

For more information, you can refer to the Convex documentation on normalizeId method (https://docs.convex.dev/api/interfaces/server.GenericDatabaseReader#normalizeid).

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@fiery quest kapa.ai is still learning and improving, please let us know how it did by reacting below

fiery quest
#

@drowsy rock is this the correct way to create helper functions?

import { MutationCtx, QueryCtx } from './_generated/server';

export const getPropertyRecordById = async (ctx: MutationCtx | QueryCtx, id: string) => {
const normalizedId = ctx.db.normalizeId('properties', id);
if (!normalizedId) throw new Error(Invalid ID: ${id});

const record = await ctx.db
    .query('properties')
    .withIndex('by_id', (q) => q.eq('_id', normalizedId))
    .unique();

if (!record) throw new Error(`Property with ID "${id}" does not exist.`);

return record._id;

};

drowsy rockBOT
#

Let me check the knowledge base for you. One moment! :books:

#

Yes, this is a correct way to create helper functions in Convex.

Your getPropertyRecordById function takes a context (ctx) and an id as parameters. The context can be either a MutationCtx or a QueryCtx, which allows this function to be used in both mutations and queries.

The function then normalizes the id using ctx.db.normalizeId('properties', id). If the id is not valid, it throws an error.

Next, it queries the 'properties' table in the database using the normalized id. If no record is found, it throws an error.

Finally, it returns the _id of the found record.

This function can be used to fetch a property record by its id in a safe and reusable way.

For more information on creating helper functions in Convex, you can refer to the Convex documentation on Best Practices (https://docs.convex.dev/production/best-practices) and Functions (https://docs.convex.dev/functions).

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@fiery quest kapa.ai is still learning and improving, please let us know how it did by reacting below

fiery quest
#

@drowsy rock I have validator which constrains my asset_category to a couple literals. How can I apply this to the category in the mutation args documents category?

const propertyAssetsValidator = v.array(
    v.object({
        property_id: schema.tables.property_assets.validator.fields.property_id,
        asset_url: schema.tables.property_assets.validator.fields.asset_url,
        asset_type: v.union(v.literal('document'), v.literal('image'), v.literal('video')),
        asset_category: v.optional(
            v.union(
                v.literal('property_image'),
                v.literal('floor_plan'),
                v.literal('brochure'),
                v.literal('other')
            )
        ),
        is_primary: schema.tables.property_assets.validator.fields.is_primary,
        display_order: schema.tables.property_assets.validator.fields.display_order,
        description: schema.tables.property_assets.validator.fields.description
    })
);
export default mutation({
    args: {
        propertyId: schema.tables.property_assets.validator.fields.property_id,
        documents: v.array(
            v.object({
                name: v.string(),
                category: propertyAssetsValidator,
                description: schema.tables.property_assets.validator.fields.description
            })
        ),
drowsy rockBOT
#

Let me check the knowledge base for you. One moment! :books:

#

In your mutation, you are trying to apply the entire propertyAssetsValidator to the category field. However, based on your propertyAssetsValidator, it seems like you only want to apply the asset_category validator to the category field in your mutation args.

Here's how you can do it:

export default mutation({
    args: {
        propertyId: schema.tables.property_assets.validator.fields.property_id,
        documents: v.array(
            v.object({
                name: v.string(),
                category: schema.tables.property_assets.validator.fields.asset_category,
                description: schema.tables.property_assets.validator.fields.description
            })
        ),
    // rest of your mutation
});

In this code, schema.tables.property_assets.validator.fields.asset_category is the validator for the asset_category field in your propertyAssetsValidator. This validator is now being used for the category field in your mutation args.

For more information, you can refer to the Convex documentation on Adding validators (https://docs.convex.dev/functions/validation#adding-validators) and Argument Validation without Repetition (https://stack.convex.dev/argument-validation-without-repetition).

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: