从前只会写前端页面,今天终于让浏览器和数据库"对话"了 ------ 一个前端人的后端初体验
大家好,我是刚学习前端的小白。在掌握了 HTML/CSS/JS 之后,我开始好奇:网页上那些用户数据(比如评论、登录信息)到底存在哪里?怎么让不同客户端共享数据?于是,我决定踏出舒适区,学习后端开发。
Node.js 让 JavaScript 可以运行在服务器上,对我来说学习曲线最平缓。而 Koa 是一个轻量优雅的 Web 框架,由 Express 原班人马打造,完全使用 async/await 解决"回调地狱"。配合 MongoDB 这种文档型数据库,非常灵活易上手。
这篇文章就是我折腾了一天半之后,成功跑起来的第一个"服务器+数据库"项目。我希望用最直白的方式,把整个搭建过程记录下来,帮助和我一样迷茫的初学者。
一、准备工作:软件安装与概念扫盲
在写代码之前,你需要安装这三样东西:
- Node.js(版本 v14 以上)官网下载安装,自带 npm。安装完成后在终端输入 node -v 和 npm -v 检查是否成功。
- MongoDB我们先用云服务或本地安装。本地安装稍麻烦,推荐初学者使用 MongoDB Atlas(免费云数据库),注册后创建一个免费集群,记下连接字符串(类似 mongodb+srv://用户名:密码@clusterxxx.mongodb.net/数据库名)。
- Postman 或 Apifox用来测试 API 接口,因为浏览器直接输入地址只能发起 GET 请求。
小知识:
- Koa 就像 Express 的"轻量升级版",洋葱模型中间件非常优雅。
- Mongoose 是 Node 环境下操作 MongoDB 的对象模型库,帮你定义数据"骨架"(Schema)。
二、初始化项目 & 安装依赖
visual
# 新建文件夹并进入
mkdir my-koa-server
cd my-koa-server
# 初始化 package.json
npm init -y
# 安装核心依赖
npm install koa koa-router koa-bodyparser
npm install mongoose
# 安装开发依赖(热重启工具,可选)
npm install nodemon -D
修改 package.json 的 scripts:
groovy
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js"
}
三、最小的 Koa 服务器:跑起来再说
创建 app.js,写入最简单的 Koa 示例:
javascript
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx) => {
ctx.body = 'Hello Koa!我的第一个服务器';
});
app.listen(3000, () => {
console.log('✅ 服务器运行在 http://localhost:3000');
});
执行 npm run dev,打开浏览器访问 http://localhost:3000,看到文字说明成功。这是最基础的"中间件"------所有请求都会经过它。
❗踩坑:Koa 中没有 req 和 res,而是使用 ctx(context)对象,它融合了 Node 的 request 和 response。
四、连接 MongoDB(使用 Mongoose)
在项目根目录下新建 db.js 用来管理数据库连接:
javascript
const mongoose = require('mongoose');
// 请把下面的连接字符串换成你自己的 Atlas 或本地地址
const dbURI = 'mongodb+srv://你的用户名:你的密码@cluster0.xxxxx.mongodb.net/myFirstDB?retryWrites=true&w=majority';
mongoose.connect(dbURI)
.then(() => console.log('✅ MongoDB 连接成功'))
.catch(err => console.error('❌ MongoDB 连接失败', err));
然后在 app.js 最顶部引入它:require('./db');。只要连接成功,控制台就会打印成功日志。
❗踩坑:密码中如果有 @ 或特殊符号,需要进行 URL 编码。建议把连接字符串放在 .env 文件里(后面可以学习 dotenv 包),避免明文上传到 GitHub。
五、定义数据模型(Schema & Model)
我们将创建一个简单的"笔记"模型:每条笔记有标题、内容和创建时间。
新建 models/Note.js:
javascript
const mongoose = require('mongoose');
const noteSchema = new mongoose.Schema({
title: {
type: String,
required: true,
trim: true
},
content: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Note', noteSchema);
六、添加路由 & 实现 CRUD 接口
我们使用 koa-router 来管理不同的 API 路径。改写 app.js:
javascript
const Koa = require('koa');
const Router = require('koa-router');
const bodyParser = require('koa-bodyparser');
require('./db'); // 连接数据库
const Note = require('./models/Note');
const app = new Koa();
const router = new Router();
// 使用 bodyParser 才能解析 JSON 格式的请求体
app.use(bodyParser());
// 测试路由
router.get('/', async (ctx) => {
ctx.body = { message: '笔记 API 服务运行中' };
});
// 1. 创建笔记 (POST /api/notes)
router.post('/api/notes', async (ctx) => {
try {
const { title, content } = ctx.request.body;
if (!title || !content) {
ctx.status = 400;
ctx.body = { error: '标题和内容不能为空' };
return;
}
const newNote = new Note({ title, content });
await newNote.save();
ctx.status = 201;
ctx.body = newNote;
} catch (err) {
ctx.status = 500;
ctx.body = { error: err.message };
}
});
// 2. 获取所有笔记 (GET /api/notes)
router.get('/api/notes', async (ctx) => {
try {
const notes = await Note.find().sort({ createdAt: -1 });
ctx.body = notes;
} catch (err) {
ctx.status = 500;
ctx.body = { error: err.message };
}
});
// 3. 获取单个笔记 (GET /api/notes/:id)
router.get('/api/notes/:id', async (ctx) => {
try {
const note = await Note.findById(ctx.params.id);
if (!note) {
ctx.status = 404;
ctx.body = { error: '笔记不存在' };
return;
}
ctx.body = note;
} catch (err) {
ctx.status = 500;
ctx.body = { error: err.message };
}
});
// 4. 更新笔记 (PUT /api/notes/:id)
router.put('/api/notes/:id', async (ctx) => {
try {
const { title, content } = ctx.request.body;
const note = await Note.findByIdAndUpdate(
ctx.params.id,
{ title, content },
{ new: true, runValidators: true }
);
if (!note) {
ctx.status = 404;
ctx.body = { error: '笔记不存在' };
return;
}
ctx.body = note;
} catch (err) {
ctx.status = 500;
ctx.body = { error: err.message };
}
});
// 5. 删除笔记 (DELETE /api/notes/:id)
router.delete('/api/notes/:id', async (ctx) => {
try {
const note = await Note.findByIdAndDelete(ctx.params.id);
if (!note) {
ctx.status = 404;
ctx.body = { error: '笔记不存在' };
return;
}
ctx.body = { message: '删除成功' };
} catch (err) {
ctx.status = 500;
ctx.body = { error: err.message };
}
});
// 注册路由
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000, () => {
console.log('🚀 Koa 服务器已启动: http://localhost:3000');
});
七、使用 Postman 测试接口
启动服务后,打开 Postman:
| 方法 | URL | 请求体示例 (JSON) |
|---|---|---|
| POST | http://localhost:3000/api/notes | {"title":"我的第一篇笔记","content":"koa真好用"} |
| GET | http://localhost:3000/api/notes | - |
| GET | http://localhost:3000/api/notes/笔记ID | - |
| PUT | http://localhost:3000/api/notes/笔记ID | {"title":"修改后标题","content":"新内容"} |
| DELETE | http://localhost:3000/api/notes/笔记ID | - |
全部测试通过后,你会真正体会到"后端接口"的魅力 ------ 数据被存到云端数据库,无论谁、用什么客户端,都可以通过 API 获取相同的数据。
八、我踩过的那些坑(希望你避开)
- 忘记 await数据库操作都是异步的,如果不加 await,你会得到 undefined 或者一个 Promise 对象。对策:凡是 find()、save() 前面都写 await,所在函数必须标记 async。
- 跨域问题如果你用浏览器直接请求(比如从 http://localhost:8080 前端调用这个 API),会报 CORS 错误。对策:安装 @koa/cors 中间件,app.use(cors()) 一行搞定。
- Koa 和 Express 的区别Koa 没有内置路由、静态文件、bodyParser,都需要自己引用中间件。好处是极简灵活,坏处是新手容易不知所措。
- MongoDB Atlas 白名单 IP云数据库默认只允许某些 IP。记得在 Atlas 后台的 Network Access 里添加 0.0.0.0/0(允许所有,仅测试用)或者你的当前公网 IP。
写在最后
从前端的 alert 到后端的 ctx.body,从本地数组存数据到真正的 MongoDB 数据库,这一小步让我对整个 Web 开发有了更整体的理解。虽然 Koa 和 MongoDB 还有很多高级特性我没掌握,但第一天能把 CRUD 跑通,已经是巨大的胜利。
如果你也是从零开始接触后端,不要被"服务器""数据库"这些词吓退。跟着上面的代码敲一遍,修改几个参数,让 Postman 返回你想要的 JSON ------ 那一刻你会觉得,从前端到全栈,其实没有想象中那么远。
勇敢的人先享受世界,动手的人先学会编程。一起加油! 🚀