RuleStack

Configs

Stacks

Compare

Diff

RuleStack

Configs

Stacks

Compare

Diff

Read API

RuleStack

Configs

Stacks

Compare

Diff

Read API

Configs/.cursorrules/paulpham157/paul-s-cursor-rules

.cursorrules (deprecated)

1/framework_rules/rules/convex-cursorrules-prompt-file/.cursorrules

Guidelines and best practices for building Convex projects, including database schema design, queries, mutations, and real-world examples

.cursorrules

Quality

53/100

Scores the file, not the repository.

Length

3,407 words

31 headings · 17 code blocks

Repository

28

— · pushed 478 days ago

Last changed

3 days ago

First indexed 3 days ago.
paulpham157/paul-s-cursor-rules/1/framework_rules/rules/convex-cursorrules-prompt-file/.cursorrulesRawGitHub
1---
2description: Guidelines and best practices for building Convex projects, including database schema design, queries, mutations, and real-world examples
3globs: **/*.{ts,tsx,js,jsx}
4---
5 
6# Convex guidelines
7## Function guidelines
8### New function syntax
9- ALWAYS use the new function syntax for Convex functions. For example:
10```typescript
11 import { query } from "./_generated/server";
12 import { v } from "convex/values";
13 export const f = query({
14 args: {},
15 returns: v.null(),
16 handler: async (ctx, args) => {
17 // Function body
18 },
19 });
20```
21 
22### Http endpoint syntax
23- HTTP endpoints are defined in `convex/http.ts` and require an `httpAction` decorator. For example:
24```typescript
25 import { httpRouter } from "convex/server";
26 import { httpAction } from "./_generated/server";
27 const http = httpRouter();
28 http.route({
29 path: "/echo",
30 method: "POST",
31 handler: httpAction(async (ctx, req) => {
32 const body = await req.bytes();
33 return new Response(body, { status: 200 });
34 }),
35 });
36```
37- HTTP endpoints are always registered at the exact path you specify in the `path` field. For example, if you specify `/api/someRoute`, the endpoint will be registered at `/api/someRoute`.
38 
39### Validators
40- Below is an example of an array validator:
41```typescript
42 import { mutation } from "./_generated/server";
43 import { v } from "convex/values";
44 
45 export default mutation({
46 args: {
47 simpleArray: v.array(v.union(v.string(), v.number())),
48 },
49 handler: async (ctx, args) => {
50 //...
51 },
52 });
53```
54- Below is an example of a schema with validators that codify a discriminated union type:
55```typescript
56 import { defineSchema, defineTable } from "convex/server";
57 import { v } from "convex/values";
58 
59 export default defineSchema({
60 results: defineTable(
61 v.union(
62 v.object({
63 kind: v.literal("error"),
64 errorMessage: v.string(),
65 }),
66 v.object({
67 kind: v.literal("success"),
68 value: v.number(),
69 }),
70 ),
71 )
72 });
73```
74- Always use the `v.null()` validator when returning a null value. Below is an example query that returns a null value:
75```typescript
76 import { query } from "./_generated/server";
77 import { v } from "convex/values";
78 
79 export const exampleQuery = query({
80 args: {},
81 returns: v.null(),
82 handler: async (ctx, args) => {
83 console.log("This query returns a null value");
84 return null;
85 },
86 });
87```
88- Here are the valid Convex types along with their respective validators:
89 Convex Type | TS/JS type | Example Usage | Validator for argument validation and schemas | Notes |
90| ----------- | ------------| -----------------------| -----------------------------------------------| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
91| Id | string | `doc._id` | `v.id(tableName)` | |
92| Null | null | `null` | `v.null()` | JavaScript's `undefined` is not a valid Convex value. Functions the return `undefined` or do not return will return `null` when called from a client. Use `null` instead. |
93| Int64 | bigint | `3n` | `v.int64()` | Int64s only support BigInts between -2^63 and 2^63-1. Convex supports `bigint`s in most modern browsers. |
94| Float64 | number | `3.1` | `v.number()` | Convex supports all IEEE-754 double-precision floating point numbers (such as NaNs). Inf and NaN are JSON serialized as strings. |
95| Boolean | boolean | `true` | `v.boolean()` |
96| String | string | `"abc"` | `v.string()` | Strings are stored as UTF-8 and must be valid Unicode sequences. Strings must be smaller than the 1MB total size limit when encoded as UTF-8. |
97| Bytes | ArrayBuffer | `new ArrayBuffer(8)` | `v.bytes()` | Convex supports first class bytestrings, passed in as `ArrayBuffer`s. Bytestrings must be smaller than the 1MB total size limit for Convex types. |
98| Array | Array] | `[1, 3.2, "abc"]` | `v.array(values)` | Arrays can have at most 8192 values. |
99| Object | Object | `{a: "abc"}` | `v.object({property: value})` | Convex only supports "plain old JavaScript objects" (objects that do not have a custom prototype). Objects can have at most 1024 entries. Field names must be nonempty and not start with "$" or "_". |
100| Record | Record | `{"a": "1", "b": "2"}` | `v.record(keys, values)` | Records are objects at runtime, but can have dynamic keys. Keys must be only ASCII characters, nonempty, and not start with "$" or "_". |
101 
102### Function registration
103- Use `internalQuery`, `internalMutation`, and `internalAction` to register internal functions. These functions are private and aren't part of an app's API. They can only be called by other Convex functions. These functions are always imported from `./_generated/server`.
104- Use `query`, `mutation`, and `action` to register public functions. These functions are part of the public API and are exposed to the public Internet. Do NOT use `query`, `mutation`, or `action` to register sensitive internal functions that should be kept private.
105- You CANNOT register a function through the `api` or `internal` objects.
106- ALWAYS include argument and return validators for all Convex functions. This includes all of `query`, `internalQuery`, `mutation`, `internalMutation`, `action`, and `internalAction`. If a function doesn't return anything, include `returns: v.null()` as its output validator.
107- If the JavaScript implementation of a Convex function doesn't have a return value, it implicitly returns `null`.
108 
109### Function calling
110- Use `ctx.runQuery` to call a query from a query, mutation, or action.
111- Use `ctx.runMutation` to call a mutation from a mutation or action.
112- Use `ctx.runAction` to call an action from an action.
113- ONLY call an action from another action if you need to cross runtimes (e.g. from V8 to Node). Otherwise, pull out the shared code into a helper async function and call that directly instead.
114- Try to use as few calls from actions to queries and mutations as possible. Queries and mutations are transactions, so splitting logic up into multiple calls introduces the risk of race conditions.
115- All of these calls take in a `FunctionReference`. Do NOT try to pass the callee function directly into one of these calls.
116- When using `ctx.runQuery`, `ctx.runMutation`, or `ctx.runAction` to call a function in the same file, specify a type annotation on the return value to work around TypeScript circularity limitations. For example,
117```
118 export const f = query({
119 args: { name: v.string() },
120 returns: v.string(),
121 handler: async (ctx, args) => {
122 return "Hello " + args.name;
123 },
124 });
125 
126 export const g = query({
127 args: {},
128 returns: v.null(),
129 handler: async (ctx, args) => {
130 const result: string = await ctx.runQuery(api.example.f, { name: "Bob" });
131 return null;
132 },
133 });
134```
135 
136### Function references
137- Function references are pointers to registered Convex functions.
138- Use the `api` object defined by the framework in `convex/_generated/api.ts` to call public functions registered with `query`, `mutation`, or `action`.
139- Use the `internal` object defined by the framework in `convex/_generated/api.ts` to call internal (or private) functions registered with `internalQuery`, `internalMutation`, or `internalAction`.
140- Convex uses file-based routing, so a public function defined in `convex/example.ts` named `f` has a function reference of `api.example.f`.
141- A private function defined in `convex/example.ts` named `g` has a function reference of `internal.example.g`.
142- Functions can also registered within directories nested within the `convex/` folder. For example, a public function `h` defined in `convex/messages/access.ts` has a function reference of `api.messages.access.h`.
143 
144### Api design
145- Convex uses file-based routing, so thoughtfully organize files with public query, mutation, or action functions within the `convex/` directory.
146- Use `query`, `mutation`, and `action` to define public functions.
147- Use `internalQuery`, `internalMutation`, and `internalAction` to define private, internal functions.
148 
149### Pagination
150- Paginated queries are queries that return a list of results in incremental pages.
151- You can define pagination using the following syntax:
152 
153```ts
154 import { v } from "convex/values";
155 import { query, mutation } from "./_generated/server";
156 import { paginationOptsValidator } from "convex/server";
157 export const listWithExtraArg = query({
158 args: { paginationOpts: paginationOptsValidator, author: v.string() },
159 handler: async (ctx, args) => {
160 return await ctx.db
161 .query("messages")
162 .filter((q) => q.eq(q.field("author"), args.author))
163 .order("desc")
164 .paginate(args.paginationOpts);
165 },
166 });
167```
168 Note: `paginationOpts` is an object with the following properties:
169 - `numItems`: the maximum number of documents to return (the validator is `v.number()`)
170 - `cursor`: the cursor to use to fetch the next page of documents (the validator is `v.union(v.string(), v.null())`)
171- A query that ends in `.paginate()` returns an object that has the following properties:
172 - page (contains an array of documents that you fetches)
173 - isDone (a boolean that represents whether or not this is the last page of documents)
174 - continueCursor (a string that represents the cursor to use to fetch the next page of documents)
175 
176 
177## Validator guidelines
178- `v.bigint()` is deprecated for representing signed 64-bit integers. Use `v.int64()` instead.
179- Use `v.record()` for defining a record type. `v.map()` and `v.set()` are not supported.
180 
181## Schema guidelines
182- Always define your schema in `convex/schema.ts`.
183- Always import the schema definition functions from `convex/server`:
184- System fields are automatically added to all documents and are prefixed with an underscore. The two system fields that are automatically added to all documents are `_creationTime` which has the validator `v.number()` and `_id` which has the validator `v.id(tableName)`.
185- Always include all index fields in the index name. For example, if an index is defined as `["field1", "field2"]`, the index name should be "by_field1_and_field2".
186- Index fields must be queried in the same order they are defined. If you want to be able to query by "field1" then "field2" and by "field2" then "field1", you must create separate indexes.
187 
188## Typescript guidelines
189- You can use the helper typescript type `Id` imported from './_generated/dataModel' to get the type of the id for a given table. For example if there is a table called 'users' you can use `Id<'users'>` to get the type of the id for that table.
190- If you need to define a `Record` make sure that you correctly provide the type of the key and value in the type. For example a validator `v.record(v.id('users'), v.string())` would have the type `Record<Id<'users'>, string>`. Below is an example of using `Record` with an `Id` type in a query:
191```ts
192 import { query } from "./_generated/server";
193 import { Doc, Id } from "./_generated/dataModel";
194 
195 export const exampleQuery = query({
196 args: { userIds: v.array(v.id("users")) },
197 returns: v.record(v.id("users"), v.string()),
198 handler: async (ctx, args) => {
199 const idToUsername: Record<Id<"users">, string> = {};
200 for (const userId of args.userIds) {
201 const user = await ctx.db.get(userId);
202 if (user) {
203 users[user._id] = user.username;
204 }
205 }
206 
207 return idToUsername;
208 },
209 });
210```
211- Be strict with types, particularly around id's of documents. For example, if a function takes in an id for a document in the 'users' table, take in `Id<'users'>` rather than `string`.
212- Always use `as const` for string literals in discriminated union types.
213- When using the `Array` type, make sure to always define your arrays as `const array: Array<T> = [...];`
214- When using the `Record` type, make sure to always define your records as `const record: Record<KeyType, ValueType> = {...};`
215- Always add `@types/node` to your `package.json` when using any Node.js built-in modules.
216 
217## Full text search guidelines
218- A query for "10 messages in channel '#general' that best match the query 'hello hi' in their body" would look like:
219 
220const messages = await ctx.db
221 .query("messages")
222 .withSearchIndex("search_body", (q) =>
223 q.search("body", "hello hi").eq("channel", "#general"),
224 )
225 .take(10);
226 
227## Query guidelines
228- Do NOT use `filter` in queries. Instead, define an index in the schema and use `withIndex` instead.
229- Convex queries do NOT support `.delete()`. Instead, `.collect()` the results, iterate over them, and call `ctx.db.delete(row._id)` on each result.
230- Use `.unique()` to get a single document from a query. This method will throw an error if there are multiple documents that match the query.
231- When using async iteration, don't use `.collect()` or `.take(n)` on the result of a query. Instead, use the `for await (const row of query)` syntax.
232### Ordering
233- By default Convex always returns documents in ascending `_creationTime` order.
234- You can use `.order('asc')` or `.order('desc')` to pick whether a query is in ascending or descending order. If the order isn't specified, it defaults to ascending.
235- Document queries that use indexes will be ordered based on the columns in the index and can avoid slow table scans.
236 
237 
238## Mutation guidelines
239- Use `ctx.db.replace` to fully replace an existing document. This method will throw an error if the document does not exist.
240- Use `ctx.db.patch` to shallow merge updates into an existing document. This method will throw an error if the document does not exist.
241 
242## Action guidelines
243- Always add `"use node";` to the top of files containing actions that use Node.js built-in modules.
244- Never use `ctx.db` inside of an action. Actions don't have access to the database.
245- Below is an example of the syntax for an action:
246```ts
247 import { action } from "./_generated/server";
248 
249 export const exampleAction = action({
250 args: {},
251 returns: v.null(),
252 handler: async (ctx, args) => {
253 console.log("This action does not return anything");
254 return null;
255 },
256 });
257```
258 
259## Scheduling guidelines
260### Cron guidelines
261- Only use the `crons.interval` or `crons.cron` methods to schedule cron jobs. Do NOT use the `crons.hourly`, `crons.daily`, or `crons.weekly` helpers.
262- Both cron methods take in a FunctionReference. Do NOT try to pass the function directly into one of these methods.
263- Define crons by declaring the top-level `crons` object, calling some methods on it, and then exporting it as default. For example,
264```ts
265 import { cronJobs } from "convex/server";
266 import { internal } from "./_generated/api";
267 import { internalAction } from "./_generated/server";
268 
269 const empty = internalAction({
270 args: {},
271 returns: v.null(),
272 handler: async (ctx, args) => {
273 console.log("empty");
274 },
275 });
276 
277 const crons = cronJobs();
278 
279 // Run `internal.crons.empty` every two hours.
280 crons.interval("delete inactive users", { hours: 2 }, internal.crons.empty, {});
281 
282 export default crons;
283```
284- You can register Convex functions within `crons.ts` just like any other file.
285- If a cron calls an internal function, always import the `internal` object from '_generated/api`, even if the internal function is registered in the same file.
286 
287 
288## File storage guidelines
289- Convex includes file storage for large files like images, videos, and PDFs.
290- The `ctx.storage.getUrl()` method returns a signed URL for a given file. It returns `null` if the file doesn't exist.
291- Do NOT use the deprecated `ctx.storage.getMetadata` call for loading a file's metadata.
292 
293 Instead, query the `_storage` system table. For example, you can use `ctx.db.system.get` to get an `Id<"_storage">`.
294```
295 import { query } from "./_generated/server";
296 import { Id } from "./_generated/dataModel";
297 
298 type FileMetadata = {
299 _id: Id<"_storage">;
300 _creationTime: number;
301 contentType?: string;
302 sha256: string;
303 size: number;
304 }
305 
306 export const exampleQuery = query({
307 args: { fileId: v.id("_storage") },
308 returns: v.null();
309 handler: async (ctx, args) => {
310 const metadata: FileMetadata | null = await ctx.db.system.get(args.fileId);
311 console.log(metadata);
312 return null;
313 },
314 });
315```
316- Convex storage stores items as `Blob` objects. You must convert all items to/from a `Blob` when using Convex storage.
317 
318 
319# Examples:
320## Example: chat-app
321 
322### Task
323```
324Create a real-time chat application backend with AI responses. The app should:
325- Allow creating users with names
326- Support multiple chat channels
327- Enable users to send messages to channels
328- Automatically generate AI responses to user messages
329- Show recent message history
330 
331The backend should provide APIs for:
3321. User management (creation)
3332. Channel management (creation)
3343. Message operations (sending, listing)
3354. AI response generation using OpenAI's GPT-4
336 
337Messages should be stored with their channel, author, and content. The system should maintain message order
338and limit history display to the 10 most recent messages per channel.
339 
340```
341 
342### Analysis
3431. Task Requirements Summary:
344- Build a real-time chat backend with AI integration
345- Support user creation
346- Enable channel-based conversations
347- Store and retrieve messages with proper ordering
348- Generate AI responses automatically
349 
3502. Main Components Needed:
351- Database tables: users, channels, messages
352- Public APIs for user/channel management
353- Message handling functions
354- Internal AI response generation system
355- Context loading for AI responses
356 
3573. Public API and Internal Functions Design:
358Public Mutations:
359- createUser:
360 - file path: convex/index.ts
361 - arguments: {name: v.string()}
362 - returns: v.object({userId: v.id("users")})
363 - purpose: Create a new user with a given name
364- createChannel:
365 - file path: convex/index.ts
366 - arguments: {name: v.string()}
367 - returns: v.object({channelId: v.id("channels")})
368 - purpose: Create a new channel with a given name
369- sendMessage:
370 - file path: convex/index.ts
371 - arguments: {channelId: v.id("channels"), authorId: v.id("users"), content: v.string()}
372 - returns: v.null()
373 - purpose: Send a message to a channel and schedule a response from the AI
374 
375Public Queries:
376- listMessages:
377 - file path: convex/index.ts
378 - arguments: {channelId: v.id("channels")}
379 - returns: v.array(v.object({
380 _id: v.id("messages"),
381 _creationTime: v.number(),
382 channelId: v.id("channels"),
383 authorId: v.optional(v.id("users")),
384 content: v.string(),
385 }))
386 - purpose: List the 10 most recent messages from a channel in descending creation order
387 
388Internal Functions:
389- generateResponse:
390 - file path: convex/index.ts
391 - arguments: {channelId: v.id("channels")}
392 - returns: v.null()
393 - purpose: Generate a response from the AI for a given channel
394- loadContext:
395 - file path: convex/index.ts
396 - arguments: {channelId: v.id("channels")}
397 - returns: v.array(v.object({
398 _id: v.id("messages"),
399 _creationTime: v.number(),
400 channelId: v.id("channels"),
401 authorId: v.optional(v.id("users")),
402 content: v.string(),
403 }))
404- writeAgentResponse:
405 - file path: convex/index.ts
406 - arguments: {channelId: v.id("channels"), content: v.string()}
407 - returns: v.null()
408 - purpose: Write an AI response to a given channel
409 
4104. Schema Design:
411- users
412 - validator: { name: v.string() }
413 - indexes: <none>
414- channels
415 - validator: { name: v.string() }
416 - indexes: <none>
417- messages
418 - validator: { channelId: v.id("channels"), authorId: v.optional(v.id("users")), content: v.string() }
419 - indexes
420 - by_channel: ["channelId"]
421 
4225. Background Processing:
423- AI response generation runs asynchronously after each user message
424- Uses OpenAI's GPT-4 to generate contextual responses
425- Maintains conversation context using recent message history
426 
427 
428### Implementation
429 
430#### package.json
431```typescript
432{
433 "name": "chat-app",
434 "description": "This example shows how to build a chat app without authentication.",
435 "version": "1.0.0",
436 "dependencies": {
437 "convex": "^1.17.4",
438 "openai": "^4.79.0"
439 },
440 "devDependencies": {
441 "typescript": "^5.7.3"
442 }
443}
444```
445 
446#### tsconfig.json
447```typescript
448{
449 "compilerOptions": {
450 "target": "ESNext",
451 "lib": ["DOM", "DOM.Iterable", "ESNext"],
452 "skipLibCheck": true,
453 "allowSyntheticDefaultImports": true,
454 "strict": true,
455 "forceConsistentCasingInFileNames": true,
456 "module": "ESNext",
457 "moduleResolution": "Bundler",
458 "resolveJsonModule": true,
459 "isolatedModules": true,
460 "allowImportingTsExtensions": true,
461 "noEmit": true,
462 "jsx": "react-jsx"
463 },
464 "exclude": ["convex"],
465 "include": ["**/src/**/*.tsx", "**/src/**/*.ts", "vite.config.ts"]
466}
467```
468 
469#### convex/index.ts
470```typescript
471import {
472 query,
473 mutation,
474 internalQuery,
475 internalMutation,
476 internalAction,
477} from "./_generated/server";
478import { v } from "convex/values";
479import OpenAI from "openai";
480import { internal } from "./_generated/api";
481 
482/**
483 * Create a user with a given name.
484 */
485export const createUser = mutation({
486 args: {
487 name: v.string(),
488 },
489 returns: v.id("users"),
490 handler: async (ctx, args) => {
491 return await ctx.db.insert("users", { name: args.name });
492 },
493});
494 
495/**
496 * Create a channel with a given name.
497 */
498export const createChannel = mutation({
499 args: {
500 name: v.string(),
501 },
502 returns: v.id("channels"),
503 handler: async (ctx, args) => {
504 return await ctx.db.insert("channels", { name: args.name });
505 },
506});
507 
508/**
509 * List the 10 most recent messages from a channel in descending creation order.
510 */
511export const listMessages = query({
512 args: {
513 channelId: v.id("channels"),
514 },
515 returns: v.array(
516 v.object({
517 _id: v.id("messages"),
518 _creationTime: v.number(),
519 channelId: v.id("channels"),
520 authorId: v.optional(v.id("users")),
521 content: v.string(),
522 }),
523 ),
524 handler: async (ctx, args) => {
525 const messages = await ctx.db
526 .query("messages")
527 .withIndex("by_channel", (q) => q.eq("channelId", args.channelId))
528 .order("desc")
529 .take(10);
530 return messages;
531 },
532});
533 
534/**
535 * Send a message to a channel and schedule a response from the AI.
536 */
537export const sendMessage = mutation({
538 args: {
539 channelId: v.id("channels"),
540 authorId: v.id("users"),
541 content: v.string(),
542 },
543 returns: v.null(),
544 handler: async (ctx, args) => {
545 const channel = await ctx.db.get(args.channelId);
546 if (!channel) {
547 throw new Error("Channel not found");
548 }
549 const user = await ctx.db.get(args.authorId);
550 if (!user) {
551 throw new Error("User not found");
552 }
553 await ctx.db.insert("messages", {
554 channelId: args.channelId,
555 authorId: args.authorId,
556 content: args.content,
557 });
558 await ctx.scheduler.runAfter(0, internal.index.generateResponse, {
559 channelId: args.channelId,
560 });
561 return null;
562 },
563});
564 
565const openai = new OpenAI();
566 
567export const generateResponse = internalAction({
568 args: {
569 channelId: v.id("channels"),
570 },
571 returns: v.null(),
572 handler: async (ctx, args) => {
573 const context = await ctx.runQuery(internal.index.loadContext, {
574 channelId: args.channelId,
575 });
576 const response = await openai.chat.completions.create({
577 model: "gpt-4o",
578 messages: context,
579 });
580 const content = response.choices[0].message.content;
581 if (!content) {
582 throw new Error("No content in response");
583 }
584 await ctx.runMutation(internal.index.writeAgentResponse, {
585 channelId: args.channelId,
586 content,
587 });
588 return null;
589 },
590});
591 
592export const loadContext = internalQuery({
593 args: {
594 channelId: v.id("channels"),
595 },
596 returns: v.array(
597 v.object({
598 role: v.union(v.literal("user"), v.literal("assistant")),
599 content: v.string(),
600 }),
601 ),
602 handler: async (ctx, args) => {
603 const channel = await ctx.db.get(args.channelId);
604 if (!channel) {
605 throw new Error("Channel not found");
606 }
607 const messages = await ctx.db
608 .query("messages")
609 .withIndex("by_channel", (q) => q.eq("channelId", args.channelId))
610 .order("desc")
611 .take(10);
612 
613 const result = [];
614 for (const message of messages) {
615 if (message.authorId) {
616 const user = await ctx.db.get(message.authorId);
617 if (!user) {
618 throw new Error("User not found");
619 }
620 result.push({
621 role: "user" as const,
622 content: `${user.name}: ${message.content}`,
623 });
624 } else {
625 result.push({ role: "assistant" as const, content: message.content });
626 }
627 }
628 return result;
629 },
630});
631 
632export const writeAgentResponse = internalMutation({
633 args: {
634 channelId: v.id("channels"),
635 content: v.string(),
636 },
637 returns: v.null(),
638 handler: async (ctx, args) => {
639 await ctx.db.insert("messages", {
640 channelId: args.channelId,
641 content: args.content,
642 });
643 return null;
644 },
645});
646```
647 
648#### convex/schema.ts
649```typescript
650import { defineSchema, defineTable } from "convex/server";
651import { v } from "convex/values";
652 
653export default defineSchema({
654 channels: defineTable({
655 name: v.string(),
656 }),
657 
658 users: defineTable({
659 name: v.string(),
660 }),
661 
662 messages: defineTable({
663 channelId: v.id("channels"),
664 authorId: v.optional(v.id("users")),
665 content: v.string(),
666 }).index("by_channel", ["channelId"]),
667});
668```
669 
670#### src/App.tsx
671```typescript
672export default function App() {
673 return <div>Hello World</div>;
674}
675```
676 
677 

Sections

  • Convex guidelines
  • Function guidelines
  • New function syntax
  • Http endpoint syntax
  • Validators
  • Function registration
  • Function calling
  • Function references
  • Api design
  • Pagination
  • Validator guidelines
  • Schema guidelines
  • Typescript guidelines
  • Full text search guidelines
  • Query guidelines
  • Ordering
  • Mutation guidelines
  • Action guidelines
  • Scheduling guidelines
  • Cron guidelines
  • File storage guidelines
  • Examples:
  • Example: chat-app
  • Task
  • Analysis
  • Implementation

What it covers

code-styletypesdatabaseapido-not

Stack — with the evidence

github-actions

(0.60)

Glob targeting

  • **/*.{ts
  • tsx
  • js
  • jsx}

Format

.cursorrules

Cursor's original single-file format, superseded by .cursor/rules/*.mdc. Tracked here precisely because it is dead: how much of the ecosystem is still shipping a deprecated file is a measurable answer, and a large share of the "best cursor rules" pages on the web still teach this format.

What the corpus says about it

Repository

Owner
paulpham157
Language
—
License
—
Archived
no

All configs in this repo

Also in paulpham157/paul-s-cursor-rules

Diff this repo’s formats

One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?

The other instruction files in this repository
RepositoryFormatStackCoversScoreChanged
paulpham157/paul-s-cursor-rules1/framework_rules/rules/react-styled-components-cursorrules-prompt-file/.cursorrules · 28.cursorrulesgithub-actionsstyle42/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/react-typescript-nextjs-nodejs-cursorrules-prompt-/.cursorrules · 28.cursorrulesgithub-actionsstyle44/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/.cursorrules · 28.cursorrulesgithub-actionsdocs34/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/android-jetpack-compose-cursorrules-prompt-file/.cursorrules · 28.cursorrulesgithub-actionstesting-strategy38/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/angular-novo-elements-cursorrules-prompt-file/.cursorrules · 28.cursorrulesgithub-actionstestlint-formatstylearch+370/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/angular-typescript-cursorrules-prompt-file/.cursorrules · 28.cursorrulesgithub-actionsteststyledocs38/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/ascii-simulation-game-cursorrules-prompt-file/.cursorrules · 28.cursorrulesgithub-actionsno sections34/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/aspnet-abp-cursorrules-prompt-file/.cursorrules · 28.cursorrulesgithub-actionstestlint-formatstylearch+570/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/astro-typescript-cursorrules-prompt-file/.cursorrules · 28.cursorrulesgithub-actionsgit30/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/chrome-extension-dev-js-typescript-cursorrules-pro/.cursorrules · 28.cursorrulesgithub-actionsstyle38/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/cpp-programming-guidelines-cursorrules-prompt-file/.cursorrules · 28.cursorrulesgithub-actionsteststylearchperformance68/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/cursor-ai-react-typescript-shadcn-ui-cursorrules-p/.cursorrules · 28.cursorrulesgithub-actionsno sections16/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/cursorrules-cursor-ai-nextjs-14-tailwind-seo-setup/.cursorrules · 28.cursorrulesgithub-actionsstyletypesuido-not65/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/cursorrules-cursor-ai-wordpress-draft-macos-prompt/.cursorrules · 28.cursorrulesgithub-actionsno sections16/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/cursorrules-file-cursor-ai-python-fastapi-api/.cursorrules · 28.cursorrulesgithub-actionsstyletypes38/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/deno-integration-techniques-cursorrules-prompt-fil/.cursorrules · 28.cursorrulesgithub-actionsno sections16/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/dragonruby-best-practices-cursorrules-prompt-file/.cursorrules · 28.cursorrulesgithub-actionsstyle34/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/drupal-11-cursorrules-prompt-file/.cursorrules · 28.cursorrulesgithub-actionsstyle46/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/elixir-engineer-guidelines-cursorrules-prompt-file/.cursorrules · 28.cursorrulesgithub-actionsno sections16/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/elixir-phoenix-docker-setup-cursorrules-prompt-fil/.cursorrules · 28.cursorrulesgithub-actionsgit30/1003 days ago
Diff against 1/framework_rules/rules/react-styled-components-cursorrules-prompt-file/.cursorrules Diff against 1/framework_rules/rules/react-typescript-nextjs-nodejs-cursorrules-prompt-/.cursorrules Diff against 1/framework_rules/.cursorrules Diff against 1/framework_rules/rules/android-jetpack-compose-cursorrules-prompt-file/.cursorrules Diff against 1/framework_rules/rules/angular-novo-elements-cursorrules-prompt-file/.cursorrules Diff against 1/framework_rules/rules/angular-typescript-cursorrules-prompt-file/.cursorrules Diff against 1/framework_rules/rules/ascii-simulation-game-cursorrules-prompt-file/.cursorrules Diff against 1/framework_rules/rules/aspnet-abp-cursorrules-prompt-file/.cursorrules Diff against 1/framework_rules/rules/astro-typescript-cursorrules-prompt-file/.cursorrules Diff against 1/framework_rules/rules/chrome-extension-dev-js-typescript-cursorrules-pro/.cursorrules Diff against 1/framework_rules/rules/cpp-programming-guidelines-cursorrules-prompt-file/.cursorrules Diff against 1/framework_rules/rules/cursor-ai-react-typescript-shadcn-ui-cursorrules-p/.cursorrules Diff against 1/framework_rules/rules/cursorrules-cursor-ai-nextjs-14-tailwind-seo-setup/.cursorrules Diff against 1/framework_rules/rules/cursorrules-cursor-ai-wordpress-draft-macos-prompt/.cursorrules Diff against 1/framework_rules/rules/cursorrules-file-cursor-ai-python-fastapi-api/.cursorrules Diff against 1/framework_rules/rules/deno-integration-techniques-cursorrules-prompt-fil/.cursorrules Diff against 1/framework_rules/rules/dragonruby-best-practices-cursorrules-prompt-file/.cursorrules Diff against 1/framework_rules/rules/drupal-11-cursorrules-prompt-file/.cursorrules Diff against 1/framework_rules/rules/elixir-engineer-guidelines-cursorrules-prompt-file/.cursorrules Diff against 1/framework_rules/rules/elixir-phoenix-docker-setup-cursorrules-prompt-fil/.cursorrules

Similar configs

Same format, overlapping stack, ranked by quality.

Same format, overlapping stack, ranked by quality
RepositoryFormatStackCoversScoreChanged
SkeneTechnologies/skene-cookbook.cursorrules · 51.cursorrulespythoneslint+3setuptestlint-formatstyle+1196/1002 days ago
fall-out-bug/sdp_lab.cursorrules · 0.cursorrulesgodocker+3setupbuildtestlint-format+386/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/python-llm-ml-workflow-cursorrules-prompt-file/.cursorrules · 28.cursorrulesgithub-actionstestlint-formatstyletypes+480/1003 days ago
storybookjs/storybook.cursorrules · 91k.cursorrulestypescriptjavascript+6teststylearchdo-not+178/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/flutter-riverpod-cursorrules-prompt-file/.cursorrules · 28.cursorrulesgithub-actionsstylearchdo-notagent-behaviour71/1003 days ago
forem/forem.cursorrules · 23k.cursorrulesrubyrails+9teststyletypesdatabase+471/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/angular-novo-elements-cursorrules-prompt-file/.cursorrules · 28.cursorrulesgithub-actionstestlint-formatstylearch+370/1003 days ago
paulpham157/paul-s-cursor-rules1/framework_rules/rules/nextjs-supabase-shadcn-pwa-cursorrules-prompt-file/.cursorrules · 28.cursorrulesgithub-actionsbuildstylearchdo-not70/1003 days ago
RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack