PlanetScale 与 Node.js 集成 #

本章将介绍如何在 Node.js 应用中集成 PlanetScale 数据库,包括多种连接方式和完整的 CRUD 示例。

项目准备 #

创建项目 #

bash
# 创建项目目录
mkdir my-planetscale-app
cd my-planetscale-app

# 初始化项目
npm init -y

# 安装依赖
npm install @planetscale/database dotenv
npm install express

环境配置 #

env
DATABASE_URL=mysql://abc123:pscale_pw_xxx@aws.connect.psdb.cloud/my-database?sslaccept=strict
PORT=3000

使用 @planetscale/database #

基本连接 #

javascript
import { connect } from '@planetscale/database';
import 'dotenv/config';

const config = {
  url: process.env.DATABASE_URL
};

const conn = connect(config);

export default conn;

Express 应用示例 #

javascript
import express from 'express';
import { connect } from '@planetscale/database';
import 'dotenv/config';

const app = express();
app.use(express.json());

const conn = connect({ url: process.env.DATABASE_URL });

app.get('/users', async (req, res) => {
  try {
    const result = await conn.execute('SELECT * FROM users ORDER BY created_at DESC');
    res.json(result.rows);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.get('/users/:id', async (req, res) => {
  try {
    const result = await conn.execute(
      'SELECT * FROM users WHERE id = ?',
      [req.params.id]
    );
    if (result.rows.length === 0) {
      return res.status(404).json({ error: 'User not found' });
    }
    res.json(result.rows[0]);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.post('/users', async (req, res) => {
  try {
    const { name, email } = req.body;
    const result = await conn.execute(
      'INSERT INTO users (name, email) VALUES (?, ?)',
      [name, email]
    );
    res.status(201).json({ 
      id: result.insertId,
      name,
      email 
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.put('/users/:id', async (req, res) => {
  try {
    const { name, email } = req.body;
    await conn.execute(
      'UPDATE users SET name = ?, email = ? WHERE id = ?',
      [name, email, req.params.id]
    );
    res.json({ id: req.params.id, name, email });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.delete('/users/:id', async (req, res) => {
  try {
    await conn.execute('DELETE FROM users WHERE id = ?', [req.params.id]);
    res.status(204).send();
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(process.env.PORT, () => {
  console.log(`Server running on port ${process.env.PORT}`);
});

使用 mysql2 #

安装 #

bash
npm install mysql2

连接池配置 #

javascript
import mysql from 'mysql2/promise';
import 'dotenv/config';

const pool = mysql.createPool({
  uri: process.env.DATABASE_URL,
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0
});

export default pool;

CRUD 示例 #

javascript
import pool from './db.js';

export async function getUsers() {
  const [rows] = await pool.execute('SELECT * FROM users ORDER BY created_at DESC');
  return rows;
}

export async function getUserById(id) {
  const [rows] = await pool.execute(
    'SELECT * FROM users WHERE id = ?',
    [id]
  );
  return rows[0];
}

export async function createUser(data) {
  const [result] = await pool.execute(
    'INSERT INTO users (name, email) VALUES (?, ?)',
    [data.name, data.email]
  );
  return { id: result.insertId, ...data };
}

export async function updateUser(id, data) {
  await pool.execute(
    'UPDATE users SET name = ?, email = ? WHERE id = ?',
    [data.name, data.email, id]
  );
  return { id, ...data };
}

export async function deleteUser(id) {
  await pool.execute('DELETE FROM users WHERE id = ?', [id]);
}

Fastify 集成 #

安装 #

bash
npm install fastify @fastify/cors

Fastify 应用 #

javascript
import Fastify from 'fastify';
import cors from '@fastify/cors';
import { connect } from '@planetscale/database';
import 'dotenv/config';

const fastify = Fastify({ logger: true });
await fastify.register(cors);

const conn = connect({ url: process.env.DATABASE_URL });

fastify.get('/users', async (request, reply) => {
  const result = await conn.execute('SELECT * FROM users ORDER BY created_at DESC');
  return result.rows;
});

fastify.get('/users/:id', async (request, reply) => {
  const { id } = request.params;
  const result = await conn.execute(
    'SELECT * FROM users WHERE id = ?',
    [id]
  );
  if (result.rows.length === 0) {
    reply.code(404);
    return { error: 'User not found' };
  }
  return result.rows[0];
});

fastify.post('/users', async (request, reply) => {
  const { name, email } = request.body;
  const result = await conn.execute(
    'INSERT INTO users (name, email) VALUES (?, ?)',
    [name, email]
  );
  reply.code(201);
  return { id: result.insertId, name, email };
});

fastify.put('/users/:id', async (request, reply) => {
  const { id } = request.params;
  const { name, email } = request.body;
  await conn.execute(
    'UPDATE users SET name = ?, email = ? WHERE id = ?',
    [name, email, id]
  );
  return { id, name, email };
});

fastify.delete('/users/:id', async (request, reply) => {
  const { id } = request.params;
  await conn.execute('DELETE FROM users WHERE id = ?', [id]);
  reply.code(204);
});

const start = async () => {
  try {
    await fastify.listen({ port: process.env.PORT || 3000 });
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};

start();

事务处理 #

使用 @planetscale/database #

javascript
import { connect } from '@planetscale/database';

const conn = connect({ url: process.env.DATABASE_URL });

async function transferFunds(fromId, toId, amount) {
  const tx = await conn.transaction();
  
  try {
    await tx.execute(
      'UPDATE accounts SET balance = balance - ? WHERE id = ?',
      [amount, fromId]
    );
    
    await tx.execute(
      'UPDATE accounts SET balance = balance + ? WHERE id = ?',
      [amount, toId]
    );
    
    await tx.commit();
    return { success: true };
  } catch (error) {
    await tx.rollback();
    throw error;
  }
}

使用 mysql2 #

javascript
import pool from './db.js';

async function transferFunds(fromId, toId, amount) {
  const connection = await pool.getConnection();
  
  try {
    await connection.beginTransaction();
    
    await connection.execute(
      'UPDATE accounts SET balance = balance - ? WHERE id = ?',
      [amount, fromId]
    );
    
    await connection.execute(
      'UPDATE accounts SET balance = balance + ? WHERE id = ?',
      [amount, toId]
    );
    
    await connection.commit();
    return { success: true };
  } catch (error) {
    await connection.rollback();
    throw error;
  } finally {
    connection.release();
  }
}

错误处理 #

统一错误处理 #

javascript
class DatabaseError extends Error {
  constructor(message, code) {
    super(message);
    this.name = 'DatabaseError';
    this.code = code;
  }
}

function handleDatabaseError(error) {
  if (error.message.includes('Duplicate entry')) {
    return new DatabaseError('Record already exists', 'DUPLICATE_ENTRY');
  }
  if (error.message.includes('Cannot delete')) {
    return new DatabaseError('Cannot delete record', 'DELETE_FAILED');
  }
  return new DatabaseError('Database operation failed', 'UNKNOWN_ERROR');
}

app.post('/users', async (req, res) => {
  try {
    const { name, email } = req.body;
    const result = await conn.execute(
      'INSERT INTO users (name, email) VALUES (?, ?)',
      [name, email]
    );
    res.status(201).json({ id: result.insertId, name, email });
  } catch (error) {
    const dbError = handleDatabaseError(error);
    res.status(400).json({ error: dbError.message, code: dbError.code });
  }
});

最佳实践 #

连接池管理 #

javascript
import mysql from 'mysql2/promise';

const pool = mysql.createPool({
  uri: process.env.DATABASE_URL,
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0,
  enableKeepAlive: true,
  keepAliveInitialDelay: 0
});

process.on('SIGINT', async () => {
  await pool.end();
  process.exit();
});

export default pool;

查询封装 #

javascript
class UserRepository {
  constructor(connection) {
    this.conn = connection;
  }

  async findAll() {
    const result = await this.conn.execute('SELECT * FROM users');
    return result.rows;
  }

  async findById(id) {
    const result = await this.conn.execute(
      'SELECT * FROM users WHERE id = ?',
      [id]
    );
    return result.rows[0];
  }

  async create(data) {
    const result = await this.conn.execute(
      'INSERT INTO users (name, email) VALUES (?, ?)',
      [data.name, data.email]
    );
    return { id: result.insertId, ...data };
  }

  async update(id, data) {
    await this.conn.execute(
      'UPDATE users SET name = ?, email = ? WHERE id = ?',
      [data.name, data.email, id]
    );
    return { id, ...data };
  }

  async delete(id) {
    await this.conn.execute('DELETE FROM users WHERE id = ?', [id]);
  }
}

export default UserRepository;

下一步 #

现在你已经掌握了 Node.js 集成,接下来学习 Prisma 集成,了解如何使用 ORM 简化数据库操作!

最后更新:2026-03-29