Below is a detailed implementation for user management, including user registration, login, profile management, and role-based access control using Next.js, NestJS, and GraphQL.
Backend (NestJS)
1. User Entity
// user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
username: string;
@Column()
password: string;
@Column()
role: string;
}
2. User Service
// user.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
import * as bcrypt from 'bcrypt';
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private usersRepository: Repository<User>,
) {}
async findOne(username: string): Promise<User | undefined> {
return this.usersRepository.findOne({ where: { username } });
}
async create(username: string, password: string, role: string): Promise<User> {
const hashedPassword = await bcrypt.hash(password, 10);
const newUser = this.usersRepository.create({ username, password: hashedPassword, role });
return this.usersRepository.save(newUser);
}
async validateUser(username: string, password: string): Promise<User | null> {
const user = await this.findOne(username);
if (user && await bcrypt.compare(password, user.password)) {
return user;
}
return null;
}
}
3. User Resolver
// user.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { UserService } from './user.service';
import { User } from './user.entity';
import { UseGuards } from '@nestjs/common';
import { GqlAuthGuard } from './guards/gql-auth.guard';
@Resolver(() => User)
export class UserResolver {
constructor(private userService: UserService) {}
@Mutation(() => User)
async register(
@Args('username') username: string,
@Args('password') password: string,
@Args('role') role: string,
) {
return this.userService.create(username, password, role);
}
@Mutation(() => String)
async login(@Args('username') username: string, @Args('password') password: string) {
const user = await this.userService.validateUser(username, password);
if (user) {
// Generate JWT token (implementation not shown here)
return 'JWT_TOKEN';
}
throw new Error('Invalid credentials');
}
@Query(() => User)
@UseGuards(GqlAuthGuard)
async profile(@Args('username') username: string) {
return this.userService.findOne(username);
}
}
4. Authentication Guard
// gql-auth.guard.ts
import { Injectable, ExecutionContext } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class GqlAuthGuard extends AuthGuard('jwt') {
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req;
}
}
5. GraphQL Schema
type User {
id: ID!
username: String!
role: String!
}
type Query {
profile(username: String!): User!
}
type Mutation {
register(username: String!, password: String!, role: String!): User!
login(username: String!, password: String!): String!
}
Frontend (Next.js)
1. Apollo Client Setup
// apollo-client.js
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: 'http://localhost:3000/graphql',
cache: new InMemoryCache(),
});
export default client;
2. Registration Form
// pages/register.js
import { useState } from 'react';
import { useMutation, gql } from '@apollo/client';
import { useRouter } from 'next/router';
const REGISTER_USER = gql`
mutation Register($username: String!, $password: String!, $role: String!) {
register(username: $username, password: $password, role: $role) {
id
username
}
}
`;
export default function Register() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [role, setRole] = useState('student');
const [register] = useMutation(REGISTER_USER);
const router = useRouter();
const handleSubmit = async (e) => {
e.preventDefault();
try {
await register({ variables: { username, password, role } });
router.push('/login');
} catch (err) {
console.error(err);
}
};
return (
<form onSubmit={handleSubmit}>
<input type="text" placeholder="Username" value={username} onChange={(e) => setUsername(e.target.value)} />
<input type="password" placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} />
<select value={role} onChange={(e) => setRole(e.target.value)}>
<option value="student">Student</option>
<option value="teacher">Teacher</option>
<option value="admin">Admin</option>
</select>
<button type="submit">Register</button>
</form>
);
}
3. Login Form
// pages/login.js
import { useState } from 'react';
import { useMutation, gql } from '@apollo/client';
import { useRouter } from 'next/router';
const LOGIN_USER = gql`
mutation Login($username: String!, $password: String!) {
login(username: $username, password: $password)
}
`;
export default function Login() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [login] = useMutation(LOGIN_USER);
const router = useRouter();
const handleSubmit = async (e) => {
e.preventDefault();
try {
const { data } = await login({ variables: { username, password } });
localStorage.setItem('token', data.login);
router.push('/profile');
} catch (err) {
console.error(err);
}
};
return (
<form onSubmit={handleSubmit}>
<input type="text" placeholder="Username" value={username} onChange={(e) => setUsername(e.target.value)} />
<input type="password" placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} />
<button type="submit">Login</button>
</form>
);
}
4. Profile Page
// pages/profile.js
import { useQuery, gql } from '@apollo/client';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
const GET_PROFILE = gql`
query GetProfile($username: String!) {
profile(username: $username) {
id
username
role
}
}
`;
export default function Profile() {
const router = useRouter();
const username = 'currentUsername'; // Replace with actual username retrieval logic
const { loading, error, data } = useQuery(GET_PROFILE, {
variables: { username },
});
useEffect(() => {
const token = localStorage.getItem('token');
if (!token) {
router.push('/login');
}
}, [router]);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Profile</h1>
<p>Username: {data.profile.username}</p>
<p>Role: {data.profile.role}</p>
</div>
);
}
Authentication Middleware
To protect the GraphQL API with JWT, you need to integrate a JWT strategy and middleware in NestJS. This includes creating a jwt.strategy.ts
, updating user.service.ts
to generate JWTs, and protecting routes using the GqlAuthGuard
.
This outline provides a solid foundation for implementing user management with registration, login, profile management, and role-based access control. You can expand it further based on specific project requirements and security best practices.
Support My Work ❤️
If you enjoy my content and find it valuable, consider supporting me by buying me a coffee. Your support helps me continue creating and sharing useful resources. Thank you!
Connect with Me 🌍
Let’s stay connected! You can follow me or reach out on these platforms:
🔹 YouTube – Tutorials, insights & tech content
🔹 LinkedIn – Professional updates & networking
🔹 GitHub – My open-source projects & contributions
🔹 Instagram – Behind-the-scenes & personal updates
🔹 X (formerly Twitter) – Quick thoughts & tech discussions
I’d love to hear from you—whether it’s feedback, collaboration ideas, or just a friendly hello!
Disclaimer
This content has been generated with the assistance of AI. While I strive for accuracy and quality, please verify critical information independently.
Top comments (0)