Kyle
Kyle
CCConvex Community
Created by Kyle on 3/19/2024 in #show-and-tell
Rust Macro: convex_model!
https://github.com/ragkit/convex-macros I've been using the Convex Rust client and found working with nested objects a bit cumbersome. I made a macro that creates native Rust structs and enums based on a Typescript schema definition. Then you can build it from convex::Value and serialize it to serde_json::Value automatically.
convex_model!(User {
_id: v.id("users"),
name: v.string(),
age: v.optional(v.int64()),
platform: v.union(
v.object({
platform: v.literal("google"),
verified: v.boolean(),
}),
v.object({
platform: v.literal("github"),
username: v.string(),
}),
),
});
convex_model!(User {
_id: v.id("users"),
name: v.string(),
age: v.optional(v.int64()),
platform: v.union(
v.object({
platform: v.literal("google"),
verified: v.boolean(),
}),
v.object({
platform: v.literal("github"),
username: v.string(),
}),
),
});
This generates pub struct User {} with various methods to convert from convex::Value and to serde_json::Value.
// This is the shape you might get from a convex function you've defined.
let user = User::from_convex_value(&Value::Object(btreemap! {
"_id".into() => Value::String("1234".into()),
"name".into() => Value::String("Alice".into()),
"age".into() => Value::Int64(42),
"platform".into() => Value::Object(btreemap! {
"platform".into() => Value::String("github".into()),
"username".into() => Value::String("alicecodes".into()),
}),
}))
.expect("it should parse");

assert_eq!("1234", user._id);
assert_eq!("alicecodes", user.platform.as_2().unwrap().username);
assert_eq!(
json!({
"_id": "1234",
"name": "Alice",
"age": 42,
"platform": {
"platform": "github",
"username": "alicecodes",
},
}),
json!(user),
);
// This is the shape you might get from a convex function you've defined.
let user = User::from_convex_value(&Value::Object(btreemap! {
"_id".into() => Value::String("1234".into()),
"name".into() => Value::String("Alice".into()),
"age".into() => Value::Int64(42),
"platform".into() => Value::Object(btreemap! {
"platform".into() => Value::String("github".into()),
"username".into() => Value::String("alicecodes".into()),
}),
}))
.expect("it should parse");

assert_eq!("1234", user._id);
assert_eq!("alicecodes", user.platform.as_2().unwrap().username);
assert_eq!(
json!({
"_id": "1234",
"name": "Alice",
"age": 42,
"platform": {
"platform": "github",
"username": "alicecodes",
},
}),
json!(user),
);
Some Features: - let user = User::from_convex_value(value)?; to parse a value from Convex client. - json!(user) to serialize as json. - Discriminated unions are automatically handled. - Helper functions for each union branch: user.platform.as_2()?.username.
10 replies
CCConvex Community
Created by Kyle on 3/13/2024 in #support-community
Mark argument as sensitive to avoid printing it during errors?
Is there a way to mark arguments as sensitive so that when there are errors convex will not print out the variable value in the error messages? For example I pass a "header" arg to some functions which contains an api key that I use to verify a user has permission to run a query/mutation. If something goes wrong though it will print out all the args in plaintext which is not behavior I ever want to be possible for this arg:
[Request ID: yA0NUBpWA99sniqy] Server Error\nArgumentValidationError: Object is missing the required field `namespace`. Consider wrapping the field validator in `v.optional(...)` if this is expected.\n\nObject: {header: \"Bearer rks_(sensitive value)\", key: \"0b2a6bde-698f-4bbe-9740-fb4a3b42c0a9\"}\nValidator: v.object({header: v.string(), namespace: v.string()})\n\n
[Request ID: yA0NUBpWA99sniqy] Server Error\nArgumentValidationError: Object is missing the required field `namespace`. Consider wrapping the field validator in `v.optional(...)` if this is expected.\n\nObject: {header: \"Bearer rks_(sensitive value)\", key: \"0b2a6bde-698f-4bbe-9740-fb4a3b42c0a9\"}\nValidator: v.object({header: v.string(), namespace: v.string()})\n\n
7 replies
CCConvex Community
Created by Kyle on 2/17/2024 in #support-community
Can you force the return of a handler when defining a custom query?
When using convex-helpers to define a customQuery, is it possible to force the return of the handler? All the examples throw when there is, for example, an authentication issue. Instead I'd like to have a type Result<T> = {success: true, data: T} | {success: false, code: number, message: string} that all queries return, and then when there is an authentication issue force the handler to return {success: false, code: 401, message: "Invalid authentication."}. I'm looking at this: https://stack.convex.dev/custom-functions, which recommends using throws like this:
const userQuery = customQuery(
query,
customCtx(async (ctx) => {
const user = await getUser(ctx);
if (!user) throw new Error("Authentication required");
return { user, db };
})
);
const userQuery = customQuery(
query,
customCtx(async (ctx) => {
const user = await getUser(ctx);
if (!user) throw new Error("Authentication required");
return { user, db };
})
);
6 replies
CCConvex Community
Created by Kyle on 2/16/2024 in #show-and-tell
Example Utility for Joining between Tables
Made a fun little helper for joining tables: https://gist.github.com/kyldvs/98aed78f96e9376b55092493a8bb0b75
// This has { _id: Id<"orgRole">, orgId: Id<"org">, ... }
const roles = await ctx.db.query("orgRole").collect();

// This has { _id: Id<"orgRole">, org: Doc<"org"> | undefined, ... }
const rolesWithOrg = await join<"orgRole", "org">(ctx.db, roles, "orgId");
// This has { _id: Id<"orgRole">, orgId: Id<"org">, ... }
const roles = await ctx.db.query("orgRole").collect();

// This has { _id: Id<"orgRole">, org: Doc<"org"> | undefined, ... }
const rolesWithOrg = await join<"orgRole", "org">(ctx.db, roles, "orgId");
Based on: https://docs.convex.dev/database/reading-data#join
4 replies