2.9 KiB
Convex + Svelte Best Practices
This document outlines the mandatory rules and best practices for integrating Convex with Svelte in this project.
1. Imports
Always use the following import paths. Do NOT use $lib/convex or relative paths for generated files unless specifically required by a local override.
Correct Imports:
import { useQuery, useConvexClient } from 'convex-svelte';
import { api } from '@sgse-app/backend/convex/_generated/api';
import type { Id, Doc } from '@sgse-app/backend/convex/_generated/dataModel';
Incorrect Imports (Avoid):
import { convex } from '$lib/convex'; // Avoid direct client usage for queries
import { api } from '$lib/convex/_generated/api'; // Incorrect path
import { api } from '../convex/_generated/api'; // Relative path
2. Data Fetching
Use useQuery for Reactivity
Instead of manually fetching data inside onMount, use the useQuery hook. This ensures your data is reactive and automatically updates when the backend data changes.
Preferred Pattern:
<script lang="ts">
import { useQuery } from 'convex-svelte';
import { api } from '@sgse-app/backend/convex/_generated/api';
const tasksQuery = useQuery(api.tasks.list, { status: 'pending' });
const tasks = $derived(tasksQuery.data || []);
const isLoading = $derived(tasksQuery.isLoading);
</script>
Avoid Pattern:
<script lang="ts">
import { onMount } from 'svelte';
import { convex } from '$lib/convex';
let tasks = [];
onMount(async () => {
// This is not reactive!
tasks = await convex.query(api.tasks.list, { status: 'pending' });
});
</script>
Mutations
Use useConvexClient to access the client for mutations.
<script lang="ts">
import { useConvexClient } from 'convex-svelte';
import { api } from '@sgse-app/backend/convex/_generated/api';
const client = useConvexClient();
async function completeTask(id) {
await client.mutation(api.tasks.complete, { id });
}
</script>
3. Type Safety
No any
Strictly avoid using any. The Convex generated data model provides precise types for all your tables.
Use Generated Types
Use Doc<"tableName"> for full document objects and Id<"tableName"> for IDs.
Correct:
import type { Doc, Id } from '@sgse-app/backend/convex/_generated/dataModel';
let selectedTask: Doc<'tasks'> | null = $state(null);
let taskId: Id<'tasks'>;
Incorrect:
let selectedTask: any = $state(null);
let taskId: string;
Union Types for Enums
When dealing with status fields or other enums, define the specific union type instead of casting to any.
Correct:
async function updateStatus(newStatus: 'pending' | 'completed' | 'archived') {
// ...
}
Incorrect:
async function updateStatus(newStatus: string) {
// ...
status: newStatus as any; // Avoid this
}