Skip to Content

Basic Usage

This guide will walk you through the basic usage patterns of CrudKit, showing you how to implement common CRUD operations with Drizzle, tRPC, and React Query.

Setting Up Your Schema

The first step in using CrudKit is to define your database schema using Drizzle:

// schema.ts import { pgTable, serial, text, varchar, timestamp } from 'drizzle-orm/pg-core'; export const products = pgTable('products', { id: serial('id').primaryKey(), name: text('name').notNull(), description: text('description'), sku: varchar('sku', { length: 50 }).notNull().unique(), price: numeric('price', { precision: 10, scale: 2 }).notNull(), createdAt: timestamp('created_at').defaultNow(), updatedAt: timestamp('updated_at'), });

Creating a tRPC Router

Next, use the generateRouter function to create a tRPC router for your schema:

// api/routers/products.ts import { createTRPCRouter, publicProcedure, protectedProcedure } from '../trpc'; import { generateRouter } from 'crudkit'; import { db } from '../db'; import { products } from '../schema'; export const productsRouter = generateRouter( { table: products, // Optional: customize specific operations customProcedures: { // Add a custom search procedure search: publicProcedure .input(z.object({ query: z.string() })) .query(async ({ input, ctx }) => { return await ctx.db .select() .from(products) .where(like(products.name, `%${input.query}%`)) .limit(10); }), }, }, { createTRPCRouter, publicProcedure, protectedProcedure, } );

Setting Up React Query Hooks

Generate hooks for your frontend with generateReactQueryHooks:

// hooks/use-products.ts import { generateReactQueryHooks } from 'crudkit'; import { trpc } from '../utils/trpc'; import { products } from '../schema'; export const useProducts = generateReactQueryHooks( { routerPath: 'products', }, trpc );

Using the Hooks in Components

Creating a Product

// components/CreateProduct.tsx import { useState } from 'react'; import { useProducts } from '../hooks/use-products'; export function CreateProduct() { const [product, setProduct] = useState({ name: '', description: '', sku: '', price: '', }); const { create, isLoading, error } = useProducts.useCreate(); const handleSubmit = (e) => { e.preventDefault(); create({ ...product, price: parseFloat(product.price), }); }; return ( <form onSubmit={handleSubmit}> <h2>Add New Product</h2> <div> <label htmlFor="name">Name</label> <input id="name" value={product.name} onChange={(e) => setProduct({ ...product, name: e.target.value })} required /> </div> {/* Add other form fields */} <button type="submit" disabled={isLoading}> {isLoading ? 'Adding...' : 'Add Product'} </button> {error && <p className="error">{error.message}</p>} </form> ); }

Displaying Products

// components/ProductsList.tsx import { useProducts } from '../hooks/use-products'; export function ProductsList() { const { data, isLoading, error } = useProducts.useGetAll(); if (isLoading) return <div>Loading products...</div>; if (error) return <div>Error: {error.message}</div>; return ( <div> <h2>Products</h2> <table> <thead> <tr> <th>Name</th> <th>SKU</th> <th>Price</th> <th>Actions</th> </tr> </thead> <tbody> {data?.map((product) => ( <tr key={product.id}> <td>{product.name}</td> <td>{product.sku}</td> <td>${product.price}</td> <td> <button>Edit</button> <button>Delete</button> </td> </tr> ))} </tbody> </table> </div> ); }

Next Steps

In the Advanced Usage guide, we’ll explore more complex scenarios like custom validation, relationships between tables, and optimizing performance.

Last updated on