Skip to content

Commit 7b1fa31

Browse files
authored
Merge pull request #2881 from evidence-dev/docs/using-queries-in-custom-components
feat: add documentation for component queries
2 parents d194078 + f50bda6 commit 7b1fa31

File tree

2 files changed

+165
-0
lines changed

2 files changed

+165
-0
lines changed
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# Component Queries
2+
3+
Component queries allow you to run SQL queries in your component code.
4+
5+
Component queries transform how we build data visualizations. Instead of passing data down through props from parent pages, components become self-contained units that can request exactly what they need. This independence makes components more reusable and easier to maintain, as all the logic for both fetching and displaying data lives in one place.
6+
7+
## Static Queries
8+
9+
Static queries are "static" because the SQL string they run cannot change throughout the component's lifecycle. They are defined once when your component is created, and are executed when `QueryLoad` is mounted.
10+
11+
Here's how to create a component that fetches and displays information about tables in your database:
12+
13+
```html title="components/TableList.svelte"
14+
<script>
15+
import { buildQuery } from '@evidence-dev/component-utilities/buildQuery';
16+
import { QueryLoad } from '@evidence-dev/core-components';
17+
18+
const query = buildQuery(
19+
'SELECT * FROM information_schema.tables',
20+
);
21+
</script>
22+
23+
<QueryLoad data={query} let:loaded={tables}>
24+
<svelte:fragment slot="skeleton" />
25+
26+
<ul>
27+
{#each tables as table}
28+
<li>{table.table_name}</li>
29+
{/each}
30+
</ul>
31+
</QueryLoad>
32+
```
33+
34+
### The Query Loader
35+
36+
The `QueryLoad` component manages the entire lifecycle of your query execution. It handles:
37+
- Executing your query against DuckDB
38+
- Managing loading states
39+
- Handling any errors that occur
40+
- Delivering results to your component
41+
42+
```html
43+
<QueryLoad data={query} let:loaded={tableData}>
44+
<svelte:fragment slot="skeleton" />
45+
<!-- Your component content here -->
46+
</QueryLoad>
47+
```
48+
49+
The `let:loaded` directive creates a new variable containing your query results. Using a descriptive name (like `tableData` or `salesMetrics`) makes your code more maintainable than the generic `loaded`.
50+
51+
## Dynamic Queries
52+
53+
For queries that change based on user input or component state, you need dynamic queries. This allows you to create interactive components.
54+
55+
Here's an example that lets users control how many rows to display:
56+
57+
```html title="components/DynamicTableList.svelte"
58+
&lt;script>
59+
import { QueryLoad } from '@evidence-dev/core-components';
60+
import { getQueryFunction } from '@evidence-dev/component-utilities/buildQuery';
61+
import { Query } from '@evidence-dev/sdk/usql';
62+
63+
// This will hold our current query result
64+
let query;
65+
66+
// Create a reactive query function
67+
const queryFunction = Query.createReactive({
68+
execFn: getQueryFunction(),
69+
callback: v => query = v
70+
});
71+
72+
// These values will control our query
73+
let limit = 10;
74+
let schemaName = 'public';
75+
76+
// This reactive statement runs whenever limit or schemaName change
77+
$: queryFunction(`
78+
SELECT *
79+
FROM information_schema.tables
80+
WHERE table_schema = '${schemaName}'
81+
LIMIT ${limit}
82+
`);
83+
</script>
84+
85+
<div>
86+
<label>
87+
Rows to show:
88+
<input type="number" bind:value={limit} min={0} />
89+
</label>
90+
<label>
91+
Schema:
92+
<input type="text" bind:value={schemaName} />
93+
</label>
94+
</div>
95+
96+
<QueryLoad data={query} let:loaded={tables}>
97+
<svelte:fragment slot="skeleton" />
98+
<ul>
99+
{#each tables as table}
100+
<li>{table.table_name}</li>
101+
{/each}
102+
</ul>
103+
</QueryLoad>
104+
```
105+
106+
Let's understand how this works:
107+
108+
### Query State Management
109+
110+
1. First, we create a variable to hold our query result:
111+
```javascript
112+
let query;
113+
```
114+
This will be updated every time our query executes with new parameters.
115+
116+
2. Next, we create a reactive query function:
117+
```javascript
118+
const queryFunction = Query.createReactive({
119+
execFn: getQueryFunction(),
120+
callback: v => query = v
121+
});
122+
```
123+
This sets up an environment that can execute queries and update our component's state.
124+
125+
3. Finally, we use Svelte's reactive declarations to run our query:
126+
```javascript
127+
$: queryFunction(`SELECT * FROM ... LIMIT ${limit}`);
128+
```
129+
The `$:` syntax tells Svelte to re-run this statement whenever `limit` changes, creating a connection between your component's state and your query.
130+
131+
## Error Handling
132+
133+
When working with queries, things can sometimes go wrong. Maybe a query is malformed, or perhaps it's trying to access a table that doesn't exist. The `QueryLoad` component helps you handle these situations gracefully through its error slot:
134+
135+
```html
136+
<QueryLoad data={query} let:loaded={tables}>
137+
<svelte:fragment slot="skeleton" />
138+
139+
<svelte:fragment slot="error" let:error>
140+
<div class="text-red-600">
141+
<h3 class="font-bold">Unable to load data</h3>
142+
<p>{error.message}</p>
143+
<p class="text-sm mt-2">
144+
Please check your query and try again.
145+
</p>
146+
</div>
147+
</svelte:fragment>
148+
149+
<ul>
150+
{#each tables as table}
151+
<li>{table.table_name}</li>
152+
{/each}
153+
</ul>
154+
</QueryLoad>
155+
```
156+
157+
When a query fails, Evidence:
158+
1. Captures the error information
159+
2. Prevents the main content from rendering
160+
3. Makes the error details available through `let:error`
161+
4. Displays your error handling content

sites/docs/pages/components/custom-components/index.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,7 @@ import formatTitle from '@evidence-dev/component-utilities/formatTitle';
216216
```
217217
- `column`: name of column to be formatted
218218
- `columnFormat`: a format object for column being formatted (can be obtained from the `getColumnSummary` function)
219+
220+
## Adding Queries
221+
222+
Custom components can execute queries rather than requiring data to be passed to them. See [component queries](./component-queries) for details

0 commit comments

Comments
 (0)