#Handlig dates in convex
13 messages · Page 1 of 1 (latest)
Hi @dreamy furnace. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
Handling dates in Convex requires some care, as Convex does not have a native date type. Here’s how you can address API responses containing dates without losing type safety:
1. Store Dates as Strings or Numbers
Convex recommends storing dates as either:
- Numbers (e.g., Unix timestamps in milliseconds, as with
Date.now()) - Strings (e.g., ISO8601 format:
"2024-03-21T14:37:15Z")
This approach preserves type safety, as you can define your schema fields as v.string() or v.number(), and TypeScript will enforce these types throughout your codebase. When you need to work with a date, you can convert the string or number to a JavaScript Date object on the client or server as needed:
const date = new Date(timeInMsSinceEpoch); // if stored as number
const date = new Date(isoString); // if stored as string
2. Avoid Storing Raw Date Objects
Convex does not support storing JavaScript Date objects directly. If you try to insert a Date object (even inside a v.any() field), you’ll get an error. If your API response includes Date objects, you should serialize them to strings before storing:
const safeData = JSON.parse(JSON.stringify(dataWithDates));
This will convert all Date objects to ISO strings, which are supported by Convex and maintain type safety if your schema expects strings Date is not a supported Convex type in v.any() field.
3. Type Safety with Zod or Validators
If you use Zod or Convex validators, define your date fields as z.string() or v.string(). This ensures that only strings (not Date objects) are accepted, and TypeScript will infer the correct types for your API responses. You can then parse these strings into Date objects where needed, preserving type safety end-to-end Zod with TypeScript for Server-side Validation and End-to-End Types.
4. End-to-End Type Safety
Convex’s type system is designed to provide end-to-end type safety. By defining your schema and function return types carefully (using only supported types), you ensure that your API responses are always type-safe. Avoid using v.any() unless absolutely necessary, as it will forfeit type safety.
Summary:
- Store dates as strings (ISO8601) or numbers (timestamps).
- Never store raw
Dateobjects in Convex. - Use validators to enforce and infer types.
- Serialize API responses with dates to strings before storing.
This approach will let you handle dates in Convex without losing type safety.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@dreamy furnace Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@calm blade I am not trying to store them, I'm just passing them back to the client through a query, and I'm getting this error. The probelm is this response is from a library and has a huge type, how do I convert this without losing my typesafety with json.parse(json.stringify())?
Let me check the knowledge base for you again. One moment! :books:
If you are returning data from a Convex query and the response (from a library) contains Date objects, you will encounter errors because Convex does not support the Date type in its value system—even when using v.any(). Convex only allows serializable types like strings, numbers, arrays, and plain objects. Date objects must be converted to strings (typically ISO8601) before returning them from a query function.
The common workaround is to use JSON.parse(JSON.stringify(obj)) to recursively convert all Date objects to strings. However, as you noted, this approach loses type safety because the result is typed as any or a generic object, not your original type.
What the Knowledge Sources Say
- Convex will not parse strings as dates, but if your data includes a
Dateobject, you will get an error. The recommended workaround is to useJSON.parse(JSON.stringify(input))to convert allDateobjects to strings before returning or storing them. This is a known and accepted workaround in the Convex community, especially when dealing with third-party library responses with complex types Date is not a supported Convex type in v.any() field. - There is no built-in Convex utility to recursively convert
Dateobjects to strings while preserving TypeScript types. The knowledge sources do not mention a type-safe, automatic way to do this. TheJSON.parse(JSON.stringify(...))method is the community recommendation, but it does lose type information.
Type Safety Tradeoff
Unfortunately, the knowledge sources do not provide a way to preserve your original TypeScript types when using this conversion. You will need to either:
- Accept the loss of type safety for this response, or
- Write a custom function that recursively walks your object, converts
Dateobjects to strings, and preserves the structure (but this is not covered in the knowledge sources).
Summary
- You must convert all
Dateobjects to strings before returning them from a Convex query. - The recommended method is
JSON.parse(JSON.stringify(obj)), but this loses type safety. - There is no built-in or documented way in Convex to preserve type safety during this conversion.
If you need strict type safety, you would need to write a custom converter, but the knowledge sources do not provide guidance or examples for this.
“Convex won’t parse strings at all, if you’re getting that error then your data includes a Date object...
JSON.parse(JSON.stringify(input))should work.”
Date is not a supported Convex type in v.any() field
I'm sorry, but the knowledge sources do not contain a type-safe solution for this scenario.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@dreamy furnace Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@calm blade I need the raw body of a request in the http webhook reciever to validate the request, how do can I do that?
Let me check the knowledge base for you again. One moment! :books:
To access the raw body of a request in a Convex HTTP webhook receiver, you should use the .text() method on the Request object provided to your HTTP action handler. This allows you to retrieve the raw string payload, which is necessary for validating signatures (such as with Stripe, GitHub, or Clerk webhooks).
Here’s an example pattern from the knowledge sources:
export const webhook = httpAction(async (ctx, request) => {
const rawBody = await request.text();
// Use rawBody for signature validation
// ... your validation logic here ...
return new Response(null, { status: 200 });
});
This approach is used in several webhook validation examples, such as Stripe, GitHub, and Clerk integrations. For example, in the Stripe webhook handler, the raw body is obtained with await request.text() and then passed to the signature verification logic Wake up, you need to make money! (Add Stripe to your product):
const signature: string = request.headers.get("stripe-signature") as string;
const result = await ctx.runAction(internal.stripe.fulfill, {
signature,
payload: await request.text(),
});
Similarly, for Clerk webhooks:
async function validateRequest(req: Request): Promise<WebhookEvent | null> {
const payloadString = await req.text();
// ... use payloadString for validation ...
}
Webhook endpoint implementation