erojas.devertek.io logo
Building a Robust Authentication System with Prisma and MongoDB

The Foundation: Prisma Schema Design

My authentication system is built around a well-structured Prisma schema that handles multiple authentication methods while maintaining data integrity and security. Let me break down the key components:

1datasource db {
2    provider = "mongodb"
3    url = env("DATABASE_URL")
4}
5  generator client {
6    provider = "prisma-client-js"
7}

I chose MongoDB for its flexibility in handling nested data structures and its excellent support for rapid development. The Prisma client provides type-safe database operations while maintaining the flexibility that MongoDB offers.

Core User Model The User model is the heart of the authentication system:

1model User {
2    id            String @id @default (auto()) @map("_id") @db.ObjectId
3    email         String @unique
4    emailVerified DateTime ?
5        password      String ?   // Make optional for OAuth users
6            name          String ?
7                image         String ?   // Add for OAuth profile pictures
8                    createdAt     DateTime @default (now())
9    updatedAt     DateTime @updatedAt
10  
11    sessions      Session[]
12    accounts      Account[] // Add for OAuth
13}

Key Design Decisions:

  1. Optional Password Field: By making password optional (String?), I can support both traditional authentication and OAuth users who don't have passwords.

  2. MongoDB ObjectId Mapping: Using @map("_id") @db.ObjectId ensures compatibility with MongoDB's native ID system while maintaining Prisma's type safety.

  3. Flexible User Data: The name and image fields are optional to accommodate different OAuth providers that may or may not provide this information.

  4. Audit Trail: createdAt and updatedAt fields provide essential tracking for user management and debugging.

The session model implements several important patterns:

  • Cascade Deletion: When a user is deleted, all their sessions are automatically removed (onDelete: Cascade)

  • Unique Session Tokens: Each session has a unique token for secure identification

  • Expiration Handling: Built-in expiration tracking for automatic session cleanup

Implementation Patterns Query Organization

I organized my database operations into focused query modules:

1export const userQueries = {
2    async getUserByEmail(email: string) {
3        return await prisma.user.findUnique({
4            where: { email },
5            select: {
6                id: true,
7                email: true,
8                name: true,
9                createdAt: true
10            }
11        })
12    },
13    // ... more user operations
14}

Benefits of this approach

By combining Prisma with MongoDB, this authentication architecture delivers a powerful balance of safety, performance, and flexibility. Prisma’s full TypeScript support ensures end-to-end type safety, reducing runtime errors and improving developer experience with auto-completed database models. MongoDB’s document-based structure provides the flexibility to evolve your schema over time without painful migrations. On the security side, features like secure password hashing, session tracking, and JWT token handling are built directly into the flow, giving you full control over user authentication and access. Because Prisma leverages efficient database operations and indexing, your queries remain scalable even as your user base grows. Most importantly, the clean separation of responsibilities—grouping logic into dedicated query modules—keeps your codebase maintainable and easy to extend as your application matures.

Lessons Learned

A key advantage of this Prisma schema design is its support for multiple authentication strategies through optional fields—making the password field optional allows the system to seamlessly handle both credential-based and OAuth logins without enforcing a rigid structure. Cascade relationships ensure that related session and account records are automatically cleaned up when a user is removed, preventing orphaned data and maintaining database integrity. Robust token management is also built in, enabling secure handling of session lifecycles and email verification flows. Additionally, by separating database operations into organized modules, the codebase remains clean, scalable, and easy to maintain. Altogether, this architecture establishes a secure and future-ready foundation for your authentication system, balancing flexibility, performance, and long-term growth. Until next time keep coding.