Spaces:
Sleeping
API Documentation — Spaces & Rooms
Tài liệu dành cho Frontend để tích hợp API Spaces và Rooms.
Base URL
/api
Authentication
Tất cả endpoints yêu cầu header:
Authorization: Bearer <access_token>
Content-Type: application/json
1. Spaces API (/spaces)
1.1 Lấy danh sách spaces của user
GET /spaces
Response:
{
"success": true,
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Lớp Toán 12A",
"description": "Không gian học tập lớp 12A",
"icon_url": "https://...",
"owner_id": "user-uuid",
"is_private": true,
"invite_code": "abc123",
"created_at": "2026-05-01T10:00:00Z",
"updated_at": "2026-05-01T10:00:00Z"
}
]
}
1.2 Tạo space mới
POST /spaces
Request Body:
{
"name": "Lớp Toán 12A",
"description": "Không gian học tập",
"icon": "https://...",
"isPrivate": true
}
| Field | Type | Required | Constraints |
|---|---|---|---|
name |
string | ✅ | Min 2, Max 100 chars |
description |
string | ❌ | Max 500 chars |
icon |
string (URL) | ❌ | — |
isPrivate |
boolean | ❌ | Default: false |
Response:
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Lớp Toán 12A",
"description": "Không gian học tập",
"icon_url": "https://...",
"owner_id": "user-uuid",
"is_private": true,
"invite_code": "abc123",
"created_at": "2026-05-01T10:00:00Z"
}
}
1.3 Lấy chi tiết space
GET /spaces/:spaceId
Params:
spaceId(UUID) — ID của space
Response:
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Lớp Toán 12A",
"description": "Không gian học tập",
"icon_url": "https://...",
"owner_id": "user-uuid",
"is_private": true,
"invite_code": "abc123",
"created_at": "2026-05-01T10:00:00Z"
}
}
Lỗi:
403 Forbidden— User không phải member của space private404 Not Found— Space không tồn tại
1.4 Cập nhật space
PATCH /spaces/:spaceId
Request Body:
{
"name": "Lớp Toán 12A (Updated)",
"description": "Mô tả mới",
"icon": "https://...",
"isPrivate": false
}
Lưu ý: Chỉ owner hoặc admin mới có quyền cập nhật. Tất cả fields optional.
Response:
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Lớp Toán 12A (Updated)",
...
}
}
1.5 Xóa space
DELETE /spaces/:spaceId
Lưu ý: Chỉ owner mới có quyền xóa. Trả về
204 No Content.
1.6 Tìm kiếm spaces công khai
GET /spaces/search?q=toán
Query Params:
q(string) — Từ khóa tìm kiếm (tối đa 100 ký tự)
Response:
{
"success": true,
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Lớp Toán 12A",
"description": "...",
"is_private": false
}
]
}
1.7 Lấy danh sách rooms trong space
GET /spaces/:spaceId/rooms
Response:
{
"success": true,
"data": [
{
"id": "room-uuid-1",
"space_id": "space-uuid",
"name": "Thông báo",
"description": "Kênh thông báo chính",
"type": "text",
"is_private": false,
"created_at": "2026-05-01T10:00:00Z"
},
{
"id": "room-uuid-2",
"space_id": "space-uuid",
"name": "Tài liệu",
"description": "Chia sẻ tài liệu",
"type": "text",
"is_private": false,
"created_at": "2026-05-01T10:00:00Z"
}
]
}
1.8 Thêm member vào space
POST /spaces/:spaceId/members
Request Body:
{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"role": "member"
}
| Field | Type | Required | Values |
|---|---|---|---|
userId |
UUID | ✅ | — |
role |
string | ❌ | "member" (default), "admin" |
Lưu ý: Chỉ owner hoặc admin mới có quyền thêm member.
Response:
{
"success": true,
"data": {
"id": "member-id",
"space_id": "space-uuid",
"user_id": "user-uuid",
"role": "member",
"joined_at": "2026-05-01T10:00:00Z"
}
}
1.9 Tạo mã mờispace
POST /spaces/:spaceId/invite
Lưu ý: Chỉ owner hoặc admin mới có quyền tạo mã mờ.
Response:
{
"success": true,
"data": {
"inviteCode": "abc123"
}
}
1.10 Tham gia space bằng mã mờ
POST /spaces/join/:code
Params:
code(string) — Mã mờ
Response:
{
"success": true,
"data": {
"id": "space-uuid",
"name": "Lớp Toán 12A",
...
}
}
1.11 Rờikhỏi space
POST /spaces/:spaceId/leave
Lưu ý: Owner không thể rờikhỏi space. Trả về
204 No Content.
2. Rooms API
2.1 Tạo room trong space
POST /spaces/:spaceId/rooms
Request Body:
{
"name": "Thảo luận",
"description": "Phòng thảo luận bài tập",
"type": "text",
"isPrivate": false
}
| Field | Type | Required | Values | Default |
|---|---|---|---|---|
name |
string | ✅ | Min 2, Max 100 chars | — |
description |
string | ❌ | Max 500 chars | — |
type |
string | ❌ | "text", "voice" |
"text" |
isPrivate |
boolean | ❌ | — | false |
Lưu ý: Chỉ space member mới có quyền tạo room.
Response:
{
"success": true,
"data": {
"id": "room-uuid",
"space_id": "space-uuid",
"name": "Thảo luận",
"description": "Phòng thảo luận bài tập",
"type": "text",
"is_private": false,
"created_at": "2026-05-01T10:00:00Z"
}
}
2.2 Lấy chi tiết room
GET /rooms/:roomId
Response:
{
"success": true,
"data": {
"id": "room-uuid",
"space_id": "space-uuid",
"name": "Thảo luận",
"description": "Phòng thảo luận bài tập",
"type": "text",
"is_private": false,
"created_at": "2026-05-01T10:00:00Z"
}
}
Lỗi:
403 Forbidden— User không phải member của room
2.3 Cập nhật room
PATCH /rooms/:roomId
Request Body:
{
"name": "Thảo luận (Updated)",
"description": "Mô tả mới",
"type": "text",
"isPrivate": true
}
Lưu ý: Chỉ room creator hoặc space admin mới có quyền cập nhật. Tất cả fields optional.
Response:
{
"success": true,
"data": {
"id": "room-uuid",
"name": "Thảo luận (Updated)",
...
}
}
2.4 Xóa room
DELETE /rooms/:roomId
Lưu ý: Chỉ room creator hoặc space admin mới có quyền xóa.
Response:
{
"success": true,
"message": "Room deleted successfully"
}
2.5 Lấy danh sách rooms trong space
GET /spaces/:spaceId/rooms
Cùng chức năng với 1.7, trả về danh sách rooms trong space.
2.6 Lấy danh sách members trong room
GET /rooms/:roomId/members
Response:
{
"success": true,
"data": [
"user-id-1",
"user-id-2",
"user-id-3"
]
}
2.7 Thêm member vào room
POST /rooms/:roomId/members
Request Body:
{
"userId": "550e8400-e29b-41d4-a716-446655440000"
}
| Field | Type | Required |
|---|---|---|
userId |
UUID | ✅ |
Lưu ý:
- User phải là member của space trước
- Chỉ room creator hoặc space admin mới có quyền thêm
Response:
{
"success": true,
"message": "Member added successfully"
}
2.8 Xóa member khỏi room
DELETE /rooms/:roomId/members/:userId
Lưu ý: Chỉ room creator hoặc space admin mới có quyền xóa.
Response:
{
"success": true,
"message": "Member removed successfully"
}
2.9 Lấy thống kê room
GET /rooms/:roomId/stats
Response:
{
"success": true,
"data": {
"memberCount": 25,
"messageCount": 1200,
"lastActivity": "2026-05-04T10:00:00Z"
}
}
3. Members API (/spaces/:spaceId/members)
3.1 Lấy danh sách members trong space
GET /spaces/:spaceId/members
Response:
{
"success": true,
"data": [
{
"id": "user-id-1",
"email": "alice@example.com",
"username": "alice",
"displayName": "Alice",
"avatar": "https://...",
"status": "online",
"role": "owner",
"joinedAt": "2026-05-01T10:00:00Z"
},
{
"id": "user-id-2",
"email": "bob@example.com",
"username": "bob",
"displayName": "Bob",
"avatar": "https://...",
"status": "offline",
"role": "member",
"joinedAt": "2026-05-02T10:00:00Z"
}
]
}
3.2 Tìm kiếm members trong space
GET /spaces/:spaceId/members/search?q=alice
Query Params:
q(string) — Từ khóa tìm kiếm username/display_name/email
Response:
{
"success": true,
"data": [
{
"id": "user-id-1",
"email": "alice@example.com",
"username": "alice",
"displayName": "Alice",
"avatar": "https://...",
"status": "online",
"role": "owner",
"joinedAt": "2026-05-01T10:00:00Z"
}
]
}
3.3 Lấy role của member
GET /spaces/:spaceId/members/:userId/role
Response:
{
"success": true,
"data": {
"role": "admin"
}
}
3.4 Cập nhật role member
PATCH /spaces/:spaceId/members/:userId/role
Request Body:
{
"role": "admin"
}
| Field | Type | Required | Values |
|---|---|---|---|
role |
string | ✅ | "member", "admin" |
Lưu ý:
- Chỉ owner mới có quyền promote lên admin
- Không thể thay đổi role của owner
Response:
{
"success": true,
"data": {
"id": "member-id",
"space_id": "space-uuid",
"user_id": "user-uuid",
"role": "admin",
"joined_at": "2026-05-01T10:00:00Z"
}
}
3.5 Lấy activity của member
GET /spaces/:spaceId/members/:userId/activity
Response:
{
"success": true,
"data": {
"lastActive": "2026-05-04T10:00:00Z",
"messageCount": 150,
"reactionCount": 45
}
}
3.6 Xóa member khỏi space
DELETE /spaces/:spaceId/members/:userId
Lưu ý:
- Chỉ owner hoặc admin mới có quyền xóa
- Admin không thể xóa owner
- Owner không thể bị xóa
- Member có thể tự xóa chính mình (leave)
Response:
{
"success": true,
"message": "Member removed successfully"
}
4. Error Responses
Format lỗi chuẩn
{
"success": false,
"message": "Error description",
"error": "ERROR_CODE"
}
Các mã lỗi phổ biến
| Status | Code | Mô tả |
|---|---|---|
400 |
BAD_REQUEST |
Request không hợp lệ (validation error) |
401 |
UNAUTHORIZED |
Token hết hạn hoặc không hợp lệ |
403 |
FORBIDDEN |
Không có quyền thực hiện |
404 |
NOT_FOUND |
Resource không tồn tại |
409 |
CONFLICT |
Conflict (đã tồn tại, v.v.) |
429 |
RATE_LIMIT |
Quá nhiều requests |
Ví dụ lỗi 403
{
"statusCode": 403,
"message": "You are not a member of this space",
"error": "Forbidden"
}
Ví dụ lỗi 400 (Validation)
{
"statusCode": 400,
"message": ["name must be longer than or equal to 2 characters"],
"error": "Bad Request"
}
5. DTO Reference
CreateSpaceDto
{
name: string; // required, min 2, max 100 chars
description?: string; // optional, max 500 chars
icon?: string; // optional, URL
isPrivate?: boolean; // optional, default: false
}
UpdateSpaceDto
{
name?: string; // optional, min 2, max 100 chars
description?: string; // optional, max 500 chars
icon?: string; // optional, URL
isPrivate?: boolean; // optional
}
CreateRoomDto
{
name: string; // required, min 2, max 100 chars
description?: string; // optional, max 500 chars
type?: string; // optional, default: "text" ("text" | "voice")
isPrivate?: boolean; // optional, default: false
}
UpdateRoomDto
{
name?: string;
description?: string;
type?: string; // "text" | "voice"
isPrivate?: boolean;
}
AddMemberDto
{
userId: string; // required, UUID
role?: string; // optional, default: "member" ("member" | "admin")
}
AddRoomMemberDto
{
userId: string; // required, UUID
}
UpdateRoleDto
{
role: string; // required, "member" | "admin"
}
6. Flow gợi ý cho Frontend
Tạo space mới
async function createSpace(name: string, description?: string) {
const response = await fetch('/api/spaces', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ name, description, isPrivate: false }),
});
return response.json();
}
Lấy danh sách spaces và rooms
async function loadSpacesAndRooms() {
// 1. Lấy danh sách spaces
const spacesRes = await fetch('/api/spaces', {
headers: { 'Authorization': `Bearer ${token}` },
});
const { data: spaces } = await spacesRes.json();
// 2. Với mỗi space, lấy rooms
for (const space of spaces) {
const roomsRes = await fetch(`/api/spaces/${space.id}/rooms`, {
headers: { 'Authorization': `Bearer ${token}` },
});
const { data: rooms } = await roomsRes.json();
space.rooms = rooms;
}
return spaces;
}
Tham gia room qua WebSocket
import { io } from 'socket.io-client';
const socket = io('wss://api.example.com/chat', {
auth: { token: accessToken },
});
// Join room
socket.emit('joinRoom', { roomId: 'room-uuid' });
// Listen events
socket.on('joinedRoom', (data) => {
console.log('Joined room:', data);
});
socket.on('newMessage', (message) => {
console.log('New message:', message);
});
Thêm member vào space
async function addMember(spaceId: string, userId: string, role: string = 'member') {
const response = await fetch(`/api/spaces/${spaceId}/members`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ userId, role }),
});
return response.json();
}
7. Permissions Summary
| Action | Who can do it |
|---|---|
| Create space | Any authenticated user |
| Update space | Owner, Admin |
| Delete space | Owner only |
| Add member | Owner, Admin |
| Remove member | Owner, Admin (except owner) |
| Update member role | Owner only |
| Create room | Space member |
| Update room | Room creator, Space admin |
| Delete room | Room creator, Space admin |
| Add room member | Room creator, Space admin |
| Remove room member | Room creator, Space admin |
| Generate invite code | Owner, Admin |
| Join by invite code | Any authenticated user |
| Leave space | Member (not owner) |
8. Cache Notes
| Endpoint | Cache |
|---|---|
GET /spaces/:spaceId/rooms |
private, max-age=30 |
GET /rooms/:roomId/members |
Redis cache 5 phút |
GET /spaces/:spaceId/members |
Redis cache 5 phút |
Frontend nên implement stale-while-revalidate cho danh sách spaces/rooms.