Files
sgse-app/.agent/rules/convex-svelte-guidelines.md

7.6 KiB
Raw Blame History

trigger: glob globs: **/.svelte, **/.ts, **/*.svelte.ts

Convex + Svelte Guidelines

Overview

These guidelines describe how to write Convex backend code and consume it from a Svelte (SvelteKit) frontend. The syntax for Convex functions stays exactly the same, but the way you import and call them from the client differs from a React/Next.js project. Below you will find the adapted sections from the original Convex style guide with Sveltespecific notes.


1. Function Syntax (Backend)

No change keep the new Convex function syntax.

import {
	query,
	mutation,
	action,
	internalQuery,
	internalMutation,
	internalAction
} from './_generated/server';
import { v } from 'convex/values';

export const getUser = query({
	args: { userId: v.id('users') },
	returns: v.object({ name: v.string(), email: v.string() }),
	handler: async (ctx, args) => {
		const user = await ctx.db.get(args.userId);
		if (!user) throw new Error('User not found');
		return { name: user.name, email: user.email };
	}
});

2. HTTP Endpoints (Backend)

No change keep the same convex/http.ts file.

import { httpRouter } from 'convex/server';
import { httpAction } from './_generated/server';

const http = httpRouter();

http.route({
	path: '/api/echo',
	method: 'POST',
	handler: httpAction(async (ctx, req) => {
		const body = await req.bytes();
		return new Response(body, { status: 200 });
	})
});

3. Validators (Backend)

No change keep the same validators (v.string(), v.id(), etc.).


4. Function Registration (Backend)

No change use query, mutation, action for public functions and internal* for private ones.


5. Function Calling from Svelte

5.1 Install the Convex client

npm i convex @convex-dev/convex-svelte

The @convex-dev/convex-svelte package provides a thin wrapper that works with Svelte stores.

5.2 Initialise the client (e.g. in src/lib/convex.ts)

import { createConvexClient } from '@convex-dev/convex-svelte';

export const convex = createConvexClient({
	url: import.meta.env.VITE_CONVEX_URL // set in .env
});

5.3 Using queries in a component

<script lang="ts">
	import { convex } from '$lib/convex';
	import { onMount } from 'svelte';
	import { api } from '../convex/_generated/api';

	let user: { name: string; email: string } | null = null;
	let loading = true;
	let error: string | null = null;

	onMount(async () => {
		try {
			user = await convex.query(api.users.getUser, { userId: 'some-id' });
		} catch (e) {
			error = (e as Error).message;
		} finally {
			loading = false;
		}
	});
</script>

{#if loading}
	<p>Loading…</p>
{:else if error}
	<p class="error">{error}</p>
{:else if user}
	<h2>{user.name}</h2>
	<p>{user.email}</p>
{/if}

5.4 Using mutations in a component

<script lang="ts">
	import { convex } from '$lib/convex';
	import { api } from '../convex/_generated/api';
	let name = '';
	let creating = false;
	let error: string | null = null;

	async function createUser() {
		creating = true;
		error = null;
		try {
			const userId = await convex.mutation(api.users.createUser, { name });
			console.log('Created user', userId);
		} catch (e) {
			error = (e as Error).message;
		} finally {
			creating = false;
		}
	}
</script>

<input bind:value={name} placeholder="Name" />
<button on:click={createUser} disabled={creating}>Create</button>
{#if error}<p class="error">{error}</p>{/if}

5.5 Using actions (Nodeonly) from Svelte

Actions run in a Node environment, so they cannot be called directly from the browser. Use a mutation that internally calls the action, or expose a HTTP endpoint that triggers the action.


6. Scheduler / Cron (Backend)

Same as original guide define crons.ts and export the default crons object.


7. File Storage (Backend)

Same as original guide use ctx.storage.getUrl() and query _storage for metadata.


8. TypeScript Helpers (Backend)

Keep using Id<'table'> from ./_generated/dataModel.


9. SvelteSpecific Tips

Topic Recommendation
Storebased data If you need reactive data across many components, wrap convex.query in a Svelte store (readable, writable).
Error handling Use try / catch around every client call; surface the error in the UI.
SSR / SvelteKit Calls made in load functions run on the server; you can use convex.query there without worrying about the browser environment.
Environment variables Prefix with VITE_ for clientside access (import.meta.env.VITE_CONVEX_URL).
Testing Use the Convex mock client (createMockConvexClient) provided by @convex-dev/convex-svelte for unit tests.

10. Full Example (SvelteKit + Convex)

10.1 Backend (convex/users.ts)

import { mutation, query } from './_generated/server';
import { v } from 'convex/values';

export const createUser = mutation({
	args: { name: v.string() },
	returns: v.id('users'),
	handler: async (ctx, args) => {
		return await ctx.db.insert('users', { name: args.name });
	}
});

export const getUser = query({
	args: { userId: v.id('users') },
	returns: v.object({ name: v.string() }),
	handler: async (ctx, args) => {
		const user = await ctx.db.get(args.userId);
		if (!user) throw new Error('Not found');
		return { name: user.name };
	}
});

10.2 Frontend (src/routes/+page.svelte)

<script lang="ts">
	import { convex } from '$lib/convex';
	import { api } from '$lib/convex/_generated/api';
	import { onMount } from 'svelte';

	let name = '';
	let createdId: string | null = null;
	let loading = false;
	let error: string | null = null;

	async function create() {
		loading = true;
		error = null;
		try {
			createdId = await convex.mutation(api.users.createUser, { name });
		} catch (e) {
			error = (e as Error).message;
		} finally {
			loading = false;
		}
	}
</script>

<input bind:value={name} placeholder="Your name" />
<button on:click={create} disabled={loading}>Create user</button>
{#if createdId}<p>Created user id: {createdId}</p>{/if}
{#if error}<p class="error">{error}</p>{/if}

11. Checklist for New Files

  • All Convex functions use the new syntax (query({ … })).
  • Every public function has argument and return validators.
  • Svelte components import the generated api object from convex/_generated/api.
  • All client calls use the convex instance from $lib/convex.
  • Environment variable VITE_CONVEX_URL is defined in .env.
  • Errors are caught and displayed in the UI.
  • Types are imported from convex/_generated/dataModel when needed.

12. References


Keep these guidelines alongside the existing svelte-rules.md so that contributors have a single source of truth for both frontend and backend conventions.