Skip to content

Conversation

jog1t
Copy link
Collaborator

@jog1t jog1t commented Aug 14, 2025

Closes FRONT-764

TL;DR

Added a new SQLite integration example for RivetKit.

What changed?

Created a new example project demonstrating SQLite integration with RivetKit. The example includes:

  • A simple chat application with message storage in SQLite
  • Implementation of basic database operations (inserting and retrieving messages)
  • Integration with RivetKit's actor system
  • Complete project structure with TypeScript configuration

The example shows how to:

  • Define a SQLite schema using Drizzle ORM
  • Set up database migrations
  • Create actor actions for sending and retrieving messages
  • Broadcast events to connected clients

How to test?

  1. Clone the repository:
git clone https://github.com/rivet-gg/rivetkit
  1. Navigate to the example directory:
cd rivetkit/examples/sqlite
  1. Install dependencies:
pnpm install
  1. Start the development server:
pnpm run dev
  1. Open your browser to https://studio.rivet.gg/ to see the RivetKit server in action.

Why make this change?

This example provides developers with a practical demonstration of how to integrate SQLite with RivetKit for persistent data storage. It serves as a reference implementation for projects that need simple, file-based database functionality without requiring external database services.

Copy link

claude bot commented Aug 14, 2025

Claude finished @jog1t's task —— View job


SQLite Example Review

I've completed a comprehensive review of the SQLite example PR. Here's my feedback:

Todo List:

  • Read CLAUDE.md for project conventions
  • Analyze all new SQLite example files
  • Compare with existing examples for consistency
  • Check code quality and best practices
  • Review security considerations
  • Check performance implications
  • Provide comprehensive feedback

Issues Found

🚨 Critical Issues

  1. Package naming inconsistency (package.json:2)

    • Package name is example-sqlite but CLAUDE.md requires example-{name} format
    • Should be example-sqlite ✅ (this is actually correct)
  2. Unused schema file (src/db/schema.ts:1-8)

    • The Drizzle schema is defined but never used in the registry
    • Creates confusion about whether this example uses raw SQL or Drizzle ORM

⚠️ Major Issues

  1. README inconsistency (README.md:20)

    • Uses pnpm install in installation instructions but pnpm run dev in development
    • Other examples use npm install consistently
  2. Missing dev script flag (package.json:7)

    • Uses tsx --watch but drizzle example uses tsx --loader @rivetkit/sql-loader --watch
    • May need SQL loader for proper database functionality

🔍 Minor Issues

  1. SQL injection prevention (src/registry.ts:24-27)

    • Good: Uses parameterized queries with placeholders
    • The prepared statement approach is secure ✅
  2. Missing test coverage

    • No test files included (most other examples have tests)
    • Should include basic tests for sendMessage and getHistory actions

Positive Aspects

  • Good security practices: Uses parameterized queries to prevent SQL injection
  • Proper structure: Follows example conventions with correct folder structure
  • Clear implementation: Simple, easy-to-understand SQLite integration
  • Consistent API: Same chat functionality as drizzle example for comparison

Recommendations

  1. Clarify the example's purpose:

    • Remove unused src/db/schema.ts file since this example uses raw SQL, not Drizzle
    • Or update registry to use the Drizzle schema consistently
  2. Add basic tests: Include test file similar to other examples

  3. Fix README: Use consistent npm commands throughout

  4. Consider performance: Add database indexes for frequently queried columns (timestamp)

Security Assessment ✅

The code is secure:

  • Uses parameterized queries (SQL injection protection)
  • No sensitive data exposure
  • Proper input validation through TypeScript types

Performance Considerations

  • Good: Uses LIMIT 100 for history queries
  • Suggestion: Consider adding ORDER BY index on timestamp column for better performance

Comparison with Drizzle Example

This example provides a good contrast to the Drizzle example by showing raw SQL usage, but the unused schema file creates confusion about the intended approach.


Copy link
Collaborator Author

jog1t commented Aug 14, 2025

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge queue - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@jog1t jog1t requested a review from NathanFlurry August 14, 2025 20:36
@jog1t jog1t self-assigned this Aug 14, 2025
Copy link

linear bot commented Aug 14, 2025

@jog1t jog1t marked this pull request as ready for review August 14, 2025 20:36
Copy link

pkg-pr-new bot commented Aug 14, 2025

More templates

@rivetkit/actor

pnpm add https://pkg.pr.new/rivet-gg/rivetkit/@rivetkit/actor@1176

@rivetkit/core

pnpm add https://pkg.pr.new/rivet-gg/rivetkit/@rivetkit/core@1176

@rivetkit/db

pnpm add https://pkg.pr.new/rivet-gg/rivetkit/@rivetkit/db@1176

rivetkit

pnpm add https://pkg.pr.new/rivet-gg/rivetkit@1176

@rivetkit/cloudflare-workers

pnpm add https://pkg.pr.new/rivet-gg/rivetkit/@rivetkit/cloudflare-workers@1176

@rivetkit/redis

pnpm add https://pkg.pr.new/rivet-gg/rivetkit/@rivetkit/redis@1176

@rivetkit/framework-base

pnpm add https://pkg.pr.new/rivet-gg/rivetkit/@rivetkit/framework-base@1176

@rivetkit/next-js

pnpm add https://pkg.pr.new/rivet-gg/rivetkit/@rivetkit/next-js@1176

@rivetkit/react

pnpm add https://pkg.pr.new/rivet-gg/rivetkit/@rivetkit/react@1176

commit: 1d5a5d8

@jog1t jog1t force-pushed the 08-14-feat_examples_add_sqlite_example branch from d242475 to 1d5a5d8 Compare August 14, 2025 20:39
Copy link

claude bot commented Aug 14, 2025

Claude finished @jog1t's task —— View job


SQLite Example PR Review

Todo List:

  • Read CLAUDE.md for project conventions
  • Analyze all new SQLite example files
  • Compare with existing examples for consistency
  • Check code quality and best practices
  • Review security considerations
  • Check performance implications
  • Address table name mismatch issue
  • Provide comprehensive feedback

Issues Found

🚨 Critical Issues

  1. Package name inconsistency (package.json:2)

    • Package name should follow example-{name} format per CLAUDE.md
    • Current: example-sqlite ✅ (actually correct)
  2. Missing schema file

    • SQLite example has no src/db/schema.ts file, unlike Drizzle example
    • This creates confusion since there's no clear schema definition for raw SQL approach

⚠️ Major Issues

  1. README installation command inconsistency (README.md:20)

    • Uses pnpm install but CLAUDE.md examples section specifies npm install should be used consistently
    • Other examples like drizzle also use pnpm install, so this might be acceptable
  2. Missing dev script SQL loader (package.json:7)

    • Uses tsx --watch but drizzle example uses tsx --loader @rivetkit/sql-loader --watch
    • This is correct for raw SQL approach - no SQL loader needed
  3. Table name mismatch mentioned in review comment

    • Review comment incorrectly identifies mismatch between schema and migration
    • No actual issue: This example uses raw SQL without Drizzle schema, so table name is correctly messages

🔍 Minor Issues

  1. Missing test coverage

    • No test files included (most other examples have tests in tests/ folder)
    • Should include basic tests for sendMessage and getHistory actions
  2. No explicit index on timestamp column (registry.ts:9-15)

    • For better query performance on ORDER BY timestamp DESC, consider adding an index

Positive Aspects

  • Good security practices: Uses parameterized queries with placeholders to prevent SQL injection (registry.ts:24-27)
  • Proper structure: Follows example conventions with correct folder structure and turbo.json
  • Clear implementation: Simple, easy-to-understand raw SQLite integration
  • Consistent API: Same chat functionality as drizzle example, making comparison easy
  • Proper error handling: Follows RivetKit patterns

Recommendations

  1. Clarify example purpose:

    • Consider adding a comment in registry.ts explaining this demonstrates raw SQL approach vs. the Drizzle ORM example
  2. Add basic tests: Include test file similar to other examples like tests/sqlite.test.ts

  3. Consider performance optimization: Add database index for timestamp column:

    CREATE INDEX IF NOT EXISTS idx_messages_timestamp ON messages(timestamp DESC)
  4. Package naming: The current naming example-sqlite follows conventions correctly

Security Assessment ✅

The code is secure:

  • ✅ Uses parameterized queries (SQL injection protection)
  • ✅ No sensitive data exposure
  • ✅ Proper input validation through TypeScript types
  • ✅ No unsafe string concatenation in SQL queries

Performance Considerations

  • ✅ Uses LIMIT 100 for history queries to prevent excessive data retrieval
  • ⚠️ Missing index on timestamp column could affect query performance at scale
  • ✅ Simple INSERT statements are efficient

Comparison with Drizzle Example

Aspect SQLite (Raw SQL) Drizzle (ORM)
Dependencies Fewer (no ORM deps) More (Drizzle deps)
Learning curve Requires SQL knowledge Type-safe ORM approach
Performance Direct SQL execution ORM overhead
Type safety Manual typing Generated types
Schema management Manual migration Automated migrations

This example provides a good contrast showing developers how to use raw SQL with RivetKit.

Final Assessment

This is a solid example that demonstrates raw SQLite integration. The main improvement would be adding basic tests and potentially a performance index. The review comment about table name mismatch is incorrect - there's no mismatch in this raw SQL approach.

Recommendation: APPROVE with suggested improvements for tests and performance optimization.


@jog1t jog1t force-pushed the 08-14-feat_examples_add_drizzle_example branch from bc35a94 to 1ad7f70 Compare August 18, 2025 23:13
@jog1t jog1t force-pushed the 08-14-feat_examples_add_sqlite_example branch from 1d5a5d8 to 2d4ef89 Compare August 18, 2025 23:13
Copy link

claude bot commented Aug 18, 2025

Claude finished @jog1t's task —— View job


Claude finished reviewing the SQLite example PR

SQLite Example Code Review

Todo List:

  • Read CLAUDE.md for project conventions
  • Analyze all new SQLite example files
  • Compare with existing examples for consistency
  • Check code quality and best practices
  • Review security considerations
  • Check performance implications
  • Address the table name mismatch review comment
  • Provide comprehensive feedback

🔍 Review Summary

This SQLite example provides a clean, straightforward demonstration of raw SQLite integration with RivetKit. The implementation is secure and follows most project conventions, with a few minor issues to address.

📋 Issues Found

⚠️ Minor Issues

  1. README installation inconsistency (README.md:20)

    • Uses pnpm install but CLAUDE.md examples section suggests npm install for consistency
    • However, the drizzle example also uses pnpm, so this may be acceptable
  2. Missing test coverage

    • No test files included while most other examples have basic tests
    • Should include tests/sqlite.test.ts with basic functionality tests
  3. No performance optimization (registry.ts:36)

    • Consider adding an index on timestamp column for better query performance:
    CREATE INDEX IF NOT EXISTS idx_messages_timestamp ON messages(timestamp DESC)

Review Comment Resolution

Table name mismatch claim is INCORRECT: The review comment suggests there's a mismatch between schema and migration, but this example uses raw SQL without Drizzle ORM. There is no schema file - the table name messages is correctly and consistently used in both the migration (registry.ts:9) and queries (registry.ts:25,36).

🎯 Positive Aspects

  • ✅ Security: Uses parameterized queries to prevent SQL injection (registry.ts:24-27)
  • ✅ Structure: Follows example conventions with correct folder structure
  • ✅ Consistency: Same chat API as drizzle example for easy comparison
  • ✅ Simplicity: Clear demonstration of raw SQLite approach vs ORM
  • ✅ Dependencies: Correctly uses workspace:* and follows package naming (example-sqlite)
  • ✅ Configuration: Proper turbo.json, tsconfig.json setup

🔒 Security Assessment

The code is secure:

  • Uses parameterized queries with placeholders (?) preventing SQL injection
  • No sensitive data exposure in logs or responses
  • Proper input validation through TypeScript types
  • No unsafe string concatenation in SQL statements

Performance Considerations

  • Good: Uses LIMIT 100 to prevent excessive data retrieval
  • Suggestion: Add database index on timestamp column for better sorting performance
  • Good: Simple INSERT and SELECT operations are efficient

🆚 Comparison with Drizzle Example

Aspect SQLite (Raw SQL) Drizzle (ORM)
Dependencies Minimal More (Drizzle deps)
Learning curve Requires SQL knowledge Type-safe ORM
Dev script tsx --watch tsx --loader @rivetkit/sql-loader --watch
Database setup Manual migration Schema + migrations
Type safety Manual typing Generated types

📝 Recommendations

  1. Add basic tests: Include test file similar to examples/counter/tests/counter.test.ts
  2. Consider performance: Add timestamp index if expecting high message volume
  3. Optional: Add comment explaining this demonstrates raw SQL vs Drizzle approach

🏁 Final Assessment

APPROVED ✅ - This is a solid example that effectively demonstrates raw SQLite integration with RivetKit. The main improvement would be adding basic tests. The review comment about table name mismatch is incorrect and can be ignored.

The example provides valuable contrast to the Drizzle example and follows RivetKit conventions well.


Copy link

claude bot commented Aug 18, 2025

Claude finished @jog1t's task —— View job


SQLite Example PR Review

Todo List:

  • Read CLAUDE.md for project conventions
  • Analyze all new SQLite example files
  • Compare with existing examples for consistency
  • Check code quality and best practices
  • Review security considerations
  • Check performance implications
  • Provide comprehensive feedback

Overall Assessment

This SQLite example provides a clean demonstration of raw SQL integration with RivetKit. The implementation is secure, follows most project conventions, and offers a good contrast to the Drizzle ORM example.

Issues Found

🔍 Minor Issues

  1. README installation command inconsistency (README.md:20)

    • Uses pnpm install but CLAUDE.md specifies examples should use npm install consistently
    • Other examples like counter, hono use npm install in their READMEs
    • Fix: Change to npm install for consistency
  2. Missing test coverage

    • No tests/ directory or test files
    • Most other examples include comprehensive tests (e.g., examples/database/tests/database.test.ts)
    • Recommendation: Add basic tests for sendMessage and getHistory actions
  3. Performance consideration (registry.ts:36)

    • Query lacks explicit index on timestamp column for ORDER BY timestamp DESC
    • At scale, this could impact performance
    • Suggestion: Consider adding an index in the migration
  4. README development command (README.md:25)

    • Uses pnpm run dev but should be npm run dev for consistency with installation

What's Working Well

Security - Excellent (registry.ts:24-27)

  • ✅ Uses parameterized queries with placeholders (?, ?, ?)
  • ✅ No string concatenation or SQL injection vulnerabilities
  • ✅ Proper input validation through TypeScript types

Code Quality - Good

  • ✅ Follows CLAUDE.md conventions for example structure
  • ✅ Correct package naming: example-sqlite
  • ✅ Proper turbo.json configuration
  • ✅ TypeScript configuration follows patterns
  • ✅ Uses workspace:* dependencies correctly

Performance - Adequate

  • ✅ Uses LIMIT 100 to prevent excessive data retrieval
  • ✅ Simple, efficient INSERT statements
  • ✅ Direct SQL execution without ORM overhead

Architecture - Clean

  • ✅ Clear separation of concerns
  • ✅ Follows RivetKit actor patterns correctly
  • ✅ Proper use of c.broadcast() for events
  • ✅ Simple migration strategy

Comparison with Drizzle Example

Aspect SQLite (Raw SQL) Drizzle (ORM)
Dependencies Minimal - no ORM Drizzle, drizzle-kit
Type Safety Manual via TS Generated from schema
Learning Curve Requires SQL knowledge Higher-level abstractions
Performance Direct SQL execution Small ORM overhead
Migration Manual SQL Automated with drizzle-kit
Schema None (raw SQL) src/db/schema.ts

Review Comment Response

The review comment about table name mismatch is incorrect. The SQLite example uses raw SQL without Drizzle schema, so:

  • ✅ Table created: messages (line 9)
  • ✅ Table queried: messages (lines 25, 36)
  • ✅ No schema file needed for raw SQL approach

Recommendations

  1. Fix README consistency:

    npm install
    # and
    npm run dev
  2. Add basic tests (optional but recommended):

    // tests/sqlite.test.ts
    test("SQLite chat can send and retrieve messages", async (ctx) => {
      const { client } = await setupTest(ctx, registry);
      const chat = client.chat.getOrCreate(["test-chat"]);
      
      const message = await chat.sendMessage("user1", "Hello world");
      expect(message).toMatchObject({
        sender: "user1",
        text: "Hello world",
        timestamp: expect.any(Number)
      });
      
      const history = await chat.getHistory();
      expect(history).toHaveLength(1);
    });
  3. Performance optimization (optional):

    CREATE INDEX IF NOT EXISTS idx_messages_timestamp ON messages(timestamp DESC)

Final Verdict

✅ APPROVE - This is a solid example that demonstrates raw SQLite integration effectively. The security is excellent, the code follows project conventions, and it provides valuable contrast to the Drizzle example. The minor README inconsistencies are easily fixable.

Priority: Fix README commands for consistency, consider adding tests for completeness.


This was referenced Aug 27, 2025
Comment on lines +34 to +37
getHistory: (c) =>
c.db
.prepare(`SELECT * FROM messages ORDER BY timestamp DESC LIMIT 100`)
.all(),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential data exposure issue: The getHistory action returns all columns from the messages table without any filtering or sanitization. If the database schema is later extended to include sensitive fields (like IP addresses, user IDs, or other metadata), this query would expose all that data to any client that calls this action. The query should explicitly specify which columns to return rather than using SELECT *.

Suggested change
getHistory: (c) =>
c.db
.prepare(`SELECT * FROM messages ORDER BY timestamp DESC LIMIT 100`)
.all(),
getHistory: (c) =>
c.db
.prepare(`SELECT id, text, username, timestamp FROM messages ORDER BY timestamp DESC LIMIT 100`)
.all(),

Spotted by Diamond

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant