Backend API cho hệ thống đánh giá nhân viên
http://localhost:4000
� Tip:n Trong production, thay bằng domain thật của bạn
Hầu hết các endpoints yêu cầu JWT token trong header:
Authorization: Bearer <your_jwt_token>| Role | Username | Password | Permissions |
|---|---|---|---|
| 👔 Supervisor | manager |
123456 |
Xem employees, tạo/xem assessments |
| 👤 Employee | sarah.johnson |
123456 |
Xem assessments của mình |
🎯 Mục đích: Đăng nhập và nhận JWT token
Endpoint:
POST /api/auth/loginRequest Body:
{
"username": "manager",
"password": "123456"
}Response Success (200):
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "507f1f77bcf86cd799439011",
"username": "manager",
"fullName": "Quản lý Hệ thống",
"email": "manager@company.com",
"department": "Engineering",
"position": "Engineering Manager",
"role": "supervisor"
}
}Response Error (400):
{
"message": "Invalid credentials"
}Ví dụ Frontend:
const response = await fetch("http://localhost:4000/api/auth/login", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
username: "manager",
password: "123456"
})
});
const data = await response.json();
if (data.token) {
localStorage.setItem("token", data.token);
localStorage.setItem("user", JSON.stringify(data.user));
}🎯 Mục đích: Lấy danh sách tất cả nhân viên
🔒 Quyền: Chỉ Supervisor
Endpoint:
GET /api/employeesHeaders:
Authorization: Bearer <supervisor_token>
Response Success (200):
[
{
"_id": "507f1f77bcf86cd799439011",
"username": "sarah.johnson",
"fullName": "Sarah Johnson",
"email": "sarah.johnson@company.com",
"department": "Engineering",
"position": "Senior Software Engineer",
"role": "employee",
"createdAt": "2024-11-05T08:30:15.123Z",
"updatedAt": "2024-11-05T08:30:15.123Z"
}
]Response Error (401):
{
"message": "No token"
}Response Error (403):
{
"message": "Forbidden"
}Ví dụ Frontend:
const token = localStorage.getItem("token");
const response = await fetch("http://localhost:4000/api/employees", {
headers: {
"Authorization": `Bearer ${token}`
}
});
const employees = await response.json();🎯 Mục đích: Tạo nhân viên mới
🔒 Quyền: Chỉ Supervisor
Endpoint:
POST /api/employeesHeaders:
Authorization: Bearer <supervisor_token>
Response (501):
{
"message": "Implement create employee later"
}🎯 Mục đích: Tạo đánh giá mới cho nhân viên
🔒 Quyền: Chỉ Supervisor
Endpoint:
POST /api/assessmentsHeaders:
Authorization: Bearer <supervisor_token>
Content-Type: application/json
Request Body:
{
"employee": "507f1f77bcf86cd799439011",
"period": "quarterly",
"cycleLabel": "Q4 2024",
"criteria": [
{
"key": "technical",
"label": "Kỹ năng chuyên môn",
"score": 4
},
{
"key": "communication",
"label": "Kỹ năng giao tiếp",
"score": 5
},
{
"key": "teamwork",
"label": "Làm việc nhóm",
"score": 4
}
],
"comment": "Nhân viên làm việc tốt, có tinh thần trách nhiệm cao.",
"nextGoals": "Học thêm về microservices architecture",
"overall": 4.3
}Field Descriptions:
employee(required): ID của nhân viên được đánh giáperiod(required): Chu kỳ đánh giá - enum:"biweekly","monthly","quarterly","yearly"cycleLabel(required): Nhãn chu kỳ (ví dụ: "Q4 2024", "January 2025")criteria(optional): Mảng các tiêu chí đánh giákey: Mã tiêu chílabel: Tên hiển thịscore: Điểm từ 0-5
comment(optional): Nhận xét chi tiếtnextGoals(optional): Mục tiêu cho kỳ tiếp theooverall(optional): Điểm tổng thể từ 0-5
Response Success (201):
{
"_id": "507f191e810c19729de860ea",
"employee": "507f1f77bcf86cd799439011",
"supervisor": "507f1f77bcf86cd799439012",
"period": "quarterly",
"cycleLabel": "Q4 2024",
"criteria": [
{
"key": "technical",
"label": "Kỹ năng chuyên môn",
"score": 4
}
],
"comment": "Nhân viên làm việc tốt...",
"nextGoals": "Học thêm về microservices...",
"overall": 4.3,
"createdAt": "2024-11-05T08:30:15.123Z",
"updatedAt": "2024-11-05T08:30:15.123Z"
}Response Error (401/403):
{
"message": "Unauthorized" // hoặc "Forbidden"
}Ví dụ Frontend:
const token = localStorage.getItem("token");
const response = await fetch("http://localhost:4000/api/assessments", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
employee: "507f1f77bcf86cd799439011",
period: "quarterly",
cycleLabel: "Q4 2024",
criteria: [
{ key: "technical", label: "Kỹ năng chuyên môn", score: 4 },
{ key: "communication", label: "Kỹ năng giao tiếp", score: 5 }
],
comment: "Làm việc tốt",
nextGoals: "Học thêm về microservices",
overall: 4.5
})
});
const assessment = await response.json();🎯 Mục đích: Lấy tất cả đánh giá của chính mình
🔒 Quyền: Chỉ Employee
Endpoint:
GET /api/assessments/meHeaders:
Authorization: Bearer <employee_token>
Response Success (200):
[
{
"_id": "507f191e810c19729de860ea",
"employee": "507f1f77bcf86cd799439011",
"supervisor": "507f1f77bcf86cd799439012",
"period": "quarterly",
"cycleLabel": "Q4 2024",
"criteria": [
{
"key": "technical",
"label": "Kỹ năng chuyên môn",
"score": 4
}
],
"comment": "Nhân viên làm việc tốt...",
"nextGoals": "Học thêm về microservices...",
"overall": 4.3,
"createdAt": "2024-11-05T08:30:15.123Z",
"updatedAt": "2024-11-05T08:30:15.123Z"
}
]Response Error (401/403):
{
"message": "Unauthorized" // hoặc "Forbidden"
}Ví dụ Frontend:
const token = localStorage.getItem("token");
const response = await fetch("http://localhost:4000/api/assessments/me", {
headers: {
"Authorization": `Bearer ${token}`
}
});
const myAssessments = await response.json();Lấy tất cả đánh giá của một nhân viên (chỉ supervisor).
Endpoint: GET /api/assessments/employee/:id
URL Parameters:
id: ID của nhân viên
Headers:
Authorization: Bearer <supervisor_token>
Response Success (200):
[
{
"_id": "507f191e810c19729de860ea",
"employee": "507f1f77bcf86cd799439011",
"supervisor": "507f1f77bcf86cd799439012",
"period": "quarterly",
"cycleLabel": "Q4 2024",
"criteria": [...],
"comment": "...",
"nextGoals": "...",
"overall": 4.3,
"createdAt": "2024-11-05T08:30:15.123Z",
"updatedAt": "2024-11-05T08:30:15.123Z"
}
]Response Error (401/403):
{
"message": "Unauthorized" // hoặc "Forbidden"
}Ví dụ Frontend:
const token = localStorage.getItem("token");
const employeeId = "507f1f77bcf86cd799439011";
const response = await fetch(
`http://localhost:4000/api/assessments/employee/${employeeId}`,
{
headers: {
"Authorization": `Bearer ${token}`
}
}
);
const assessments = await response.json();| Code | Meaning | Khi nào xảy ra |
|---|---|---|
| 200 | OK | Request thành công |
| 201 | Created | Tạo mới thành công |
| 400 | Bad Request | Dữ liệu không hợp lệ (sai username/password) |
| 401 | Unauthorized | Chưa đăng nhập hoặc token không hợp lệ |
| 403 | Forbidden | Đã đăng nhập nhưng không có quyền |
| 404 | Not Found | Endpoint không tồn tại |
| 500 | Internal Server Error | Lỗi server |
| 501 | Not Implemented | Chức năng chưa được implement |
Tất cả errors đều trả về format:
{
"message": "Error description"
}Ví dụ xử lý lỗi trong Frontend:
try {
const response = await fetch("http://localhost:4000/api/employees", {
headers: {
"Authorization": `Bearer ${token}`
}
});
if (!response.ok) {
const error = await response.json();
if (response.status === 401) {
// Token hết hạn hoặc không hợp lệ
localStorage.removeItem("token");
window.location.href = "/login";
} else if (response.status === 403) {
// Không có quyền
alert("Bạn không có quyền truy cập");
} else {
alert(error.message);
}
return;
}
const data = await response.json();
// Xử lý data...
} catch (error) {
console.error("Network error:", error);
alert("Không thể kết nối tới server");
}// utils/api.js
const API_BASE_URL = "http://localhost:4000";
export async function apiCall(endpoint, options = {}) {
const token = localStorage.getItem("token");
const config = {
...options,
headers: {
"Content-Type": "application/json",
...options.headers,
}
};
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
const response = await fetch(`${API_BASE_URL}${endpoint}`, config);
if (response.status === 401) {
localStorage.removeItem("token");
window.location.href = "/login";
throw new Error("Unauthorized");
}
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || "Request failed");
}
return response.json();
}
// Sử dụng:
const employees = await apiCall("/api/employees");
const assessment = await apiCall("/api/assessments", {
method: "POST",
body: JSON.stringify({ employee: "...", ... })
});// hooks/useEmployees.js
import { useState, useEffect } from "react";
import { apiCall } from "../utils/api";
export function useEmployees() {
const [employees, setEmployees] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchEmployees() {
try {
const data = await apiCall("/api/employees");
setEmployees(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
fetchEmployees();
}, []);
return { employees, loading, error };
}
// Sử dụng trong component:
function EmployeeList() {
const { employees, loading, error } = useEmployees();
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<ul>
{employees.map(emp => (
<li key={emp._id}>{emp.fullName}</li>
))}
</ul>
);
}POST http://localhost:4000/api/auth/login
Body (JSON):
{
"username": "manager",
"password": "123456"
}
→ Copy token từ response
GET http://localhost:4000/api/employees
Headers:
Authorization: Bearer <paste_token_here>
POST http://localhost:4000/api/assessments
Headers:
Authorization: Bearer <paste_token_here>
Content-Type: application/json
Body (JSON):
{
"employee": "507f1f77bcf86cd799439011",
"period": "quarterly",
"cycleLabel": "Q4 2024",
"criteria": [
{ "key": "technical", "label": "Kỹ năng chuyên môn", "score": 4 }
],
"overall": 4
}
-
Token Expiration: JWT token hết hạn sau 8 giờ. Frontend cần handle 401 error và redirect về login.
-
CORS: Backend chỉ chấp nhận requests từ
http://localhost:5173. Nếu frontend chạy port khác, cần update trongserver.js. -
Role-based Access:
supervisor: Có thể xem danh sách employees, tạo assessments, xem assessments của bất kỳ employee nàoemployee: Chỉ có thể xem assessments của chính mình
-
Data Validation: Backend tự động validate:
- Required fields
- Enum values (period, role)
- Number ranges (score: 0-5, overall: 0-5)
- Unique constraints (username)
-
Timestamps: Mọi documents đều có
createdAtvàupdatedAttự động.
Nếu có vấn đề hoặc cần thêm endpoints, liên hệ Backend Team.