Tham chiếu Context - Tính năng nâng cao
Tham chiếu Context - Tính năng nâng cao Các tính năng context nâng cao gồm tải tệp lên, thông tin API, context dùng chung và truy cập package. Transaction Dùng await @TRANSACTION.run(async () => { ... }) khi các mutation qua repository phải cùng commit hoặc cùng rollback. Nó tươn
Tham chiếu Context - Tính năng nâng cao
Các tính năng context nâng cao gồm tải tệp lên, thông tin API, context dùng chung và truy cập package.
Transaction
Dùng await @TRANSACTION.run(async () => { ... }) khi các mutation qua repository phải cùng commit hoặc cùng rollback. Nó tương đương với $ctx.$transaction.run(...) và hoạt động trên database backend đang cấu hình.
await @TRANSACTION.run(async () => {
await @REPOS.orders.create({ data: @BODY.order });
await @REPOS.inventory.update({
id: @BODY.inventoryId,
data: { reserved: true }
});
});
Chỉ các thao tác repository mới thuộc transaction. Không được giả định @FETCH, @STORAGE, @CACHE, @SOCKET hoặc @TRIGGER có thể rollback; khi cần, chỉ thực hiện các side effect này sau khi transaction thành công.
Tải tệp lên
Truy cập thông tin về tệp đã tải lên.
$ctx.$uploadedFile
Thông tin về tệp đã tải lên (nếu có).
if ($ctx.$uploadedFile) {
const filename = $ctx.$uploadedFile.originalname;
const mimetype = $ctx.$uploadedFile.mimetype;
const size = $ctx.$uploadedFile.size;
const fieldname = $ctx.$uploadedFile.fieldname;
}
$ctx.$uploadedFile describes a multipart request file. It does not expose the file body as a buffer. Pass it directly to $ctx.$storage.$upload({ file: $ctx.$uploadedFile }) or $ctx.$storage.$update(id, { file: $ctx.$uploadedFile }) so Enfyra streams from the temp file path.
Xử lý tệp đã tải lên
if ($ctx.$uploadedFile) {
$ctx.$logs(`File uploaded: ${$ctx.$uploadedFile.originalname}`);
$ctx.$logs(`File size: ${$ctx.$uploadedFile.size} bytes`);
$ctx.$logs(`MIME type: ${$ctx.$uploadedFile.mimetype}`);
// Save request file using the streaming helper.
const fileResult = await $ctx.$storage.$upload({
file: $ctx.$uploadedFile,
description: $ctx.$body.description
});
}
Streaming response HTTP
Với endpoint proxy, dùng một server package đã cài đặt để lấy readable stream rồi truyền thẳng vào @RES.stream:
const upstream = await @PKGS.undici.request(@QUERY.url, { method: 'GET' });
await @RES.stream(upstream.body, {
mimetype: 'text/event-stream',
transform: (text, kind) => {
if (kind === 'end') return 'data: [DONE]\n\n';
return text.replace(/^data:/gm, 'event: converted\ndata:');
}
});
transform(text, kind) chạy trước khi relay mỗi chunk và lần end cuối. Return string để thay output, null để bỏ output, hoặc undefined để giữ nguyên. Nó dùng để điều chỉnh stream mà client nhận; lỗi bị ném sẽ làm stream thất bại. Enfyra dùng một UTF-8 decoder incremental cho toàn bộ stream trước khi gọi transform, nên ký tự bị cắt giữa các byte network vẫn còn nguyên trong text. Tuy vậy, text vẫn có thể kết thúc giữa một dòng SSE, JSON object hoặc event. Hãy giữ SSE buffer theo từng request và chỉ parse record kết thúc bằng dòng trống (\n\n hoặc dạng CRLF); không dùng kích thước chunk cố định hay dấu cách làm ranh giới. @HELPERS.$fetch buffer toàn bộ response nên không phù hợp với SSE hoặc chat streaming. Giữ authorization header của upstream ở server, chỉ chuyển tiếp response header an toàn, và không return payload khác sau khi @RES.stream bắt đầu.
Thông tin API
Truy cập thông tin chi tiết về request và response API hiện tại.
Thông tin request
const method = $ctx.$api.request.method; // HTTP method
const url = $ctx.$api.request.url; // Request URL
const timestamp = $ctx.$api.request.timestamp; // Request timestamp
const correlationId = $ctx.$api.request.correlationId; // Unique request ID
const userAgent = $ctx.$api.request.userAgent; // User agent
const ip = $ctx.$api.request.ip; // Client IP
Thông tin response
const statusCode = $ctx.$api.response.statusCode; // HTTP status code
const responseTime = $ctx.$api.response.responseTime; // Response time in ms
const timestamp = $ctx.$api.response.timestamp; // Response timestamp
Thông tin lỗi (chỉ trong postHook)
if ($ctx.$api.error) {
const message = $ctx.$api.error.message; // Error message
const stack = $ctx.$api.error.stack; // Stack trace
const name = $ctx.$api.error.name; // Error class name
const statusCode = $ctx.$api.error.statusCode; // HTTP error status
const timestamp = $ctx.$api.error.timestamp; // Error timestamp
const details = $ctx.$api.error.details; // Additional details
}
Context dùng chung
Lưu dữ liệu tồn tại xuyên suốt các hook và handler.
$ctx.$share
Vùng chứa dữ liệu dùng chung để truyền dữ liệu giữa các hook.
// In preHook - store data
$ctx.$share.validationPassed = true;
$ctx.$share.processStartTime = Date.now();
$ctx.$share.userId = $ctx.$user.id;
// In postHook - access shared data
if ($ctx.$share.validationPassed) {
const processingTime = Date.now() - $ctx.$share.processStartTime;
$ctx.$data.processingTime = processingTime;
}
Truy cập log
// All logs are stored in $ctx.$share.$logs
const allLogs = $ctx.$share.$logs; // Array of all logged messages
Truy cập môi trường
Đọc các giá trị môi trường không nhạy cảm từ bản chụp môi trường đã được lọc.
$ctx.$env
$ctx.$env exposes a sanitized copy of process.env for dynamic scripts. It is useful for non-secret runtime switches such as node names, deployment labels, public URLs, or feature flags.
const nodeName = $ctx.$env.NODE_NAME || 'default';
const appMode = $ctx.$env.ENFYRA_MODE || 'all';
Các key hạ tầng nhạy cảm không được expose. Danh sách chặn hiện tại gồm:
DB_URIDB_REPLICA_URISREDIS_URISECRET_KEYADMIN_PASSWORD
Không dùng $ctx.$env để lưu secret của ứng dụng. Hãy lưu secret vào field bảng chưa publish có isEncrypted=true, sau đó đọc/ghi plaintext qua repository hoặc REST như bình thường.
Field bảng được mã hóa gắn với SECRET_KEY của server. Khi self-host, hãy giữ SECRET_KEY ổn định, có backup và giống nhau trên mọi server instance của cùng một ứng dụng Enfyra. Thay đổi hoặc làm mất key này sẽ khiến dữ liệu đã mã hóa không thể giải mã.
Truy cập package
Truy cập các package NPM đã cài trong hệ thống.
$ctx.$pkgs
Truy cập package đã cài theo tên.
// Use installed packages
const axios = $ctx.$pkgs.axios;
const lodash = $ctx.$pkgs.lodash;
const moment = $ctx.$pkgs.moment;
// Example: Using axios
if ($ctx.$pkgs.axios) {
const response = await $ctx.$pkgs.axios.get('https://api.example.com/data');
}
Lưu ý: Package phải được cài qua Package Management trên UI thì mới truy cập được.
Mẫu phổ biến
Mẫu 1: Xác thực và biến đổi dữ liệu request
// Validate required fields
if (!$ctx.$body.email) {
$ctx.$throw['400']('Email is required');
return;
}
// Transform data
$ctx.$body.email = $ctx.$body.email.toLowerCase().trim();
Mẫu 2: Kiểm tra quyền người dùng
if (!$ctx.$user) {
$ctx.$throw['401']('Authentication required');
return;
}
if ($ctx.$user.role !== 'admin') {
$ctx.$throw['403']('Admin access required');
return;
}
Mẫu 3: Dùng context chung giữa các hook
// In preHook
$ctx.$share.userId = $ctx.$user.id;
$ctx.$share.validationData = { passed: true };
// In postHook
const userId = $ctx.$share.userId;
if ($ctx.$share.validationData.passed) {
// Use validation result
}
Mẫu 4: Cache dữ liệu được truy cập thường xuyên
// Check cache first
const cacheKey = `user:${$ctx.$params.id}`;
let user = await $ctx.$cache.get(cacheKey);
if (!user) {
// Cache miss - fetch from database
const result = await $ctx.$repos.enfyra_user.find({
filter: { id: { _eq: $ctx.$params.id } }
});
if (result.data.length > 0) {
user = result.data[0];
// Cache for 5 minutes
await $ctx.$cache.set(cacheKey, user, 300000);
}
}
Mẫu 5: Xử lý lỗi trong postHook
// In postHook
if ($ctx.$api.error) {
// Log error
$ctx.$logs(`Error: ${$ctx.$api.error.message}`);
// Log to audit system
await $ctx.$repos.audit_logs.create({
data: {
action: 'error_occurred',
userId: $ctx.$user?.id,
errorMessage: $ctx.$api.error.message,
statusCode: $ctx.$api.error.statusCode,
timestamp: new Date()
}
});
} else {
// Success - add metadata
$ctx.$data.processedAt = new Date();
$ctx.$data.processedBy = $ctx.$user?.id;
}
Tiếp theo
- Xem Xử lý tệp để biết hướng dẫn tải tệp đầy đủ
- Xem Thao tác cache để biết các mẫu cache chi tiết
- Tìm hiểu Xử lý lỗi để biết các mẫu xử lý lỗi