Skip to content

Commit fb1a0f2

Browse files
committed
instructions tweak
1 parent 97de113 commit fb1a0f2

File tree

1 file changed

+165
-0
lines changed

1 file changed

+165
-0
lines changed

instructions/convex_instructions.md

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,171 @@ Write helper functions in your convex/ directory and use them within your Convex
128128

129129
Helper functions allow sharing code while still executing the entire query or mutation in a single transaction. For actions, sharing code via helper functions instead of using ctx.runAction reduces function calls and resource usage.
130130

131+
### Bad Example
132+
133+
**** This example overuses `ctx.runQuery` and `ctx.runMutation`, which is discussed more in the [Avoid sequential `ctx.runMutation` / `ctx.runQuery` from actions](https://docs.convex.dev/understanding/best-practices/#avoid-sequential-ctx-runmutation-ctx-runquery-from-actions) section.
134+
135+
**convex/users.ts**
136+
```ts
137+
export const getCurrentUser = query({
138+
args: {},
139+
handler: async (ctx) => {
140+
const userIdentity = await ctx.auth.getUserIdentity();
141+
if (userIdentity === null) {
142+
throw new Error("Unauthorized");
143+
}
144+
const user = /* query ctx.db to load the user */
145+
const userSettings = /* load other documents related to the user */
146+
return { user, settings: userSettings };
147+
},
148+
});
149+
```
150+
151+
**convex/conversations.ts**
152+
```ts
153+
export const listMessages = query({
154+
args: {
155+
conversationId: v.id("conversations"),
156+
},
157+
handler: async (ctx, { conversationId }) => {
158+
// ❌ runQuery
159+
const user = await ctx.runQuery(api.users.getCurrentUser);
160+
const conversation = await ctx.db.get(conversationId);
161+
if (conversation === null || !conversation.members.includes(user._id)) {
162+
throw new Error("Unauthorized");
163+
}
164+
const messages = /* query ctx.db to load the messages */
165+
return messages;
166+
},
167+
});
168+
169+
export const summarizeConversation = action({
170+
args: {
171+
conversationId: v.id("conversations"),
172+
},
173+
handler: async (ctx, { conversationId }) => {
174+
// ❌ runQuery
175+
const messages = await ctx.runQuery(api.conversations.listMessages, {
176+
conversationId,
177+
});
178+
const summary = /* call some external service to summarize the conversation */
179+
await ctx.runMutation(api.conversations.addSummary, {
180+
conversationId,
181+
summary,
182+
});
183+
},
184+
});
185+
```
186+
187+
### Good Example
188+
189+
**** Most of the code here is now in the `convex/model` directory. The API for this application is in `convex/conversations.ts`, which contains very little code itself.
190+
191+
**convex/model/users.ts**
192+
```ts
193+
import { QueryCtx } from '../_generated/server';
194+
195+
export async function getCurrentUser(ctx: QueryCtx) {
196+
const userIdentity = await ctx.auth.getUserIdentity();
197+
if (userIdentity === null) {
198+
throw new Error("Unauthorized");
199+
}
200+
const user = /* query ctx.db to load the user */
201+
const userSettings = /* load other documents related to the user */
202+
return { user, settings: userSettings };
203+
}
204+
```
205+
206+
**convex/model/conversations.ts**
207+
```ts
208+
import { QueryCtx, MutationCtx } from '../_generated/server';
209+
import * as Users from './users';
210+
211+
export async function ensureHasAccess(
212+
ctx: QueryCtx,
213+
{ conversationId }: { conversationId: Id<"conversations"> },
214+
) {
215+
const user = await Users.getCurrentUser(ctx);
216+
const conversation = await ctx.db.get(conversationId);
217+
if (conversation === null || !conversation.members.includes(user._id)) {
218+
throw new Error("Unauthorized");
219+
}
220+
return conversation;
221+
}
222+
223+
export async function listMessages(
224+
ctx: QueryCtx,
225+
{ conversationId }: { conversationId: Id<"conversations"> },
226+
) {
227+
await ensureHasAccess(ctx, { conversationId });
228+
const messages = /* query ctx.db to load the messages */
229+
return messages;
230+
}
231+
232+
export async function addSummary(
233+
ctx: MutationCtx,
234+
{
235+
conversationId,
236+
summary,
237+
}: { conversationId: Id<"conversations">; summary: string },
238+
) {
239+
await ensureHasAccess(ctx, { conversationId });
240+
await ctx.db.patch(conversationId, { summary });
241+
}
242+
243+
export async function generateSummary(
244+
messages: Doc<"messages">[],
245+
conversationId: Id<"conversations">,
246+
) {
247+
const summary = /* call some external service to summarize the conversation */
248+
return summary;
249+
}
250+
```
251+
252+
**convex/conversations.ts**
253+
```ts
254+
import * as Conversations from './model/conversations';
255+
256+
export const addSummary = internalMutation({
257+
args: {
258+
conversationId: v.id("conversations"),
259+
summary: v.string(),
260+
},
261+
handler: async (ctx, { conversationId, summary }) => {
262+
await Conversations.addSummary(ctx, { conversationId, summary });
263+
},
264+
});
265+
266+
export const listMessages = internalQuery({
267+
args: {
268+
conversationId: v.id("conversations"),
269+
},
270+
handler: async (ctx, { conversationId }) => {
271+
return Conversations.listMessages(ctx, { conversationId });
272+
},
273+
});
274+
275+
export const summarizeConversation = action({
276+
args: {
277+
conversationId: v.id("conversations"),
278+
},
279+
handler: async (ctx, { conversationId }) => {
280+
const messages = await ctx.runQuery(internal.conversations.listMessages, {
281+
conversationId,
282+
});
283+
const summary = await Conversations.generateSummary(
284+
messages,
285+
conversationId,
286+
);
287+
await ctx.runMutation(internal.conversations.addSummary, {
288+
conversationId,
289+
summary,
290+
});
291+
},
292+
});
293+
```
294+
295+
131296
## Prefer queries and mutations over actions
132297
You should generally avoid using actions when the same goal can be achieved using queries or mutations. Since actions can have side effects, they can't be automatically retried nor their results cached. Actions should be used in more limited scenarios, such as calling third-party services.
133298

0 commit comments

Comments
 (0)