Brain Learning Center
Learning management platform: a NestJS API and a bilingual Next.js admin dashboard with four roles, two-factor sign-in, and course, lesson and review tools.
About
An online school platform built in two parts: a REST API and an admin dashboard. Administrators, instructors and students each get their own view of courses, lessons, enrollments, progress and reviews. It is a self-initiated build, used to work through role-based access, authentication and a bilingual English and Arabic interface from the database to the screen.
Challenges
Four roles (super admin, admin, instructor and student) need different permissions on the same data, and the rules have to hold on the server, not only in the interface. Sign-in had to be safe by default: short-lived access tokens, refresh tokens that scripts cannot read, and two-factor authentication. The dashboard also had to work in Arabic, which means a right-to-left layout and not only translated text.
Solutions
The API is NestJS with Prisma and PostgreSQL, split into nine modules: auth, admin, categories, courses, lessons, enrollments, progress, reviews and mailer. Role guards and ownership checks enforce access on every route. Access tokens last five minutes in the default setup, refresh tokens rotate and travel in an httpOnly cookie, and sign-in supports email codes and authenticator apps, with one-time-code password reset. Every response uses one envelope with a request ID on errors, lists use cursor pagination, and the API is documented with Swagger. The dashboard is Next.js 16 with TanStack Query and Table for server-driven lists (search, sort, filter, pagination), React Hook Form with Zod for forms, and next-intl with a direction switch for English and Arabic.
Technologies
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const required = this.reflector.getAllAndOverride<RoleName[]>(ROLES_KEY, [
context.getHandler(),
context.getClass(),
]);
// Public route, no role restriction applied
if (!required || required.length === 0) return true;
const request = context.switchToHttp().getRequest();
const user = request.user as AuthUserPayload | undefined;
// 401 instead of 403 to avoid leaking protected route info
if (!user) throw new UnauthorizedException();
const hasRole = required.some((role) => user.roles.includes(role));
if (!hasRole) throw new UnauthorizedException();
return true;
}
}



Ready To Build Something Real?
Whether you need a Webflow site your team can run, a full-stack web app, or a second pair of hands on an existing codebase, I'm open to freelance projects and full-time roles. Agencies: I also build white-label, under your brand.