Database
Database Overview
Database Overview
Solo Kit uses Convex - a reactive, serverless database platform that combines database, backend functions, and real-time sync in one unified system. This guide covers everything from basic concepts to advanced database management.
ποΈ Architecture Overview
Convex-First Design
Solo Kit is built with Convex, providing:
- Type Safety - End-to-end TypeScript with generated types
- Real-time - Automatic UI updates when data changes
- Serverless - Zero operations, automatic scaling
- Consistency - Same development experience from dev to production
Technology Stack
π Application Layer (React/Next.js)
β (useQuery, useMutation hooks)
π§ Convex Functions (TypeScript)
β (Type-safe queries/mutations)
πΎ Convex Database (Document store)
β (Global distribution)
βοΈ Convex Cloud (Managed infrastructure)ποΈ Database Structure
Convex Organization
apps/web/convex/
βββ schema.ts # Database schema definition
βββ _generated/ # Auto-generated types
β βββ api.ts # API exports
β βββ server.ts # Server types
β βββ dataModel.ts # Data model types
βββ users.ts # User functions
βββ auth.ts # Authentication functions
βββ sessions.ts # Session management
βββ payments.ts # Payment functionsπ§ Core Concepts
Schema Definition
Type-Safe Schema:
import { defineSchema, defineTable } from 'convex/server';
import { v } from 'convex/values';
export default defineSchema({
users: defineTable({
name: v.string(),
email: v.string(),
image: v.optional(v.string()),
emailVerified: v.optional(v.boolean()),
createdAt: v.number(),
}).index('by_email', ['email']),
sessions: defineTable({
userId: v.id('users'),
expiresAt: v.number(),
token: v.string(),
})
.index('by_user', ['userId'])
.index('by_token', ['token']),
});Benefits:
- Automatic type generation
- Runtime validation
- Built-in indexing
- Real-time reactivity
Convex Functions
Three types of functions handle all backend logic:
Queries (Read Data):
import { query } from './_generated/server';
import { v } from 'convex/values';
export const getUser = query({
args: { userId: v.id('users') },
handler: async (ctx, { userId }) => {
return await ctx.db.get(userId);
},
});Mutations (Write Data):
import { mutation } from './_generated/server';
import { v } from 'convex/values';
export const createUser = mutation({
args: {
name: v.string(),
email: v.string(),
},
handler: async (ctx, args) => {
return await ctx.db.insert('users', {
...args,
emailVerified: false,
createdAt: Date.now(),
});
},
});Actions (External Operations):
import { action } from './_generated/server';
import { v } from 'convex/values';
export const sendEmail = action({
args: {
to: v.string(),
subject: v.string(),
},
handler: async (ctx, { to, subject }) => {
// Call external email service
await fetch('https://api.email.com/send', {
method: 'POST',
body: JSON.stringify({ to, subject }),
});
},
});π» Using Convex in Your App
Client Components
Real-time reactive data:
"use client";
import { useQuery, useMutation } from "convex/react";
import { api } from "@/convex/_generated/api";
export function UserProfile({ email }: { email: string }) {
// Automatically updates when data changes
const user = useQuery(api.users.getUserByEmail, { email });
const updateUser = useMutation(api.users.updateUser);
if (!user) return <div>Loading...</div>;
return (
<div>
<h1>{user.name}</h1>
<button onClick={() => updateUser({ userId: user._id, name: "New Name" })}>
Update Name
</button>
</div>
);
}Server Components
Fetch data for SSR:
import { fetchQuery } from "convex/nextjs";
import { api } from "@/convex/_generated/api";
export default async function UserPage({ params }: { params: { id: string } }) {
const user = await fetchQuery(api.users.getUser, {
userId: params.id as Id<"users">
});
return <div>{user.name}</div>;
}π Getting Started
1. Initialize Convex
# Start Convex dev server
npx convex devThis will:
- Create a Convex project
- Set up your
convex/directory - Add environment variables
- Start the dev server
2. Define Your Schema
Create convex/schema.ts:
import { defineSchema, defineTable } from 'convex/server';
import { v } from 'convex/values';
export default defineSchema({
users: defineTable({
email: v.string(),
name: v.string(),
}).index('by_email', ['email']),
});3. Create Functions
Create convex/users.ts:
import { query, mutation } from './_generated/server';
import { v } from 'convex/values';
export const list = query({
handler: async (ctx) => {
return await ctx.db.query('users').collect();
},
});
export const create = mutation({
args: {
email: v.string(),
name: v.string(),
},
handler: async (ctx, args) => {
return await ctx.db.insert('users', args);
},
});4. Use in Your App
"use client";
import { useQuery, useMutation } from "convex/react";
import { api } from "@/convex/_generated/api";
export function Users() {
const users = useQuery(api.users.list);
const createUser = useMutation(api.users.create);
return (
<div>
{users?.map(user => <div key={user._id}>{user.name}</div>)}
<button onClick={() => createUser({ email: "new@example.com", name: "New User" })}>
Add User
</button>
</div>
);
}π― Key Features
Real-time Updates
Convex queries automatically subscribe to data changes:
// Component re-renders automatically when data changes
const users = useQuery(api.users.list);Type Safety
TypeScript types are generated from your schema:
import { Doc, Id } from './_generated/dataModel';
type User = Doc<'users'>;
type UserId = Id<'users'>;Performance
- Automatic indexing
- Query optimization
- Global edge deployment
- WebSocket connections for real-time
Developer Experience
- Local dev server with instant feedback
- Dashboard for data browsing
- Function logs and performance metrics
- Built-in testing utilities
π Best Practices
Schema Design
- Use indexes for frequently queried fields
- Keep documents relatively flat
- Use references (IDs) to link documents
- Add search indexes for text search
Query Optimization
- Use indexes for filtering
- Limit results with
.take(n) - Paginate large result sets
- Avoid full table scans
Security
- Implement authorization in functions
- Validate inputs with Convex validators
- Never expose sensitive data
- Use function-level security checks
π Learn More
π Getting Help
π Quick Reference
Common Operations
// Get by ID
const user = await ctx.db.get(userId);
// Query with index
const user = await ctx.db
.query('users')
.withIndex('by_email', (q) => q.eq('email', email))
.first();
// Insert
const userId = await ctx.db.insert('users', {
name: 'John',
email: 'john@example.com',
});
// Update
await ctx.db.patch(userId, {
name: 'John Updated',
});
// Delete
await ctx.db.delete(userId);Environment Variables
# Required for Convex
NEXT_PUBLIC_CONVEX_URL=https://your-project.convex.cloud
CONVEX_DEPLOY_KEY=your-deploy-keyCLI Commands
# Start dev server
npx convex dev
# Deploy to production
npx convex deploy
# View dashboard
npx convex dashboard
# Clear local data
npx convex data clearNext Steps: