Thiết lập môi trường và cài đặt thư viện LibSQL
1. Cài đặt thư viện cho Node.js (TypeScript/JavaScript)
Bước đầu tiên là chuẩn bị thư viện libsql cho ứng dụng Node.js. Thư viện này là wrapper của LibSQL, cho phép kết nối trực tiếp đến Turso mà không cần qua proxy trung gian, tối ưu hóa độ trễ.
Thực hiện lệnh cài đặt trong thư mục dự án của bạn.
npm install @libsql/client
Kết quả mong đợi: Thư viện @libsql/client được thêm vào file package.json và thư mục node_modules.
2. Cài đặt thư viện cho Go
Đối với ứng dụng Go, chúng ta sử dụng driver libsql tương thích với database/sql tiêu chuẩn của Go, giúp dễ dàng tích hợp vào code hiện có.
Chạy lệnh sau trong terminal tại thư mục dự án Go.
go get github.com/tursodatabase/libsql-client-go/libsql
Kết quả mong đợi: Module được tải về và ghi vào file go.mod.
3. Cài đặt thư viện cho Python
Với Python, thư viện libsql cung cấp API tương tự sqlite3 tiêu chuẩn nhưng hỗ trợ kết nối mạng.
Cài đặt qua pip như sau.
pip install libsql
Kết quả mong đợi: Gói libsql được cài đặt vào môi trường Python hiện tại.
Cấu hình Connection String và Token
1. Lấy thông tin kết nối từ Turso CLI
Trước khi viết code, bạn cần xác định đường dẫn URL branch và Authentication Token. Token này đóng vai trò như mật khẩu để xác thực truy cập vào database cụ thể.
Chạy lệnh sau để hiển thị thông tin branch chính (main branch).
turso branch show
Kết quả mong đợi: CLI trả về một bảng thông tin, trong đó có cột URL (ví dụ: libsql://your-db.turso.io) và Branch (thường là main).
2. Tạo biến môi trường bảo mật
Tuyệt đối không hardcode token vào source code. Hãy tạo file .env tại thư mục gốc dự án để lưu trữ thông tin nhạy cảm.
File .env tại đường dẫn /var/www/app/.env cần có nội dung hoàn chỉnh như sau (thay thế các giá trị bằng thông tin thực tế của bạn):
TURSO_DATABASE_URL=libsql://your-database-name.turso.io
TURSO_AUTH_TOKEN=your-actual-auth-token-here
TURSO_BRANCH=main
Kết quả mong đợi: File .env được tạo, chứa 3 biến môi trường cần thiết. Đảm bảo file này đã được thêm vào .gitignore để không commit lên repo.
Triển khai mã nguồn CRUD mẫu (Node.js)
1. Thiết lập file cấu hình kết nối
Tạo file cấu hình riêng để khởi tạo connection. Điều này giúp tách biệt logic kết nối khỏi logic nghiệp vụ, dễ dàng maintain và test.
File db.js tại đường dẫn /var/www/app/db.js với nội dung:
import { createClient } from '@libsql/client';
import { config } from 'dotenv';
config();
export const client = createClient({
url: process.env.TURSO_DATABASE_URL,
authToken: process.env.TURSO_AUTH_TOKEN,
branch: process.env.TURSO_BRANCH || 'main',
});
Kết quả mong đợi: File db.js xuất hiện, xuất khẩu đối tượng client đã được cấu hình sẵn.
2. Viết mã nguồn CRUD (Create, Read, Update, Delete)
Tiếp theo, tạo file xử lý logic nghiệp vụ. Chúng ta sẽ thực hiện các thao tác trên bảng users đã tạo ở phần 4.
File crud.js tại đường dẫn /var/www/app/crud.js với nội dung hoàn chỉnh:
import { client } from './db.js';
import { config } from 'dotenv';
config();
async function runCRUD() {
try {
// 1. CREATE: Thêm mới một người dùng
console.log('Creating user...');
const insertResult = await client.execute({
sql: 'INSERT INTO users (name, email, created_at) VALUES (?, ?, datetime("now"))',
args: ['Nguyen Van A', 'nguyenvana@example.com']
});
console.log('Insert result:', insertResult);
// 2. READ: Đọc tất cả người dùng
console.log('Reading users...');
const selectResult = await client.execute({
sql: 'SELECT id, name, email FROM users'
});
console.log('Users:', selectResult.rows);
// 3. UPDATE: Cập nhật email của user đầu tiên
const firstUser = selectResult.rows[0];
if (firstUser) {
console.log('Updating user...');
const updateResult = await client.execute({
sql: 'UPDATE users SET email = ? WHERE id = ?',
args: ['updated_email@example.com', firstUser.id]
});
console.log('Update result:', updateResult);
}
// 4. DELETE: Xóa người dùng vừa tạo (dùng lại ID)
console.log('Deleting user...');
const deleteResult = await client.execute({
sql: 'DELETE FROM users WHERE id = ?',
args: [firstUser.id]
});
console.log('Delete result:', deleteResult);
// Kiểm tra lại xem đã xóa chưa
const verifyResult = await client.execute({
sql: 'SELECT count(*) as total FROM users'
});
console.log('Total users remaining:', verifyResult.rows[0].total);
} catch (error) {
console.error('Error during CRUD operations:', error.message);
process.exit(1);
} finally {
await client.close();
}
}
runCRUD();
Kết quả mong đợi: Khi chạy file này, terminal sẽ in ra log chi tiết của từng bước: thêm thành công, hiển thị danh sách, cập nhật thành công, xóa thành công và số lượng còn lại.
Xử lý lỗi kết nối và Timeout trong môi trường Serverless
1. Cấu hình Retry và Timeout
Môi trường Serverless (như Vercel, AWS Lambda) thường bị ngắt kết nối nếu request quá lâu hoặc gặp sự cố mạng nhất thời. LibSQL hỗ trợ cơ chế retry tự động, nhưng bạn cần cấu hình rõ ràng.
File db-configured.js tại đường dẫn /var/www/app/db-configured.js với cấu hình nâng cao:
import { createClient } from '@libsql/client';
import { config } from 'dotenv';
config();
export const client = createClient({
url: process.env.TURSO_DATABASE_URL,
authToken: process.env.TURSO_AUTH_TOKEN,
branch: process.env.TURSO_BRANCH || 'main',
// Cấu hình retry: thử lại tối đa 3 lần
maxRetries: 3,
// Thời gian chờ giữa các lần retry (ms)
retryBackoff: 1000,
// Timeout cho mỗi lần query (ms) - quan trọng với Serverless
timeout: 5000,
});
Kết quả mong đợi: Client được khởi tạo với các tham số bảo vệ, giúp ứng dụng không bị crash ngay lập tức khi mạng chập chờn.
2. Xử lý lỗi đặc thù (Error Handling)
Cần nắm rõ các mã lỗi phổ biến của LibSQL/Turso để xử lý đúng cách, ví dụ: lỗi ConnectionRefused, Timeout, hoặc AuthenticationFailed.
File error-handler.js tại đường dẫn /var/www/app/error-handler.js với nội dung:
import { client } from './db-configured.js';
import { config } from 'dotenv';
config();
async function safeQuery(sql, args = []) {
try {
const result = await client.execute({ sql, args });
return { success: true, data: result.rows };
} catch (error) {
// Kiểm tra mã lỗi cụ thể
if (error.code === 'ECONNREFUSED' || error.code === 'ETIMEDOUT') {
console.error('Network issue: Database unreachable or timed out.');
// Có thể trigger fallback logic ở đây
return { success: false, error: 'NETWORK_ERROR', message: error.message };
}
if (error.message.includes('authentication')) {
console.error('Auth issue: Token invalid or expired.');
return { success: false, error: 'AUTH_ERROR', message: error.message };
}
// Lỗi SQL thông thường (syntax, constraint)
console.error('SQL Error:', error.message);
return { success: false, error: 'SQL_ERROR', message: error.message };
}
}
// Test thử
(async () => {
// Test 1: Query đúng
const res1 = await safeQuery('SELECT 1 as test');
console.log('Test 1:', res1);
// Test 2: Query sai cú pháp
const res2 = await safeQuery('SELEC * FROM users'); // Sai cú pháp
console.log('Test 2:', res2);
await client.close();
})();
Kết quả mong đợi: Chương trình chạy, in ra kết quả thành công cho query đúng và trả về object lỗi có cấu trúc rõ ràng cho query sai, không làm sập toàn bộ ứng dụng.
Verify kết quả và kiểm thử
1. Chạy thử script CRUD
Để xác nhận mọi thứ hoạt động, chạy script CRUD đã viết ở trên.
node crud.js
Kết quả mong đợi: Bạn thấy dòng log Total users remaining: 0 (hoặc số lượng ban đầu nếu không xóa), chứng tỏ các thao tác CRUD đã thực thi thành công trên Turso.
2. Kiểm tra dữ liệu trên Dashboard Turso
Mở trình duyệt, truy cập vào dashboard Turso của bạn. Chọn database và vào phần Branch Explorer.
Kết quả mong đợi: Bạn thấy dữ liệu trong bảng users đã thay đổi tương ứng với các thao tác trong script (có thêm, có sửa, có xóa). Nếu script chạy xong và xóa hết, bảng sẽ trống.
3. Test xử lý lỗi mạng (Simulate Timeout)
Để kiểm tra cơ chế retry, bạn có thể tạm thời ngắt kết nối mạng hoặc thay đổi URL sai trong file .env rồi chạy lại script error-handler.js.
node error-handler.js
Kết quả mong đợi: Script không bị uncaught exception mà in ra log lỗi có cấu trúc (ví dụ: NETWORK_ERROR) và tiếp tục chạy đến bước finally hoặc đóng connection an toàn.
Điều hướng series:
Mục lục: Series: Triển khai Database Serverless với Turso và SQLite trên Ubuntu 24.04
« Phần 4: Thiết kế Schema và nhập dữ liệu mẫu
Phần 6: Tối ưu hóa hiệu năng và cân bằng tải (Replication) »