NodeJs MySQL 使用 Node JS 和 MySQL 构建一个简单的应用程序。

2025-05-25

NodeJs MySQL使用 Node JS 和 MySQL 构建一个简单的应用程序。

你好,世界!在本教程中,我们将使用 Node JS 和 MySQL 构建一个简单的 CRUD 应用程序。

我们将要建造什么?


上图展示了这款应用。它允许你将球员添加到数据库,并显示数据库中球员的详细信息。你还可以删除和编辑球员的详细信息。

先决条件

在参加本教程之前,我们假设您满足以下列出的要求:

  • 您的电脑上安装了 Node JS。
  • 对 Node JS 和 Express JS 有基本的了解。
  • 了解 SQL,您应该知道并理解如何查询数据库。
  • 您的电脑上已安装 phpmyadmin。我建议安装xampp,因为它已经包含 phpmyadmin。
  • 了解如何使用模板引擎——我们将在本教程中使用 ejs)。
  • 您选择的文本编辑器或 IDE。

文件夹结构

这就是该项目的结构。

├── node-mqsql-crud-app (main directory)   
    ├── node_modules
    ├── public
        ├── assets 
            ├── img
    ├── routes
        ├── index.js
        ├── player.js
    ├── views
        ├── partials 
            ├── header.ejs
        ├── index.ejs
        ├── add-player.ejs
        ├── edit-player.ejs
    ├── app.js    
Enter fullscreen mode Exit fullscreen mode

为项目创建目录

在合适的目录中打开命令提示符并输入以下命令:

mkdir node-mysql-crud-app 
Enter fullscreen mode Exit fullscreen mode

然后通过输入以下命令切换到目录

cd node-mysql-crud-app 
Enter fullscreen mode Exit fullscreen mode

初始化项目

在项目目录中打开命令提示符并输入以下命令:

npm init
Enter fullscreen mode Exit fullscreen mode

安装所需的模块。

要成功构建该应用程序,需要以下模块。

  • express:用于创建处理路由并处理来自客户端的请求。
  • express-fileupload:围绕 busboy 的简单快速文件上传中间件。
  • body-parser:用于解析来自客户端的传入请求。
  • mysql:MySQL 的 Node JS 驱动程序。
  • ejs:模板引擎,用于为应用程序呈现 html 页面。
  • req-flash:用于向视图发送 flash 消息
  • nodemon:全局安装。用于监视文件更改并自动重启服务器。

键入以下命令以安装前 7 个模块作为依赖项。

npm install express express-fileupload body-parser mysql ejs req-flash --save
Enter fullscreen mode Exit fullscreen mode

然后键入以下命令在您的 PC 上全局安装最后一个模块。

npm install nodemon -g
Enter fullscreen mode Exit fullscreen mode

为应用程序创建数据库

复制以下命令并导航到您的 phpmyadmin 仪表板并在控制台中执行以下查询(通常位于页面底部)以便为应用程序创建数据库和表。

CREATE DATABASE socka;
CREATE TABLE IF NOT EXISTS `players` (
  `id` int(5) NOT NULL AUTO_INCREMENT,
  `first_name` varchar(255) NOT NULL,
  `last_name` varchar(255) NOT NULL,
  `position` varchar(255) NOT NULL,
  `number` int(11) NOT NULL,
  `image` varchar(255) NOT NULL,
  `user_name` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;
Enter fullscreen mode Exit fullscreen mode

添加视图

header.ejs

header.ejs文件将位于/views/partials文件夹中,并将包含在项目的其余部分中。

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <!--<link rel="stylesheet" href="/assets/css/custom.css">-->
    <title><%= title %></title>
</head>
<style>
    .table-wrapper {
        margin-top: 50px;
    }

    .player-img {
        width: 40px;
        height: 40px;
    }

    .add-player-form {
        margin-top: 50px;
    }
</style>
<body>
<div class="page-wrapper">
    <nav class="navbar navbar-light bg-light">
        <span class="navbar-brand mb-0 h1" ><a href="/">Socka Players</a></span>
        <a class="float-right" href="/add" title="Add a New Player">Add a Player</a>
    </nav>
Enter fullscreen mode Exit fullscreen mode

索引.ejs

这是应用程序的主页,其中包含一个表格,显示所有玩家的列表。

<% include partials/header.ejs %>
    <div class="table-wrapper">
        <% if (players.length > 0) {%>
            <table class="table table-hovered">
                <thead class="thead-dark">
                    <tr>
                        <th scope="col">ID</th>
                        <th scope="col">Image</th>
                        <th scope="col">First Name</th>
                        <th scope="col">Last Name</th>
                        <th scope="col">Position</th>
                        <th scope="col">Number</th>
                        <th scope="col">Username</th>
                        <th scope="col">Action</th>
                    </tr>
                </thead>
                <tbody>
                    <% players.forEach((player, index) => { %>
                        <tr>
                            <th scope="row"><%= player.id %></th>
                            <td><img src="/assets/img/<%= player.image %>" class="rounded-circle player-img" alt=""></td>
                            <td><%= player.first_name %></td>
                            <td><%= player.last_name %></td>
                            <td><%= player.position %></td>
                            <td><%= player.number %></td>
                            <td>@<%= player.user_name %></td>
                            <td>
                                <a href="/edit/<%= player.id %>" target="_blank" rel="noopener" class="btn btn-sm btn-success">Edit</a>
                                <a href="/delete/<%= player.id %>" class="btn btn-sm btn-danger">Delete</a>
                            </td>
                        </tr>
                    <% }) %>
                </tbody>
            </table>
        <% } else { %>
            <p class="text-center">No players found. Go <a href="/add" >here</a> to add players.</p>
        <% } %>
    </div>
</div>
</body>
</html>

Enter fullscreen mode Exit fullscreen mode

添加播放器.ejs

此页面包含向数据库添加新玩家的表单。

<% include partials/header.ejs %>
    <div class="container">
        <% if (message != '') { %>
            <p class="text-center text-danger"><%= message %></p>
        <% } %>
        <form class="add-player-form" action="" method="post" enctype="multipart/form-data">
            <div class="form-row">
                <div class="form-group col-md-4">
                    <input type="text" class="form-control" name="first_name" id="first-name" placeholder="First Name" required>
                </div>
                <div class="form-group col-md-4">
                    <input type="text" class="form-control" name="last_name" id="last-name" placeholder="Last Name" required>
                </div>
                <div class="form-group col-md-4">
                    <input type="text" class="form-control" name="username" id="username" placeholder="Username" required>
                </div>
            </div>
            <div class="form-row">
                <div class="form-group col-md-6">
                    <input type="number" class="form-control" name="number" id="number" placeholder="Number" required>
                </div>
                <div class="form-group col-md-6">
                    <select id="position" name="position" class="form-control" required>
                        <option selected disabled>Choose position</option>
                        <option>Goalkeeper</option>
                        <option>Defender</option>
                        <option>Midfielder</option>
                        <option>Forward</option>
                    </select>
                </div>
                <div class="col-md-12">
                    <label for="player-img"><b>Player Image</b></label><br>
                    <input type="file" name="image" id="player-img" class="" required>
                </div>
            </div>
            <button type="submit" class="btn btn-primary float-right">Add Player</button>
        </form>
    </div>
</div>
</body>
</html>

Enter fullscreen mode Exit fullscreen mode

编辑播放器.ejs

此页面包含编辑已添加到数据库的玩家的表单。

<% include partials/header.ejs %>
<div class="container">
    <% if (message) { %>
        <p class="text-center text-danger"><%= message %></p>
    <% } %>

    <% if (player) { %>
        <form class="add-player-form" action="" method="post" enctype="multipart/form-data">
            <div class="form-row">
                <div class="form-group col-md-4">
                    <label for="first-name">First Name</label>
                    <input type="text" class="form-control" name="first_name" id="first-name" value="<%= player.first_name %>" required>
                </div>
                <div class="form-group col-md-4">
                    <label for="last-name">Last Name</label>
                    <input type="text" class="form-control" name="last_name" id="last-name" value="<%= player.last_name %>" required>
                </div>
                <div class="form-group col-md-4">
                    <label for="username">Username</label>
                    <input type="text" class="form-control" name="username" id="username" value="<%= player.user_name %>" required disabled title="Username cannot be edited.">
                </div>
            </div>
            <div class="form-row">
                <div class="form-group col-md-6">
                    <label for="number">Number</label>
                    <input type="number" class="form-control" name="number" id="number" placeholder="Number" value="<%= player.number %>" required>
                </div>
                <div class="form-group col-md-6">
                    <label for="position">Position</label>
                    <select id="position" name="position" class="form-control" required>
                        <option selected><%= player.position %></option>
                        <option>Goalkeeper</option>
                        <option>Centre Back</option>
                        <option>Right Back</option>
                        <option>Left Back</option>
                        <option>Defensive Midfielder</option>
                        <option>Central Midfielder</option>
                        <option>Attacking Midfielder</option>
                        <option>Right Wing Forward</option>
                        <option>Left Wing Forward</option>
                        <option>Striker</option>
                    </select>
                </div>
            </div>
            <button type="submit" class="btn btn-success float-right">Update Player</button>
        </form>
    <% } else { %>
        <p class="text-center">Player Not Found. Go <a href="/add">here</a> to add players.</p>
    <% } %>
</div>
</div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

在服务器上工作。

App.js

const express = require('express');
const fileUpload = require('express-fileupload');
const bodyParser = require('body-parser');
const mysql = require('mysql');
const path = require('path');
const app = express();

// const {getHomePage} = require('./routes/index');
// const {addPlayerPage, addPlayer, deletePlayer, editPlayer, editPlayerPage} = require('./routes/player');
const port = 5000;

// create connection to database
// the mysql.createConnection function takes in a configuration object which contains host, user, password and the database name.
const db = mysql.createConnection ({
    host: 'localhost',
    user: 'root',
    password: '',
    database: 'socka'
});

// connect to database
db.connect((err) => {
    if (err) {
        throw err;
    }
    console.log('Connected to database');
});
global.db = db;

// configure middleware
app.set('port', process.env.port || port); // set express to use this port
app.set('views', __dirname + '/views'); // set express to look in this folder to render our view
app.set('view engine', 'ejs'); // configure template engine
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json()); // parse form data client
app.use(express.static(path.join(__dirname, 'public'))); // configure express to use public folder
app.use(fileUpload()); // configure fileupload

// routes for the app
/*
app.get('/', getHomePage);
app.get('/add', addPlayerPage);
app.get('/edit/:id', editPlayerPage);
app.get('/delete/:id', deletePlayer);
app.post('/add', addPlayer);
app.post('/edit/:id', editPlayer);
*/

// set the app to listen on the port
app.listen(port, () => {
    console.log(`Server running on port: ${port}`);
});

Enter fullscreen mode Exit fullscreen mode

在上面的代码中,我们需要用到这些模块,然后创建一个与数据库的连接。mysql.createConnection函数接受一个对象,该对象包含要连接的数据库的配置信息。在下一个语句中,我们将连接数据库。在命令提示符下运行下面的代码来启动服务器。

nodemon app.js
Enter fullscreen mode Exit fullscreen mode

您的控制台应该显示以下结果:

添加路线

index.js

将以下代码复制到/routes目录中的 index.js 文件中。

module.exports = {
    getHomePage: (req, res) => {
        let query = "SELECT * FROM `players` ORDER BY id ASC"; // query database to get all the players

        // execute query
        db.query(query, (err, result) => {
            if (err) {
                res.redirect('/');
            }
            res.render('index.ejs', {
                title: Welcome to Socka | View Players
                ,players: result
            });
        });
    },
};

Enter fullscreen mode Exit fullscreen mode

db.query函数用于查询数据库。它接受查询语句、字符串以及一个包含两个参数的回调函数。如果查询成功,则将结果传递给res.render函数中的视图

播放器.js

player.js文件将包含玩家页面的所有路线,例如添加玩家、更新玩家的详细信息和删除玩家。

const fs = require('fs');

module.exports = {
    addPlayerPage: (req, res) => {
        res.render('add-player.ejs', {
            title: Welcome to Socka | Add a new player
            ,message: ''
        });
    },
    addPlayer: (req, res) => {
        if (!req.files) {
            return res.status(400).send("No files were uploaded.");
        }

        let message = '';
        let first_name = req.body.first_name;
        let last_name = req.body.last_name;
        let position = req.body.position;
        let number = req.body.number;
        let username = req.body.username;
        let uploadedFile = req.files.image;
        let image_name = uploadedFile.name;
        let fileExtension = uploadedFile.mimetype.split('/')[1];
        image_name = username + '.' + fileExtension;

        let usernameQuery = "SELECT * FROM `players` WHERE user_name = '" + username + "'";

        db.query(usernameQuery, (err, result) => {
            if (err) {
                return res.status(500).send(err);
            }
            if (result.length > 0) {
                message = 'Username already exists';
                res.render('add-player.ejs', {
                    message,
                    title: Welcome to Socka | Add a new player
                });
            } else {
                // check the filetype before uploading it
                if (uploadedFile.mimetype === 'image/png' || uploadedFile.mimetype === 'image/jpeg' || uploadedFile.mimetype === 'image/gif') {
                    // upload the file to the /public/assets/img directory
                    uploadedFile.mv(`public/assets/img/${image_name}`, (err ) => {
                        if (err) {
                            return res.status(500).send(err);
                        }
                        // send the player's details to the database
                        let query = "INSERT INTO `players` (first_name, last_name, position, number, image, user_name) VALUES ('" +
                            first_name + "', '" + last_name + "', '" + position + "', '" + number + "', '" + image_name + "', '" + username + "')";
                        db.query(query, (err, result) => {
                            if (err) {
                                return res.status(500).send(err);
                            }
                            res.redirect('/');
                        });
                    });
                } else {
                    message = "Invalid File format. Only 'gif', 'jpeg' and 'png' images are allowed.";
                    res.render('add-player.ejs', {
                        message,
                        title: Welcome to Socka | Add a new player
                    });
                }
            }
        });
    },
    editPlayerPage: (req, res) => {
        let playerId = req.params.id;
        let query = "SELECT * FROM `players` WHERE id = '" + playerId + "' ";
        db.query(query, (err, result) => {
            if (err) {
                return res.status(500).send(err);
            }
            res.render('edit-player.ejs', {
                title: Edit  Player
                ,player: result[0]
                ,message: ''
            });
        });
    },
    editPlayer: (req, res) => {
        let playerId = req.params.id;
        let first_name = req.body.first_name;
        let last_name = req.body.last_name;
        let position = req.body.position;
        let number = req.body.number;

        let query = "UPDATE `players` SET `first_name` = '" + first_name + "', `last_name` = '" + last_name + "', `position` = '" + position + "', `number` = '" + number + "' WHERE `players`.`id` = '" + playerId + "'";
        db.query(query, (err, result) => {
            if (err) {
                return res.status(500).send(err);
            }
            res.redirect('/');
        });
    },
    deletePlayer: (req, res) => {
        let playerId = req.params.id;
        let getImageQuery = 'SELECT image from `players` WHERE id = "' + playerId + '"';
        let deleteUserQuery = 'DELETE FROM players WHERE id = "' + playerId + '"';

        db.query(getImageQuery, (err, result) => {
            if (err) {
                return res.status(500).send(err);
            }

            let image = result[0].image;

            fs.unlink(`public/assets/img/${image}`, (err) => {
                if (err) {
                    return res.status(500).send(err);
                }
                db.query(deleteUserQuery, (err, result) => {
                    if (err) {
                        return res.status(500).send(err);
                    }
                    res.redirect('/');
                });
            });
        });
    }
};

Enter fullscreen mode Exit fullscreen mode

此文件处理玩家页面的所有 post 和 get 请求。“添加玩家”函数包含一个函数,用于将玩家图像上传到/public/assets/img目录,并将玩家详细信息发送到数据库。

连接路线和景观

转到app.js并取消注释以下几行

  • 在第 8 行和第 9 行
// const {getHomePage} = require('./routes/index');
// const {addPlayerPage, addPlayer, deletePlayer, editPlayer, editPlayerPage} = require('./routes/player');
Enter fullscreen mode Exit fullscreen mode
  • 第 40 至 47 行
/*
app.get('/', getHomePage);
app.get('/add', addPlayerPage);
app.get('/edit/:id', editPlayerPage);
app.get('/delete/:id', deletePlayer);
app.post('/add', addPlayer);
app.post('/edit/:id', editPlayer);
*/
Enter fullscreen mode Exit fullscreen mode

运行应用程序

删除注释行后,检查命令提示符以确保代码没有错误,然后打开浏览器并打开http://localhost:5000。此时会显示索引页,由于尚未添加玩家,因此页面将类似于下图:

点击页面上的“添加球员”链接,将加载“添加球员”页面,然后填写表单以添加球员。添加球员后,主页将以表格形式显示已添加的球员。像这样

如果遇到错误,请在评论区留言或查看项目在GitHub上的仓库。
代码并不完善,欢迎随时改进并发送拉取请求。

别忘了在twittergithub上查看我

文章来源:https://dev.to/achowba/build-a-simple-app-using-node-js-and-mysql-19me
PREV
👋如何将任何网站转换为桌面应用程序(30 秒内)
NEXT
您是否开始开发项目,但没有在 github 上开发项目?