Does NestJS Work With Lucia?
NestJS and Lucia integrate seamlessly for session-based authentication in enterprise Node.js applications.
Quick Facts
How NestJS Works With Lucia
NestJS and Lucia work together naturally because Lucia is framework-agnostic and designed to integrate with any Node.js HTTP framework. You create a Lucia auth instance, configure it with a session adapter (like Redis or a database), and then use NestJS guards and middleware to protect routes. The typical pattern involves creating a custom NestJS guard that validates Lucia sessions via cookies, then attaching user data to the request object for downstream handlers. Lucia handles the cryptographic session management while NestJS provides the dependency injection and decorator-based route protection.
The developer experience is smooth: Lucia's minimal API surface means you're not fighting framework abstractions, and NestJS's guard system integrates cleanly with session validation. You'll set up Lucia in a service, inject it into guards, and use standard NestJS decorators like `@UseGuards()` to protect endpoints. Lucia's database adapter pattern means you control exactly how sessions persist, whether through TypeORM (which NestJS loves), Prisma, or raw SQL. The only consideration is that Lucia requires explicit cookie handling—you need to manage Set-Cookie headers yourself, but NestJS's response object makes this straightforward.
Best Use Cases
Quick Setup
npm install lucia @lucia-auth/adapter-postgresql postgres// auth.service.ts
import { Lucia } from 'lucia';
import { NodePostgresAdapter } from '@lucia-auth/adapter-postgresql';
import { Injectable } from '@nestjs/common';
import { pool } from './db';
@Injectable()
export class AuthService {
lucia = new Lucia(new NodePostgresAdapter(pool, {
user: 'auth_user',
session: 'user_session',
}), {
sessionCookie: {
attributes: {
secure: true,
httpOnly: true,
sameSite: 'lax',
},
},
});
async createSession(userId: string) {
const session = await this.lucia.createSession(userId, {});
return this.lucia.createSessionCookie(session.id);
}
}
// auth.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { AuthService } from './auth.service';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const sessionId = request.cookies.auth_session;
const { session, user } = await this.authService.lucia
.validateSession(sessionId);
request.user = user;
return !!user;
}
}
// usage
@Get('dashboard')
@UseGuards(AuthGuard)
dashboard(@Req() req: Request) {
return { userId: req.user.id };
}Known Issues & Gotchas
Lucia requires explicit cookie configuration; NestJS won't auto-handle session cookies
Fix: Set secure, httpOnly, sameSite flags manually in your response interceptor or use a library like cookie-parser middleware
Database adapter setup can be verbose; Lucia doesn't provide ORM integrations out-of-the-box
Fix: Create a simple session/user table and implement Lucia's `SessionAdapter` interface—takes 20 minutes with TypeORM
CORS and credentials don't work without proper configuration
Fix: Enable `credentials: 'include'` on frontend and configure NestJS CORS with `credentials: true`
Alternatives
- •NestJS + Passport.js (more opinionated, larger ecosystem, steeper learning curve)
- •NestJS + jsonwebtoken with custom middleware (stateless but requires token refresh complexity)
- •NestJS + Auth0/Cognito (outsourced auth, less control, enterprise-friendly)
Resources
Related Compatibility Guides
Explore more compatibility guides