Skip to content

Commit fdbec4b

Browse files
authored
Merge pull request #102 from teableio/feat/app-builder-best-practices
docs: add App Builder best practices guide (EN + ZH)
2 parents 7a37691 + 1cea6f5 commit fdbec4b

3 files changed

Lines changed: 500 additions & 2 deletions

File tree

docs.json

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,13 @@
5353
"pages": [
5454
"en/basic/ai/overview",
5555
"en/basic/ai/ai-chat",
56-
"en/basic/ai/app-builder",
56+
{
57+
"group": "App Builder",
58+
"pages": [
59+
"en/basic/ai/app-builder",
60+
"en/basic/ai/app-builder-practical-guide"
61+
]
62+
},
5763
"en/basic/ai/custom-model"
5864
]
5965
},
@@ -396,7 +402,13 @@
396402
"pages": [
397403
"zh/basic/ai/overview",
398404
"zh/basic/ai/ai-chat",
399-
"zh/basic/ai/app-builder",
405+
{
406+
"group": "应用构建器",
407+
"pages": [
408+
"zh/basic/ai/app-builder",
409+
"zh/basic/ai/app-builder-practical-guide"
410+
]
411+
},
400412
"zh/basic/ai/custom-model"
401413
]
402414
},
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
---
2+
title: "App Builder Best Practices"
3+
description: "Practical tips and patterns for getting the most out of Teable App Builder."
4+
---
5+
6+
## Six Core Principles
7+
8+
### 1. Define your data model in Teable first
9+
10+
Teable App Builder works on top of your existing Teable tables — **your tables and fields are your schema**, and the AI reads them directly when generating UI and logic.
11+
12+
So before you start building, define your data model clearly. The more precise your field types, links, and read/write paths are, the higher the quality of what the AI produces.
13+
14+
### 2. Plan before you build
15+
16+
Once your data model is in place, still resist the urge to jump straight into building the UI. Run a planning pass with the AI first.
17+
18+
Say something like: *"Let's not write code yet — let's make a plan."* Describe the problem you're solving, the target users, and the rough feature set. Let the AI draft a structured proposal. Review it, adjust it, and only start building once you agree the plan makes sense.
19+
20+
<Tip>A few extra minutes aligning on direction up front saves hours of rework later.</Tip>
21+
22+
### 3. Start small and layer up
23+
24+
Don't try to cram every feature into a single prompt. Describe the core functionality first, get a minimal working version running, then add one thing at a time: one interaction, one style tweak, one piece of logic.
25+
26+
Verify each change before moving on. When something breaks, you only need to roll back one small change instead of starting over.
27+
28+
### 4. Be specific, not abstract
29+
30+
Descriptions like *"make it prettier"* or *"make the interactions feel more natural"* carry almost no information for the AI.
31+
32+
Effective prompts are concrete: **which page, which area, what behavior you want, what you don't want**. Attaching screenshots or reference UIs helps a lot.
33+
34+
<Tip>Treat your prompt like a brief for a smart person who knows nothing about your project. The more precise the instructions, the closer the output gets to what you imagined.</Tip>
35+
36+
### 5. Diagnose first, fix second
37+
38+
When the app behaves unexpectedly, resist the urge to tell the AI to *"just fix it."* Vague fix instructions push the AI into blindly patching things, often introducing new bugs along the way.
39+
40+
A better approach is two steps:
41+
42+
<Steps>
43+
<Step title="Ask the AI to analyze first">
44+
Describe the symptoms and ask the AI to list likely causes and possible approaches — without touching the code yet.
45+
</Step>
46+
<Step title="Pick a direction, then implement">
47+
Decide which explanation is most plausible, and tell the AI to proceed along that path.
48+
</Step>
49+
</Steps>
50+
51+
<Warning>If several fix attempts in a row fail, roll back to the last known-good version and start fresh. It's usually faster than patching on top of patches.</Warning>
52+
53+
### 6. Lean on version rollbacks
54+
55+
Every conversation with the AI produces changes. The recommended rhythm: finish one feature module, confirm it works, then move on. **Don't juggle multiple unfinished features at once.**
56+
57+
Later change broke something? Roll back to the last stable version and try again with a clearer prompt.
58+
59+
## FAQ
60+
61+
### What should I do about 429 (Too Many Requests) errors?
62+
63+
<Warning>
64+
The Teable API is limited to **10 QPS** (10 requests per second). Apps generated by App Builder can hit 429 errors during normal use if request handling isn't optimized.
65+
</Warning>
66+
67+
There are three broad strategies to address this:
68+
69+
<CardGroup cols={3}>
70+
<Card title="Caching" icon="database">
71+
Reduce duplicate requests
72+
</Card>
73+
<Card title="Pagination & batching" icon="layer-group">
74+
Shrink per-request payloads
75+
</Card>
76+
<Card title="Debounce & throttle" icon="gauge-high">
77+
Lower request frequency
78+
</Card>
79+
</CardGroup>
80+
81+
Each section below lists common scenarios, the fix, and a reference prompt you can reuse.
82+
83+
#### Strategy 1: Caching (reduce duplicate requests)
84+
85+
<AccordionGroup>
86+
<Accordion title="Too many requests fired on page load" icon="bolt">
87+
**Scenario**: A dashboard page has multiple charts, stat cards, and lists. Each component independently queries a different table, so the moment the page opens, concurrent requests exceed the limit.
88+
89+
**Fix**: Cache data in app memory after loading (1–3 minute TTL is a good start), and serve cached data on subsequent views. Lazy-load below-the-fold components to stagger request timing.
90+
91+
<Tip>
92+
**Reference prompt**: "Cache data locally after the page loads, with a 1-minute TTL. Don't re-request the API within the TTL window. Delay loading for non-critical components by 500 ms."
93+
</Tip>
94+
</Accordion>
95+
96+
<Accordion title="Multiple components querying the same table" icon="copy">
97+
**Scenario**: Three components on the same page each need data from the same table and each fires its own request — when one would have been enough.
98+
99+
**Fix**: Centralize data fetching so the same dataset is loaded once and shared across components.
100+
101+
<Tip>
102+
**Reference prompt**: "If multiple components need data from the same table, fetch it once and share it across all of them. Do not fire duplicate requests."
103+
</Tip>
104+
</Accordion>
105+
106+
<Accordion title="Re-fetching on page navigation" icon="arrows-rotate">
107+
**Scenario**: Users move back and forth between pages. Each return triggers a fresh fetch, even when nothing has changed.
108+
109+
**Fix**: Within the cache TTL, reuse the previously loaded data instead of re-requesting.
110+
111+
<Tip>
112+
**Reference prompt**: "When a user returns to a page, if it's been less than 1 minute since the last load, use the cached data. Do not re-request the API."
113+
</Tip>
114+
</Accordion>
115+
116+
<Accordion title="Dropdowns loading huge option lists" icon="list">
117+
**Scenario**: A dropdown shows every record from a table as an option. With many records, that single request alone is heavy.
118+
119+
**Fix**: Convert it into a search-style picker — only fetch matching records after the user types. Alternatively, cache the option list.
120+
121+
<Tip>
122+
**Reference prompt**: "Dropdowns should not load all options upfront. Switch to a keyword search that fetches matching records on input, with debounce applied."
123+
</Tip>
124+
</Accordion>
125+
126+
<Accordion title="Cascading selectors chaining requests" icon="sitemap">
127+
**Scenario**: Choosing one field triggers a load for the next level's options. Multi-level cascades rack up several requests per interaction.
128+
129+
**Fix**: Preload the related data once and filter locally, or cache cascade data after the first load.
130+
131+
<Tip>
132+
**Reference prompt**: "Cache cascading selector option data locally after loading. When the user changes a parent option, filter from the cache instead of re-requesting."
133+
</Tip>
134+
</Accordion>
135+
136+
<Accordion title="Re-renders triggering duplicate requests" icon="repeat">
137+
**Scenario**: Poor state management causes components to re-fetch data on every render.
138+
139+
**Fix**: Trigger data fetching on specific events (initial mount, explicit user action), not on every render. Use caching as a safety net.
140+
141+
<Tip>
142+
**Reference prompt**: "Only fetch data on first page load or explicit user actions. Do not re-fetch on re-renders — use cached data instead."
143+
</Tip>
144+
</Accordion>
145+
</AccordionGroup>
146+
147+
#### Strategy 2: Pagination & batching (shrink per-request payloads)
148+
149+
<AccordionGroup>
150+
<Accordion title="Lists or tables without pagination" icon="table-list">
151+
**Scenario**: Loading all records at once produces a flood of API calls when the dataset grows.
152+
153+
**Fix**: Paginate. Only fetch the current page's data.
154+
155+
<Tip>
156+
**Reference prompt**: "Show 20 rows per page. Only load the next page when the user navigates to it. Do not load everything at once."
157+
</Tip>
158+
</Accordion>
159+
160+
<Accordion title="Per-row fetching for linked data (N+1)" icon="link">
161+
**Scenario**: After loading a list, you fetch linked-table details for each record one at a time. Loading 50 projects and then 50 owner lookups = 50 extra requests in an instant.
162+
163+
**Fix**: Fetch all linked data in one batch, not row by row.
164+
165+
<Tip>
166+
**Reference prompt**: "When loading a list, batch-fetch all linked data in a single request. Do not loop through records to fetch their linked information individually."
167+
</Tip>
168+
</Accordion>
169+
170+
<Accordion title="Per-row writes during bulk updates" icon="pen-to-square">
171+
**Scenario**: Bulk-updating multiple records by sending one update request per record instead of a single batch request.
172+
173+
**Fix**: Use the batch update API to submit all changes in one call.
174+
175+
<Tip>
176+
**Reference prompt**: "For bulk operations, merge multiple record changes into a single batch request. Do not send one update per record."
177+
</Tip>
178+
</Accordion>
179+
180+
<Accordion title="API calls inside loops" icon="rotate">
181+
**Scenario**: A `for` loop processes records one at a time, calling the API each iteration.
182+
183+
**Fix**: Collect all the IDs first, then issue a single batch request.
184+
185+
<Tip>
186+
**Reference prompt**: "Do not call the API inside a loop. Collect all required IDs first, then issue a single batch request."
187+
</Tip>
188+
</Accordion>
189+
</AccordionGroup>
190+
191+
#### Strategy 3: Debounce & throttle (lower request frequency)
192+
193+
<AccordionGroup>
194+
<Accordion title="Search or filters without debounce" icon="magnifying-glass">
195+
**Scenario**: Every keystroke in a search box fires a request. Typing a 4-character query produces 4 requests.
196+
197+
**Fix**: Debounce the input — wait 300–500 ms after the user stops typing before firing the request.
198+
199+
<Tip>
200+
**Reference prompt**: "Debounce the search input. Only send a request 300 ms after the user stops typing. Do not send requests mid-typing."
201+
</Tip>
202+
</Accordion>
203+
204+
<Accordion title="Rapid repeated user actions" icon="hand-pointer">
205+
**Scenario**: Rapid-fire submit clicks, quick filter toggling, fast pagination — each action fires a request immediately.
206+
207+
**Fix**: Apply debounce or throttle. Disable submit buttons until the request completes to prevent double-submits.
208+
209+
<Tip>
210+
**Reference prompt**: "Disable the submit button after click and re-enable it once the request resolves. Debounce filter changes so rapid changes within 300 ms fire only one request."
211+
</Tip>
212+
</Accordion>
213+
214+
<Accordion title="Overly aggressive form auto-save" icon="floppy-disk">
215+
**Scenario**: Every field change saves immediately. Filling out a form can trigger a dozen writes.
216+
217+
**Fix**: Switch to explicit save on button click, or debounce auto-save so it fires once after a pause in editing.
218+
219+
<Tip>
220+
**Reference prompt**: "Do not save on every field change. Save on explicit button click, or auto-save once after the user has paused editing for 2 seconds."
221+
</Tip>
222+
</Accordion>
223+
224+
<Accordion title="Polling intervals that are too short" icon="clock">
225+
**Scenario**: Data refreshes every few seconds, producing sustained high-frequency traffic.
226+
227+
**Fix**: Lengthen the poll interval to something reasonable (30 seconds or more), or switch to manual refresh.
228+
229+
<Tip>
230+
**Reference prompt**: "Set the auto-refresh interval to 60 seconds. Add a manual refresh button so users can pull the latest data on demand."
231+
</Tip>
232+
</Accordion>
233+
234+
<Accordion title="Multiple components polling independently" icon="timer">
235+
**Scenario**: Several components on a page each set up their own poll timer. The combined load easily exceeds the limit.
236+
237+
**Fix**: Centralize polling. Run one periodic fetch, then fan the result out to every component that needs it.
238+
239+
<Tip>
240+
**Reference prompt**: "Don't let each component set up its own poll timer. Use a single refresh mechanism that fetches everything on a schedule and distributes the data to the components."
241+
</Tip>
242+
</Accordion>
243+
</AccordionGroup>

0 commit comments

Comments
 (0)