beerman
beerman3mo ago

Svelte SSR

@ballingt So I'm getting an error when switching between statuses.
<script lang="ts">
import { page } from '$app/stores';
import { useQuery } from 'convex-svelte';
import { api } from '../../../convex/_generated/api';
import { goto } from '$app/navigation';
import PropertyCard from '$lib/components/PropertyCard.svelte';

let status = $derived($page.params.status) as 'for-sale' | 'for-rent';
// if (!['for-sale', 'for-rent'].includes(status)) goto('/properties');

let properties = $derived(useQuery(api.getProperties.get, { status: status }));

$inspect(properties);
</script>
<script lang="ts">
import { page } from '$app/stores';
import { useQuery } from 'convex-svelte';
import { api } from '../../../convex/_generated/api';
import { goto } from '$app/navigation';
import PropertyCard from '$lib/components/PropertyCard.svelte';

let status = $derived($page.params.status) as 'for-sale' | 'for-rent';
// if (!['for-sale', 'for-rent'].includes(status)) goto('/properties');

let properties = $derived(useQuery(api.getProperties.get, { status: status }));

$inspect(properties);
</script>
No description
37 Replies
ballingt
ballingt3mo ago
How is your code different from this example here https://github.com/get-convex/convex-svelte This looks like a client-side error, not an SSR error an SSR error would appear in the terminal see also the svelte channel here https://discord.com/channels/1019350475847499849/1278420438149496952 Do you have setupConvex in a parent component?
beerman
beermanOP3mo ago
I reread and made sure I did everything right. So let me rephrase, this error only occurs when I put my query in $derived() which I have to otherwise properties will not refetch when status changes when switching routes. Of course, otherwise it wouldn't work at all.
ballingt
ballingt3mo ago
I have to drive now, folks in that Svelte channel might hve ideas. I don't know why putting this in derived would change how useQuery grabs the current convex object
beerman
beermanOP3mo ago
Actually, I just got it to work. The problem was that this:
let status = $derived($page.params.status) as 'for-sale' | 'for-rent';

let properties = useQuery(api.getProperties.get, { status: status })
let status = $derived($page.params.status) as 'for-sale' | 'for-rent';

let properties = useQuery(api.getProperties.get, { status: status })
would produce:
Diagnostics: 1. State referenced in its own scope will never update. Did you mean to reference it inside a closure? [state_referenced_locally]
However, this works
let status = $derived($page.params.status) as 'for-sale' | 'for-rent';

let properties = useQuery(api.getProperties.get, () => ({ status }), {
keepPreviousData: true
});
let status = $derived($page.params.status) as 'for-sale' | 'for-rent';

let properties = useQuery(api.getProperties.get, () => ({ status }), {
keepPreviousData: true
});
I guess because the anonymous function runs when I call the query reevaluating the status Is there any way to load initial data on server via a load function?
ballingt
ballingt3mo ago
Yeah, see the example in the repo for this pattern
beerman
beermanOP3mo ago
Yep, thanks. You should add Deno do your documentation You already added Bun and Deno 2.0 is out so might as well I've implemented it but there is a problem i've been trying to resolve for a day now and can't seem to get it right
Diagnostics: Argument of type '() => { status: string; }' is not assignable to parameter of type '{ status?: "for-sale" | "for-rent" | undefined; } | (() => { status?: "for-sale" | "for-rent" | undefined; })'. Type '() => { status: string; }' is not assignable to type '() => { status?: "for-sale" | "for-rent" | undefined; }'. Call signature return types '{ status: string; }' and '{ status?: "for-sale" | "for-rent" | undefined; }' are incompatible. The types of 'status' are incompatible between these types. Type 'string' is not assignable to type '"for-sale" | "for-rent" | undefined'. [2345]
getProperties.ts
export const PropertyStatusValidator = v.union(
v.literal("for-sale"),
v.literal("for-rent"),
);
export type PropertyStatusType = Infer<typeof PropertyStatusValidator>;

export const get = query({
args: {
status: v.optional(PropertyStatusValidator),
export const PropertyStatusValidator = v.union(
v.literal("for-sale"),
v.literal("for-rent"),
);
export type PropertyStatusType = Infer<typeof PropertyStatusValidator>;

export const get = query({
args: {
status: v.optional(PropertyStatusValidator),
+page.server.ts
import type { PropertyStatusType } from "../../../convex/getProperties.ts";

export async function load(
{ params }: { params: { status: string } },
) {
const client = new ConvexHttpClient(PUBLIC_CONVEX_URL!);

if (!["for-sale", "for-rent"].includes(params.status)) {
redirect(302, "/properties");
}

return {
status: params.status as PropertyStatusType,
properties: await client.query(api.getProperties.get, {
status: params.status as PropertyStatusType,
}),
};
}
import type { PropertyStatusType } from "../../../convex/getProperties.ts";

export async function load(
{ params }: { params: { status: string } },
) {
const client = new ConvexHttpClient(PUBLIC_CONVEX_URL!);

if (!["for-sale", "for-rent"].includes(params.status)) {
redirect(302, "/properties");
}

return {
status: params.status as PropertyStatusType,
properties: await client.query(api.getProperties.get, {
status: params.status as PropertyStatusType,
}),
};
}
I am returning both as their appropriate types. +page.svelte
let { data }: { data: PageData } = $props();
let initialProperties = $derived(data.properties);

let properties = useQuery(
api.getProperties.get,
() => ({
status: data.status
}),
() => ({
initialData: initialProperties,
keepPreviousData: true
})
);
</script>
let { data }: { data: PageData } = $props();
let initialProperties = $derived(data.properties);

let properties = useQuery(
api.getProperties.get,
() => ({
status: data.status
}),
() => ({
initialData: initialProperties,
keepPreviousData: true
})
);
</script>
beerman
beermanOP3mo ago
as you can see, the data passed into the page is typed as properties is of the correct type, but the status type doesn't want to come through. I could just cast it after the check and redirect like so const status = params.status as PropertyStatusType; before returning it, but that doesn't feel right. Is there a validator.safeParse() for convex?
No description
beerman
beermanOP3mo ago
Another thing, it looks the most recent commit to convex-backend broke Infer
No description
ballingt
ballingt3mo ago
most recent commit to convex-backend? What verison of convex are you using?
beerman
beermanOP3mo ago
Sorry, convex-js
beerman
beermanOP3mo ago
No description
beerman
beermanOP3mo ago
I think it's something to do with my machine actually No recent commits to the lib
beerman
beermanOP3mo ago
No description
beerman
beermanOP3mo ago
Yes, I know
beerman
beermanOP3mo ago
No description
beerman
beermanOP3mo ago
I'm really confused
ballingt
ballingt3mo ago
Do you have a file in your convex directories called 'values'?
beerman
beermanOP3mo ago
No description
ballingt
ballingt3mo ago
If you gf / gd on that import what do you get, what does "convex/values" look like?
beerman
beermanOP3mo ago
No description
beerman
beermanOP3mo ago
No description
beerman
beermanOP3mo ago
And I don't have a bad import anywhere
No description
ballingt
ballingt3mo ago
Is it possible you're using Infer as a value somewhere instead of a type e.g. convex foo = Infer
beerman
beermanOP3mo ago
No description
beerman
beermanOP3mo ago
No, only as parameter types
beerman
beermanOP3mo ago
No description
beerman
beermanOP3mo ago
I'm gonna try clear my project cache This looks like an invalid one off bug sort of thing
ballingt
ballingt3mo ago
Huh! I don't know what else to check, yeah try clearing things
beerman
beermanOP3mo ago
nah
No description
beerman
beermanOP3mo ago
It happens when I import import { type PropertyStatusType } from "../../../convex/getProperties.ts"; into my +page.server.ts
import { ConvexHttpClient } from "convex/browser";
import { PUBLIC_CONVEX_URL } from "$env/static/public";
import { api } from "../../../convex/_generated/api.js";
import { redirect } from "@sveltejs/kit";
import { type PropertyStatusType } from "../../../convex/getProperties.ts";

// * @type {import('../../../../.svelte-kit/types/src/routes/properties/[status]/$types.d.ts').PageServerLoad}
export async function load(
{ params }: { params: { status: string } },
) {
const client = new ConvexHttpClient(PUBLIC_CONVEX_URL!);

if (!["for-sale", "for-rent"].includes(params.status)) {
redirect(302, "/properties");
}

const status = params.status as PropertyStatusType;

return {
status,
properties: await client.query(api.getProperties.get, {
status,
}),
};
}
import { ConvexHttpClient } from "convex/browser";
import { PUBLIC_CONVEX_URL } from "$env/static/public";
import { api } from "../../../convex/_generated/api.js";
import { redirect } from "@sveltejs/kit";
import { type PropertyStatusType } from "../../../convex/getProperties.ts";

// * @type {import('../../../../.svelte-kit/types/src/routes/properties/[status]/$types.d.ts').PageServerLoad}
export async function load(
{ params }: { params: { status: string } },
) {
const client = new ConvexHttpClient(PUBLIC_CONVEX_URL!);

if (!["for-sale", "for-rent"].includes(params.status)) {
redirect(302, "/properties");
}

const status = params.status as PropertyStatusType;

return {
status,
properties: await client.query(api.getProperties.get, {
status,
}),
};
}
What's the best way to validate this param? I wanted to do
const validate = PropertyStatusValidator.safeParse(params.status)
if (!validation.success) redirect(302, "/properties");
const status = validation.data;

return {
status,
properties: await client.query(api.getProperties.get, {
status,
}),
};
const validate = PropertyStatusValidator.safeParse(params.status)
if (!validation.success) redirect(302, "/properties");
const status = validation.data;

return {
status,
properties: await client.query(api.getProperties.get, {
status,
}),
};
` but convex validators don't have that method, so the above was the next cleanest way I could think of, but it looks like there's an incompatibility with SSR so I'm not sure how to do this anymore. I just found out about your Table helper so I'm reading through that to see if I find anything useful. Honestly, your documentation and stack.convex are all over the place.
ballingt
ballingt3mo ago
You might not need that, since you can work with schemas more directly now. Depends on what you're doing. I'm confused about the SSR part, but yeah COnvex validators don't offer a way to run them on the client. especially if you have a small repro that Infer issue is pretty interesting, would be great to file it I haven't been able to reproduce it
beerman
beermanOP3mo ago
Shall I add you as a collaborator to my repo? Dang, right as I converted most of my schema. Yeah, there's a lot of information. This is like the second or third time you've told me something has changed.
ballingt
ballingt3mo ago
sure! a minimal repro is even better, but yeah for this that'd be great. I wonder if it's Svelte-specific.
beerman
beermanOP3mo ago
You should get somebody to make sure all documentation is up to date
ballingt
ballingt3mo ago
This is the same change we talked about the other day, you can access elements of validators We should absolutley document validators better, that's on the list
beerman
beermanOP3mo ago
can you dm me your gh username
ballingt
ballingt3mo ago
thomasballinger

Did you find this page helpful?