sveltelean Query
Version v0.2.0

Example

Type a name, pick a team, page through the result.

Members table

The example runs in the browser.

Requests the server received

  1. None yet.
Code
<!-- src/routes/members/+page.svelte -->
<script lang="ts">
	import { createQuery } from '@svelte-lean/query';
	import { createPaged, tablePaging } from '@svelte-lean/query/paged';
	import { DataTable, defaultState, type Column } from '@svelte-lean/table';
	import '@svelte-lean/table/style.css';
	import { listMembers, listTeams } from '$api/members';
	import type { Member } from '$api/types';

	const columns: Column<Member>[] = [
		{ id: 'name', header: 'Name' },
		{ id: 'email', header: 'Email' },
		{ id: 'team', header: 'Team' },
		{ id: 'status', header: 'Status' }
	];

	let view = $state(defaultState({ pageSize: 5 }));
	let filters = $state({ search: '', team: '' });

	const teams = createQuery(listTeams);
	// The page lives in the table state; a filter change sends page 1.
	const list = createPaged(
		listMembers,
		() => ({ search: filters.search || undefined, team: filters.team || undefined }),
		tablePaging(() => view)
	);
</script>

<form role="search" aria-label="Filter members" onsubmit={(event) => event.preventDefault()}>
	<label>Search <input type="search" bind:value={filters.search} /></label>
	<label>
		Team
		<select bind:value={filters.team}>
			<option value="">All teams</option>
			{#each teams.data ?? [] as team (team.id)}
				<option value={team.name}>{team.name} ({team.members})</option>
			{/each}
		</select>
	</label>
</form>

<DataTable bind:state={view} {...list.table} {columns} getRowId={(m) => m.id} label="Members" />
The component shown, against an in-memory server with twenty-four members that answers after a fixed delay.
src/lib/api/members.ts
export const listMembers = defineQuery({
	get: 'api/members',
	params: type<MemberFilters>(),
	result: page<Member>(),
	entity: 'Member', // every row carries tag('Member', row.id)
	tags: ['Members'],
	fresh: '15s',
	access: 'members.view'
});

How it works

MomentRequestTable
First renderGET api/members?page=1&perPage=5 and GET api/teamsskeleton rows (loading)
Next pagepage=2, cause paramspage 1 stays until page 2 answers
A keystroke in the search fieldpage=1 with the new search; the unanswered request is abortedprevious rows stay
A page past the end after a filterclamped to the last pagethe last page
Back to a page seen beforenone while fresh (fresh: '15s' on the definition)rows at once
  • {...list.table} passes manual, data, rowCount, pageCount, loading, fetching and refreshing; each prop follows its own field.
  • tablePaging(() => view) keeps the page and the page size in the table state, so the table's pager drives the query.
  • The page params come from the transport profile (page, perPage without one); the endpoint's params type holds the filters only.

Variations

Another pager

TypeScript
// Without a DataTable: the list owns its page; render any pager.
const list = createPaged(listMembers, () => filters, { perPage: 25 });
// list.rows, list.page, list.lastPage, list.setPage(n)

Polling and the next page

TypeScript
// Poll the shown page every 30 s while the tab is visible; prefetch the next page.
const polled = createPaged(listMembers, () => filters, {
	poll: () => '30s',
	prefetch: 'next'
});