We officially started Sprint 1 of building our Lawyer Management System, Lura. After the planning sessions and backlog setup, it was finally time to get our hands dirty with code.
🛠️ Goals of Sprint 1:
Sprint 1 was focused on building the foundational features of the app:
- Magic Link Authentication
- Workspace Management
- Case Management (CRUD operations)
We broke down the sprint into clear user stories, each mapped to GitHub issues and prioritized in the backlog. With a sprint goal defined, we started implementing the structure piece by piece.
🔐 Magic Link Authentication with Clerk (or NextAuth + custom logic)
For our authentication, we chose a Magic Link method. Unlike traditional username/password logins, Magic Link provides a secure, frictionless login flow using one-time login links via email. This is a huge UX win for business-facing apps like Lura.
Key steps involved:
- Setup of the authentication service (we opted for Clerk, but this can be replicated with Supabase Auth or custom NextAuth flow).
- Integration in the Next.js frontend using clerkProvider and session handling via hooks.
- Restricting access to protected routes using middleware and server-side guards.
// Example: Protecting a page in Next.js
import { useUser } from "@clerk/nextjs";
export default function Dashboard() {
const { isSignedIn, user } = useUser();
if (!isSignedIn) return <SignIn />;
return <div>Welcome {user.firstName}</div>;
}
We also wrote a role-checking helper that can be reused across the app, ensuring that each user only accesses the areas and actions permitted by their role.
🧱 Workspace Creation and Management
Each lawyer/admin should operate within a defined workspace, similar to how Slack or Notion handles organizations.
Technically:
- We created a Workspace model in Prisma.
- Related it with a UserWorkspace join table to manage multi-user access.
- Added basic CRUD API routes using NestJS controllers and services.
- Built the UI in Next.js with a responsive workspace creation modal.
model Workspace {
id String @id @default(cuid())
name String
createdAt DateTime @default(now())
users UserWorkspace[]
cases Case[]
}
📂 Case Management
Cases are the core of Lura — each case lives inside a workspace and can contain documents, tags, and comments.
We defined a Case model and linked it to both Workspace and User.
Key features:
- Create / Read / Update / Delete (CRUD) endpoints in NestJS.
- Route protection using guards for role-based permissions.
- UI built with TailwindCSS and Next.js dynamic routes.
- Cases are displayed in a sidebar for easy navigation.
Example:
ts
@Get(':workspaceId/cases')
@UseGuards(AuthGuard)
findCases(@Param('workspaceId') id: string) {
return this.caseService.findByWorkspace(id);
}
🤖 Technical Stack Overview
- Frontend: Next.js + TailwindCSS
- Backend: NestJS
- ORM: Prisma
- DB: PostgreSQL
- Auth: Magic Link (via Clerk/Supabase/NextAuth)
- Hosting: Vercel (Frontend) + Render or Railway (Backend)
✅ Takeaways
- Planning ahead made implementation smoother — especially for auth and data relations.
- Prisma made defining models and relations very intuitive.
- Integrating both frontend and backend from the start helped ensure alignment in data structures.
- Role-based access is tricky but essential — we began building a permission service early to avoid security gaps.
❓Questions for You:
- How do you structure role-based access in your apps?
- Do you start with auth or database models first?
Top comments (0)