Kysely Integration
Pure Dev uses Kysely as its query builder, providing a fully type-safe SQL query interface that integrates seamlessly with your Supabase database.
Automatic Type Generation
When you connect to your Supabase database, Pure Dev automatically:
- Introspects your Supabase database schema
- Generates TypeScript interfaces for all tables
- Creates the necessary Kysely type definitions
- Provides these types to your code editor for autocomplete and type checking
Advanced Querying Examples
Joins with Type Safety
export async function getPostsWithComments(pure: Pure): Promise<PureResponse> {
const postsWithComments = await pure.db
.selectFrom("blog.posts as p")
.leftJoin("blog.comments as c", "c.post_id", "p.id")
.select([
"p.id as post_id",
"p.title",
"p.content",
"c.id as comment_id",
"c.content as comment_content"
])
.where("p.status", "=", "published")
.execute();
// Transform into nested structure
const posts = {};
postsWithComments.forEach(row => {
if (!posts[row.post_id]) {
posts[row.post_id] = {
id: row.post_id,
title: row.title,
content: row.content,
comments: []
};
}
if (row.comment_id) {
posts[row.post_id].comments.push({
id: row.comment_id,
content: row.comment_content
});
}
});
return pure.json(Object.values(posts));
}
Complex Filtering
export async function searchPosts(pure: Pure): Promise<PureResponse> {
const { query, tags, status } = pure.req.queries;
let postsQuery = pure.db
.selectFrom("blog.posts")
.selectAll();
// Add text search if provided
if (query) {
postsQuery = postsQuery
.where(eb => eb.or([
eb("title", "like", `%${query}%`),
eb("content", "like", `%${query}%`),
eb("excerpt", "like", `%${query}%`)
]));
}
// Filter by status if provided
if (status) {
postsQuery = postsQuery.where("status", "=", status);
}
// Filter by tags if provided
if (tags && tags.length > 0) {
const tagList = tags.split(",");
postsQuery = postsQuery
.innerJoin(
"blog.post_tags as pt",
"pt.post_id",
"blog.posts.id"
)
.innerJoin(
"blog.tags as t",
"t.id",
"pt.tag_id"
)
.where("t.name", "in", tagList);
}
const posts = await postsQuery.execute();
return pure.json(posts);
}
Aggregations
export async function handler(pure: Pure): Promise<PureResponse> {
const stats = await pure.db
.selectFrom("blog.posts")
.select([
(eb) => eb.fn.count("id").as("total_posts"),
(eb) => eb.fn.avg("blog.post_views.view_count").as("avg_views"),
"status"
])
.leftJoin(
"blog.post_views",
"blog.post_views.post_id",
"blog.posts.id"
)
.groupBy("status")
.execute();
return pure.json(stats);
}
Development Experience
Working with Supabase in Pure Dev provides an exceptional development experience:
Interactive Query Logs
See exactly what's happening with your database in real-time:
- View generated SQL queries
- Track execution time
- Inspect query results
Schema Validation
Pure Dev helps ensure your code matches your database schema:
- Warnings for missing columns
- Errors for invalid data types
- Suggestions for query optimization (coming soon)
- Feedback on index usage (coming soon)
Best Practices
When working with Supabase in Pure Dev, we recommend:
Use Repository Pattern
export class UsersRepository {
constructor(private db: Kysely<Database>) {}
async findByEmail(email: string): Selectable<Tables.Users> {
return this.db
.selectFrom('users')
.where('email', '=', email)
.selectAll()
.executeTakeFirst();
}
}
// Using the repository
export async function handler(pure: Pure): Promise<PureResponse> {
const { email } = pure.req.queries;
const usersRepo = new UsersRepository(db);
const user = await usersRepo.findByEmail(email);
if (!user) {
return pure.json({ error: 'User not found' }, 404);
}
return pure.json(user);
}
Implement Error Handling
export async function handler(pure: Pure): Promise<PureResponse> {
try {
const { userId } = pure.req.params;
const user = await pure.db
.selectFrom('users')
.where('id', '=', userId)
.selectAll()
.executeTakeFirst();
if (!user) {
return pure.json({ error: 'User not found' }, 404);
}
return pure.json(user);
} catch (error) {
// Handle unexpected errors
return pure.json({ error: error.message }, 500);
}
}
Use Transactions for Related Operations
export async function createOrderWithItems(pure: Pure): Promise<PureResponse> {
const { order, items } = pure.req.body;
return await pure.db.transaction().execute(async (trx) => {
// Create the order
const [newOrder] = await trx
.insertInto('orders')
.values(order)
.returning('id')
.execute();
// Add order ID to each item
const orderItems = items.map(item => ({
...item,
order_id: newOrder.id
}));
// Insert all items
await trx
.insertInto('order_items')
.values(orderItems)
.execute();
return pure.json({
message: 'Order created successfully',
orderId: newOrder.id
}, 201);
});
}