Call MySQL Database
Overview
Cloud Functions provide multiple methods to connect and operate MySQL databases, meeting application requirements in different scenarios. Choose the appropriate connection method based on your database deployment location and network environment:
| Connection Method | Use Case | Advantages |
|---|---|---|
| CloudBase MySQL (Recommended) | Use the built-in MySQL database in CloudBase environment | Simple configuration, automatic VPC direct connection |
| VPC Interconnection | Connect to other MySQL instances in Tencent Cloud | High performance, low latency, secure |
| Public Network Connection | Connect to any publicly accessible MySQL instance | High flexibility, wide applicability |
📄️ Quick Start
Connect to CloudBase MySQL database in 5 minutes
📄️ Connection Methods Explained
Learn about configuration steps and applicable scenarios for three connection methods
📄️ Database Operations
Learn how to perform CRUD operations in Cloud Functions
📄️ Best Practices
Practical tips for connection pool optimization, error handling, transaction management, etc.
📄️ Common Issues
Solve common issues like connection timeout, performance optimization, etc.
Quick Start
Step 1: Prepare the Database
- CloudBase MySQL (Recommended)
- Tencent Cloud MySQL
- Public Network MySQL
- Log in to CloudBase Platform / MySQL Database
- Choose according to your situation:
- Existing Database: Refer to the MySQL Migration to User Account documentation to migrate the database to your own Tencent Cloud account. After migration, VPC internal network connection is supported
- First-time Use: The system will prompt you to initialize the database. Select the VPC and subnet, then confirm
- Copy the "Internal Network Connection Address" from the Database Settings page
Format: mysql://root:password@internal-address:3306/tcb
- Log in to Tencent Cloud MySQL Console
- Ensure the MySQL instance and Cloud Functions are in the same region (Shanghai recommended)
- Record the instance's internal network address, port, username, and password
- Ensure the MySQL database has public network access enabled
- Configure firewall rules to allow Cloud Function access
- Record the database's public network address, port, username, and password
Public network connections pose security risks. For production environments, it's recommended to use internal network connections
Step 2: Install Database Driver
Install mysql2 in your Cloud Function project:
- npm
- yarn
- pnpm
npm install mysql2 --save
yarn add mysql2
pnpm add mysql2
Step 3: Configure Network Connection
- CloudBase MySQL
- Tencent Cloud MySQL
- Public Network MySQL
- Go to CloudBase Platform / Cloud Functions
- Select the Cloud Function and go to the "Function Configuration" page
- Enable "Advanced Configuration / Private Network"
- Select the VPC where the CloudBase MySQL database is located
After configuring VPC, Cloud Functions cannot access the public network by default. To access both public and internal networks simultaneously, refer to VPC Access Configuration
- Go to CloudBase Platform / Cloud Functions
- Select the Cloud Function and go to the "Function Configuration" page
- Enable "Advanced Configuration / Private Network"
- Select the target VPC (the VPC where the MySQL instance is located)
No network configuration required. Cloud Functions can access the public network by default
Step 4: Write Cloud Function Code
const mysql = require('mysql2/promise');
// Global connection pool
let pool;
function getPool() {
if (!pool) {
pool = mysql.createPool({
host: process.env.DB_HOST,
port: process.env.DB_PORT || 3306,
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME || 'tcb',
waitForConnections: true,
connectionLimit: 5,
queueLimit: 0,
acquireTimeout: 60000,
timeout: 60000,
charset: 'utf8mb4'
});
}
return pool;
}
exports.main = async (event, context) => {
try {
const pool = getPool();
const connection = await pool.getConnection();
try {
const [rows] = await connection.query('SELECT * FROM persons LIMIT 10');
return { success: true, data: rows };
} finally {
connection.release();
}
} catch (error) {
console.error('Database operation failed:', error);
return { success: false, error: error.message };
}
};
Step 5: Configure Environment Variables
Configure in the "Environment Variables" of your Cloud Function:
| Environment Variable | Description | Example Value |
|---|---|---|
DB_HOST | Database address | gz-xxxxx.mysql.tencentcdb.com |
DB_PORT | Port | 3306 |
DB_USER | Username | root |
DB_PASSWORD | Password | your_password |
DB_NAME | Database name | tcb |
You can also configure the CONNECTION_URI environment variable to use a complete connection string directly
Step 6: Test Connection
After deployment, click the "Test" button in the Cloud Function console
If the connection is successful, it will return records from the database.
Connection Methods Explained
Configuration Differences Comparison
The core difference among the three connection methods lies only in network configuration; the code implementation is identical:
| Connection Method | Network Configuration | Database Address | Use Case |
|---|---|---|---|
| CloudBase MySQL | Configure VPC (VPC where database is located) | Internal network address | Preferred for CloudBase projects |
| VPC Interconnection | Enable VPC Interconnection (target VPC) | Internal network address | For existing Tencent Cloud MySQL |
| Public Network Connection | No configuration required | Public network address | Third-party or self-built databases |
- CloudBase MySQL: Use "Quick Start" directly, simplest method
- Tencent Cloud MySQL: Only need to adjust network configuration to "VPC Interconnection", other steps are the same
- Public Network MySQL: No network configuration required, but ensure database has public network access enabled
Public Network Connection Special Notes
Security Configuration
Public network connections pose security risks. Please ensure:
- Use strong passwords
- Configure firewall rules to restrict access sources
- Regularly update database versions and security patches
- For production environments, it's recommended to use internal network connections
Sample Table Structure
If you need to create a test table, you can refer to the following SQL:
CREATE TABLE persons (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
age INT NOT NULL,
email VARCHAR(255) UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- Insert test data
INSERT INTO persons (name, age, email) VALUES
('Zhang San', 25, 'zhangsan@example.com'),
('Li Si', 30, 'lisi@example.com');
Database Operations
Basic CRUD Operations
Implement complete Create, Read, Update, Delete operations in Cloud Functions:
const mysql = require('mysql2/promise');
let pool;
function getPool() {
if (!pool) {
pool = mysql.createPool({
host: process.env.DB_HOST,
port: process.env.DB_PORT || 3306,
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME || 'tcb',
connectionLimit: 5,
waitForConnections: true,
charset: 'utf8mb4'
});
}
return pool;
}
exports.main = async (event, context) => {
const { action, data } = event;
try {
const pool = getPool();
const connection = await pool.getConnection();
try {
let result;
switch (action) {
case 'list':
// Query list
const [rows] = await connection.query(
'SELECT * FROM persons ORDER BY created_at DESC LIMIT 10'
);
result = { success: true, data: rows };
break;
case 'create':
// Create record
const { name, age, email } = data;
const [insertResult] = await connection.query(
'INSERT INTO persons (name, age, email) VALUES (?, ?, ?)',
[name, age, email]
);
result = {
success: true,
data: { id: insertResult.insertId, name, age, email }
};
break;
case 'update':
// Update record
const { id, ...updateData } = data;
const [updateResult] = await connection.query(
'UPDATE persons SET ? WHERE id = ?',
[updateData, id]
);
result = {
success: true,
affectedRows: updateResult.affectedRows
};
break;
case 'delete':
// Delete record
const [deleteResult] = await connection.query(
'DELETE FROM persons WHERE id = ?',
[data.id]
);
result = {
success: true,
affectedRows: deleteResult.affectedRows
};
break;
default:
result = { success: false, error: 'Unsupported operation' };
}
return result;
} finally {
connection.release();
}
} catch (error) {
console.error('Database operation failed:', error);
return { success: false, error: error.message };
}
};
Web Application Integration (Express/Koa)
- Express
- Koa
const express = require('express');
const mysql = require('mysql2/promise');
const router = express.Router();
// Create connection pool
const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
port: process.env.DB_PORT || 3306,
connectionLimit: 10,
charset: 'utf8mb4'
});
// Health check
router.get('/health', async (req, res) => {
try {
const connection = await pool.getConnection();
await connection.ping();
connection.release();
res.json({ status: 'healthy' });
} catch (error) {
res.status(500).json({ status: 'unhealthy', error: error.message });
}
});
// Paginated query
router.get('/users', async (req, res) => {
const { page = 1, limit = 10 } = req.query;
const offset = (page - 1) * limit;
try {
const connection = await pool.getConnection();
try {
const [countResult] = await connection.query('SELECT COUNT(*) as total FROM persons');
const [rows] = await connection.query(
'SELECT * FROM persons ORDER BY created_at DESC LIMIT ? OFFSET ?',
[parseInt(limit), parseInt(offset)]
);
res.json({
success: true,
data: rows,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total: countResult[0].total
}
});
} finally {
connection.release();
}
} catch (error) {
console.error('Query failed:', error);
res.status(500).json({ success: false, error: 'Query failed' });
}
});
// Create user
router.post('/users', async (req, res) => {
const { name, age, email } = req.body;
if (!name || !age || !email) {
return res.status(400).json({ error: 'Missing required parameters' });
}
try {
const connection = await pool.getConnection();
try {
const [result] = await connection.query(
'INSERT INTO persons (name, age, email) VALUES (?, ?, ?)',
[name, age, email]
);
res.status(201).json({
success: true,
data: { id: result.insertId, name, age, email }
});
} finally {
connection.release();
}
} catch (error) {
if (error.code === 'ER_DUP_ENTRY') {
return res.status(409).json({ error: 'Email already exists' });
}
res.status(500).json({ error: 'Creation failed' });
}
});
// Update user
router.put('/users/:id', async (req, res) => {
const { id } = req.params;
const { name, age, email } = req.body;
try {
const connection = await pool.getConnection();
try {
const [result] = await connection.query(
'UPDATE persons SET name = ?, age = ?, email = ? WHERE id = ?',
[name, age, email, id]
);
if (result.affectedRows === 0) {
return res.status(404).json({ error: 'User does not exist' });
}
res.json({ success: true });
} finally {
connection.release();
}
} catch (error) {
res.status(500).json({ error: 'Update failed' });
}
});
// Delete user
router.delete('/users/:id', async (req, res) => {
const { id } = req.params;
try {
const connection = await pool.getConnection();
try {
const [result] = await connection.query('DELETE FROM persons WHERE id = ?', [id]);
if (result.affectedRows === 0) {
return res.status(404).json({ error: 'User does not exist' });
}
res.json({ success: true });
} finally {
connection.release();
}
} catch (error) {
res.status(500).json({ error: 'Deletion failed' });
}
});
module.exports = router;
const Koa = require('koa');
const Router = require('koa-router');
const bodyParser = require('koa-bodyparser');
const mysql = require('mysql2/promise');
const app = new Koa();
const router = new Router();
const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
port: process.env.DB_PORT || 3306,
connectionLimit: 10
});
router.get('/users', async (ctx) => {
try {
const connection = await pool.getConnection();
try {
const [rows] = await connection.query('SELECT * FROM persons LIMIT 10');
ctx.body = { success: true, data: rows };
} finally {
connection.release();
}
} catch (error) {
ctx.status = 500;
ctx.body = { success: false, error: 'Query failed' };
}
});
app.use(bodyParser());
app.use(router.routes());
module.exports = app;
Test API
- cURL
- JavaScript
# Health check
curl https://your-domain.com/health
# Get user list
curl https://your-domain.com/users
# Create user
curl -X POST https://your-domain.com/users \
-H "Content-Type: application/json" \
-d '{"name":"Zhang San","age":25,"email":"test@example.com"}'
# Update user
curl -X PUT https://your-domain.com/users/1 \
-H "Content-Type: application/json" \
-d '{"name":"Li Si","age":30,"email":"lisi@example.com"}'
# Delete user
curl -X DELETE https://your-domain.com/users/1
// Get user list
async function getUsers() {
const response = await fetch('https://your-domain.com/users');
const result = await response.json();
console.log(result);
}
// Create user
async function createUser() {
const response = await fetch('https://your-domain.com/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'New User',
age: 25,
email: 'newuser@example.com'
})
});
const result = await response.json();
console.log(result);
}
Best Practices
Connection Pool Configuration Optimization
const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
port: process.env.DB_PORT ? Number(process.env.DB_PORT) : 3306,
// Connection pool configuration
connectionLimit: 10, // Maximum number of connections
queueLimit: 0, // Queue limit, 0 means unlimited
acquireTimeout: 60000, // Connection acquisition timeout
timeout: 60000, // Query timeout
// Reconnection configuration
reconnect: true, // Auto-reconnect
// Character set configuration
charset: 'utf8mb4', // Support emoji and special characters
// SSL configuration (recommended for production)
ssl: process.env.NODE_ENV === 'production' ? {
rejectUnauthorized: false
} : false,
// Timezone configuration
timezone: '+08:00'
});
Error Handling and Retry Mechanism
// Database operation with retry
async function executeWithRetry(operation, maxRetries = 3) {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
return await operation();
} catch (error) {
lastError = error;
// Check if error is retryable
if (isRetryableError(error) && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000; // Exponential backoff
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
throw lastError;
}
function isRetryableError(error) {
const retryableCodes = [
'ECONNRESET',
'ETIMEDOUT',
'ENOTFOUND',
'ER_LOCK_WAIT_TIMEOUT',
'PROTOCOL_CONNECTION_LOST'
];
return retryableCodes.includes(error.code);
}
// Usage example
router.get('/users', async (req, res) => {
try {
const result = await executeWithRetry(async () => {
const connection = await pool.getConnection();
try {
const [rows] = await connection.query('SELECT * FROM persons');
return rows;
} finally {
connection.release();
}
});
res.json({ success: true, data: result });
} catch (error) {
console.error('Query failed:', error);
res.status(500).json({ success: false, error: 'Query failed' });
}
});
Transaction Processing
// Transaction processing example
async function transferMoney(fromUserId, toUserId, amount) {
const connection = await pool.getConnection();
try {
await connection.beginTransaction();
// Check sender's balance
const [fromUser] = await connection.query(
'SELECT balance FROM users WHERE id = ? FOR UPDATE',
[fromUserId]
);
if (fromUser[0].balance < amount) {
throw new Error('Insufficient balance');
}
// Deduct sender's balance
await connection.query(
'UPDATE users SET balance = balance - ? WHERE id = ?',
[amount, fromUserId]
);
// Add to receiver's balance
await connection.query(
'UPDATE users SET balance = balance + ? WHERE id = ?',
[amount, toUserId]
);
// Record transfer log
await connection.query(
'INSERT INTO transfer_logs (from_user_id, to_user_id, amount, created_at) VALUES (?, ?, ?, NOW())',
[fromUserId, toUserId, amount]
);
await connection.commit();
return { success: true, message: 'Transfer successful' };
} catch (error) {
await connection.rollback();
throw error;
} finally {
connection.release();
}
}
Performance Monitoring
// Query performance monitoring
function withPerformanceMonitoring(queryFunction) {
return async function(...args) {
const startTime = Date.now();
const queryId = Math.random().toString(36).substr(2, 9);
console.log(`[${queryId}] Query started:`, args[0]);
try {
const result = await queryFunction.apply(this, args);
const duration = Date.now() - startTime;
console.log(`[${queryId}] Query completed: ${duration}ms`);
// Log slow queries
if (duration > 1000) {
console.warn(`[${queryId}] Slow query detected: ${duration}ms`, {
sql: args[0],
params: args[1]
});
}
return result;
} catch (error) {
const duration = Date.now() - startTime;
console.error(`[${queryId}] Query failed: ${duration}ms`, {
error: error.message,
sql: args[0]
});
throw error;
}
};
}
// Wrap connection's query method
const originalQuery = pool.query.bind(pool);
pool.query = withPerformanceMonitoring(originalQuery);
Common Issues
Connection Issues
What to do about connection timeout?
Possible Causes:
- High network latency
- High database server load
- Firewall blocking connection
Solutions:
// Increase timeout
const pool = mysql.createPool({
// ... other configurations
acquireTimeout: 120000, // Increase to 2 minutes
timeout: 120000, // Query timeout 2 minutes
connectTimeout: 60000 // Connection timeout 1 minute
});
How to handle too many connections?
Solutions:
- Optimize connection pool configuration
- Release connections promptly
- Use connection monitoring
// Monitor connection pool status
setInterval(() => {
console.log('Connection pool status:', {
Total connections: pool._allConnections.length,
Free connections: pool._freeConnections.length,
Acquiring connections: pool._acquiringConnections.length
});
}, 30000);
Performance Issues
How to optimize slow queries?
Optimization Strategies:
- Add appropriate indexes
- Optimize SQL queries
- Use connection pool
- Implement query caching
-- Index example
CREATE INDEX idx_persons_email ON persons(email);
CREATE INDEX idx_persons_age ON persons(age);
CREATE INDEX idx_persons_created_at ON persons(created_at);
Security Issues
How to prevent SQL injection?
Protection Measures:
- Always use parameterized queries
- Validate input data
- Use principle of least privilege
// Correct parameterized query
const [rows] = await connection.query(
'SELECT * FROM persons WHERE name = ? AND age > ?',
[userName, minAge]
);
// Incorrect string concatenation (vulnerable to SQL injection)
// const query = `SELECT * FROM persons WHERE name = '${userName}'`;
- Enable connection pool in production environments to improve performance
- Add indexes for frequently queried fields
- Use transactions to ensure data consistency
- Regularly monitor database performance and slow query logs
- Avoid hardcoding database passwords in code
- Use parameterized queries to prevent SQL injection
- Regularly update database versions and security patches
- Configure appropriate firewall rules