Prisma Jotai Client ๐
A fully dynamic Prisma client wrapper for Jotai with conditional typing and built-in pagination support
Installation โข Quick Start โข API โข Examples โข Contributing
โจ Features
- ๐ Fully Dynamic: All models and operations extracted automatically from generated Prisma types
- ๐ฏ Conditional Typing: Return types inferred based on parameters (
select,include,omit,paginated) - ๐ Built-in Pagination: Native pagination support with automatic metadata
- ๐ก๏ธ Type Safe: Complete TypeScript support with full autocompletion
- โก Optimized: Intelligent caching with atom families and
isEqual - ๐ช Zero Config: No manual configuration required
- ๐ง Extensible: New models automatically supported
- ๐ Universal: Works with any Prisma schema
๐๏ธ Architecture
This library creates a bridge between Prisma and Jotai by:
- Using dynamic proxies to intercept model access
- Leveraging generated Prisma types (
Prisma.TypeMap,Prisma.ModelName) - Creating cached atom families for optimal performance
- Providing advanced TypeScript inference for conditional return types
๐ฆ Installation
npm install prisma-jotai-client # or yarn add prisma-jotai-client # or pnpm add prisma-jotai-client # or bun add prisma-jotai-client
Peer Dependencies
Make sure you have the required peer dependencies installed:
npm install @prisma/client jotai react
๐ Quick Start
- Generate your Prisma client (if not already done):
- Import and use:
import { $ } from 'prisma-jotai-client'; import { useAtom } from 'jotai'; function UserProfile({ userId }: { userId: string }) { // Automatically typed based on your Prisma schema const [userAtom] = useAtom($.user.findUnique({ where: { id: userId }, include: { posts: true, profile: true } })); // userAtom type: Atom<Promise<User & { posts: Post[]; profile: Profile | null } | null>> return ( <Suspense fallback="Loading..."> <UserDisplay userAtom={userAtom} /> </Suspense> ); }
๐ฏ Conditional Typing
The return type changes automatically based on the parameters you use:
// SELECT: Returns only selected fields const userWithSelect = $.user.findUnique({ where: { id: 'user-123' }, select: { id: true, email: true } }); // Type: Atom<Promise<{ id: string; email: string } | null>> // INCLUDE: Returns full object + relations const userWithInclude = $.user.findUnique({ where: { id: 'user-123' }, include: { posts: true, comments: true } }); // Type: Atom<Promise<User & { posts: Post[]; comments: Comment[] } | null>> // Default: Returns base object const userBasic = $.user.findUnique({ where: { id: 'user-123' } }); // Type: Atom<Promise<User | null>>
๐ Pagination
Built-in pagination support with automatic metadata:
// Basic pagination const paginatedUsers = $.user.findMany({ take: 10, skip: 0, paginated: true }); // Returns: { data: User[], total: number, take: number, skip: number } // Pagination with filters and sorting const filteredPosts = $.post.findMany({ where: { published: true }, orderBy: { createdAt: 'desc' }, take: 25, skip: 50, paginated: true }); // Pagination with SELECT const paginatedWithSelect = $.user.findMany({ select: { id: true, email: true }, take: 20, paginated: true }); // Type: { data: { id: string; email: string }[], total: number, take: number, skip: number }
Automatic metadata:
-
data: Query results -
total: Total number of records -
take: Applied limit -
skip: Applied offset
๐ API Reference
Available Operations
All standard Prisma operations are supported automatically:
-
findUnique/findUniqueOrThrow -
findFirst/findFirstOrThrow findMany-
create/createMany/createManyAndReturn -
update/updateMany/updateManyAndReturn -
delete/deleteMany upsertcountaggregategroupBy
Dynamic Model Access
Access any model from your Prisma schema:
$.user.findMany() // User operations $.post.findMany() // Post operations $.comment.findMany() // Comment operations $.profile.findMany() // Profile operations // ... any model from your schema
Type-Safe Parameters
All parameters are fully typed based on your Prisma schema:
// TypeScript will provide autocompletion and validation $.post.findMany({ where: { title: { contains: 'prisma' }, author: { email: { endsWith: '@example.com' } } }, orderBy: { createdAt: 'desc' }, include: { author: true, comments: true } });
๐ก Examples
Real-World Usage Patterns
User Management with Search
function useUserList(search: string, page: number) { return useAtom($.user.findMany({ where: search ? { OR: [ { email: { contains: search, mode: 'insensitive' } }, { name: { contains: search, mode: 'insensitive' } } ] } : undefined, orderBy: { createdAt: 'desc' }, take: 20, skip: (page - 1) * 20, select: { id: true, email: true, name: true, createdAt: true }, paginated: true })); }
Blog Posts with Analytics
function useBlogPosts(authorId?: string) { return useAtom($.post.findMany({ where: authorId ? { authorId } : { published: true }, include: { author: { select: { name: true, email: true } }, _count: { comments: true } }, orderBy: { createdAt: 'desc' } })); }
Advanced Filtering
function usePostsWithFilters(filters: { authorId?: string; dateFrom?: Date; dateTo?: Date; hasComments?: boolean; }) { return useAtom($.post.findMany({ where: { ...(filters.authorId && { authorId: filters.authorId }), ...(filters.dateFrom && { createdAt: { gte: filters.dateFrom } }), ...(filters.dateTo && { createdAt: { lte: filters.dateTo } }), ...(filters.hasComments !== undefined && { comments: filters.hasComments ? { some: {} } : { none: {} } }) }, include: { author: true, comments: true } })); }
๐งช Testing
Run the included examples:
# See full demonstration npm run demo # Test pagination examples npm run demo:pagination
๐๏ธ Development
Setup
# Clone the repository git clone https://github.com/yourusername/prisma-jotai-client.git cd prisma-jotai-client # Install dependencies npm install # Run development build npm run dev
Build
# Build for production npm run build # Type check npm run type-check # Lint npm run lint
๐ Benefits
-
Conditional Typing: Return types inferred based on
select/include/omit/paginated -
Native Pagination: Automatic metadata (
total,take,skip) - Zero Maintenance: New models automatically supported
- Type Safety: TypeScript errors for invalid models/fields
- Performance: Intelligent atom caching
- Developer Experience: Full IDE autocompletion
- Flexibility: Complete Prisma feature support
๐ง Configuration
TypeScript
Ensure your tsconfig.json includes:
{
"compilerOptions": {
"strict": true,
"moduleResolution": "node",
"esModuleInterop": true
}
}Prisma
Works with any Prisma schema. Just run prisma generate and you're ready to go!
๐ค Contributing
We welcome contributions! Please see our Contributing Guide for details.
Development Workflow
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Run
npm run buildandnpm run type-check - Submit a pull request
๐ License
MIT ยฉ Your Name
๐ Acknowledgments
- Prisma for the amazing ORM and type generation
- Jotai for the excellent atomic state management
- The React and TypeScript communities
๐ Support
- ๐ Documentation
- ๐ Bug Reports
- ๐ฌ Discussions
- ๐ง Email Support