我来一步步教你在 VS Code 里搭建 Next.js(基于 TypeScript)项目环境,并创建一个可以直接运行的项目。
一、安装必要环境
1. 安装 Node.js
Next.js 依赖 Node 环境。
进入官网下载安装最新版 LTS(推荐):
https://nodejs.org/安装后验证:
node -v npm -v
✅ Node.js ≥ 18.17.0 或更高版本,Next.js 14+ 都没问题。
2. 安装 VS Code 插件
打开 VS Code,安装以下扩展(在扩展市场搜索):
ES7+ React/Redux/React-Native snippets(快速生成组件模板)
Prettier - Code formatter(代码格式化)
Tailwind CSS IntelliSense(若你用 Tailwind)
TypeScript React (tsx)(语法高亮)
二、创建 Next.js 项目
在你想放项目的目录打开终端(或用 VS Code 自带终端):
方式 1:使用官方脚手架(推荐)
npx create-next-app@latest my-next-app
接下来会出现交互式选项:
✔ What is your project named? … my-next-app
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes/No(可选)
✔ Would you like to use src/ directory? … Yes(推荐)
✔ Would you like to use App Router? (recommended) … Yes ✅
✔ Would you like to customize the default import alias (@/*)? … Yes
✔ What import alias would you like configured? … @/*
⚙️ 如果你只是要最基础项目,也可以:
npx create-next-app my-next-app --typescript --use-npm
三、进入项目并运行
cd my-next-app
npm run dev
浏览器打开:
http://localhost:3000
如果看到 “Welcome to Next.js!” 的页面,就说明环境搭建成功
四、VS Code 运行配置(可选)
如果你想直接用 VS Code F5 启动项目:
打开
.vscode/launch.json(若没有则新建)填入以下内容:
{"version": "0.2.0","configurations": [{"name": "Next.js App","type": "node","request": "launch","runtimeExecutable": "npm","runtimeArgs": ["run", "dev"],"port": 3000,"console": "integratedTerminal"}]
}
然后点击 “运行和调试 (Run and Debug)” ▶️ 即可直接启动。
五、项目结构简介(App Router 模式)
my-next-app/
├── app/ # 页面与路由目录
│ ├── page.tsx # 首页
│ ├── layout.tsx # 页面布局
│ └── globals.css # 全局样式
├── public/ # 静态文件
├── next.config.mjs # Next.js 配置
├── tsconfig.json # TypeScript 配置
├── package.json
└── tailwind.config.ts # 若启用 Tailwind
六、下一步建议
| 任务 | 说明 |
|---|---|
✅ 运行 npm run build | 构建生产版本 |
✅ 运行 npm start | 启动生产服务器 |
⚙️ 学习 pages/ vs app/ | App Router 是新标准 |
| 添加 Tailwind CSS | 用于快速样式开发 |
| 连接 API Routes | 在 /app/api 下写后端接口 |
Next.js 前端项目 + Rust REST API 后端 是一个非常干净、高性能的组合。
我来详细解释一下架构思路、跨语言通信方式和具体实现步骤。
一、总体架构
[Next.js 14+ (前端)]↓ HTTP 请求 (fetch / axios)
[ Rust Web 后端 (Actix / Axum / Rocket) ]↓ 数据层
[ 数据库: PostgreSQL / MySQL / SQLite 等 ]
Next.js 用来做前端 UI 和 SSR(服务端渲染),
Rust 用来提供高性能的 RESTful API(处理业务逻辑、数据库、AI推理等)。
二、Rust后端项目创建
下面给你一个完整可运行的示例:演示如何 创建 Rust 项目 → 连接 MySQL → 提供 RESTful API(CRUD)。示例使用 axum 作为 web 框架,sqlx 作为 MySQL 客户端(异步连接池),并使用 tokio 作为运行时。
我会给出:
创建项目与依赖(
Cargo.toml)。数据库建表 SQL。
完整代码(
src/main.rs+ 辅助模块)。环境变量配置与运行示例(curl 请求)。
1. 新建项目
在终端里:
cargo new rust_mysql_api --bin
cd rust_mysql_api
2. Cargo.toml
把 Cargo.toml 换成下面内容(或在原有基础上添加依赖):
[package]
name = "rust_mysql_api"
version = "0.1.0"
edition = "2021"
[dependencies]
tokio = { version = "1.36", features = ["rt-multi-thread", "macros"] }
axum = "0.7"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sqlx = { version = "0.7", features = ["mysql", "runtime-tokio-native-tls", "macros"] }
tower = "0.4"
thiserror = "1.0"
dotenvy = "0.15"
uuid = { version = "1.4", features = ["v4"] }
说明:
sqlx带macros会在编译时为query!等宏做检查(可选)。如果你不想用 macros,可以移除macros特性并使用动态查询。
3. MySQL 建表(示例)
在你的 MySQL 中建一个简单的 users 表(示例):
CREATE DATABASE IF NOT EXISTS rust_api DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
USE rust_api;
CREATE TABLE IF NOT EXISTS users (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(100) NOT NULL,email VARCHAR(150) NOT NULL,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
4. 环境变量
在项目根目录创建 .env(示例):
DATABASE_URL=mysql://username:password@127.0.0.1:3306/rust_api
BIND_ADDR=127.0.0.1:3000
把 username/password/host/port/dbname 改成你的。
5. 完整代码:src/main.rs
将 src/main.rs 替换为下面内容(包含 CRUD 的处理函数):
use axum::{extract::{Path, State},response::IntoResponse,routing::{get, post, put, delete},Json, Router,
};
use serde::{Deserialize, Serialize};
use sqlx::{mysql::MySqlPoolOptions, MySql, Pool, FromRow};
use std::{net::SocketAddr, sync::Arc};
use thiserror::Error;
use dotenvy::dotenv;
use std::env;
use axum::http::StatusCode;
type DBPool = Pool;
#[derive(Clone)]
struct AppState {pool: Arc,
}
#[derive(Debug, Serialize, Deserialize, FromRow)]
struct User {id: i32,name: String,email: String,created_at: chrono::NaiveDateTime,
}
#[derive(Debug, Serialize, Deserialize)]
struct NewUser {name: String,email: String,
}
#[derive(Debug, Serialize, Deserialize)]
struct UpdateUser {name: Option,email: Option,
}
#[derive(Error, Debug)]
enum ApiError {#[error("Database error: {0}")]Db(#[from] sqlx::Error),#[error("Not found")]NotFound,
}
impl IntoResponse for ApiError {fn into_response(self) -> axum::response::Response {match &self {ApiError::Db(e) => {let body = serde_json::json!({"error": format!("db error: {}", e)});(StatusCode::INTERNAL_SERVER_ERROR, Json(body)).into_response()}ApiError::NotFound => {let body = serde_json::json!({"error": "not found"});(StatusCode::NOT_FOUND, Json(body)).into_response()}}}
}
#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {dotenv().ok();let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set in .env or environment");let bind_addr = env::var("BIND_ADDR").unwrap_or_else(|_| "127.0.0.1:3000".to_string());// create poollet pool = MySqlPoolOptions::new().max_connections(5).connect(&database_url).await?;let state = AppState { pool: Arc::new(pool) };// routerlet app = Router::new().route("/users", get(list_users).post(create_user)).route("/users/:id", get(get_user).put(update_user).delete(delete_user)).with_state(state);let addr: SocketAddr = bind_addr.parse()?;println!("Listening on {}", addr);axum::Server::bind(&addr).serve(app.into_make_service()).await?;Ok(())
}
// GET /users
async fn list_users(State(state): State) -> Result>, ApiError> {let rows = sqlx::query_as::<_, User>("SELECT id, name, email, created_at FROM users ORDER BY id").fetch_all(&*state.pool).await?;Ok(Json(rows))
}
// GET /users/:id
async fn get_user(Path(id): Path, State(state): State) -> Result, ApiError> {let user = sqlx::query_as::<_, User>("SELECT id, name, email, created_at FROM users WHERE id = ?").bind(id).fetch_optional(&*state.pool).await?;match user {Some(u) => Ok(Json(u)),None => Err(ApiError::NotFound),}
}
// POST /users body: { "name": "...", "email": "..." }
async fn create_user(State(state): State, Json(payload): Json) -> Result<(StatusCode, Json), ApiError> {let rec = sqlx::query("INSERT INTO users (name, email) VALUES (?, ?)").bind(&payload.name).bind(&payload.email).execute(&*state.pool).await?;// 获取插入 idlet last_id = rec.last_insert_id() as i32;let user = sqlx::query_as::<_, User>("SELECT id, name, email, created_at FROM users WHERE id = ?").bind(last_id).fetch_one(&*state.pool).await?;Ok((StatusCode::CREATED, Json(user)))
}
// PUT /users/:id body: { "name": optional, "email": optional }
async fn update_user(Path(id): Path, State(state): State, Json(payload): Json) -> Result, ApiError> {// 获取现有数据(确保存在)let existing = sqlx::query_as::<_, User>("SELECT id, name, email, created_at FROM users WHERE id = ?").bind(id).fetch_optional(&*state.pool).await?;let existing = match existing {Some(u) => u,None => return Err(ApiError::NotFound),};let new_name = payload.name.as_ref().unwrap_or(&existing.name);let new_email = payload.email.as_ref().unwrap_or(&existing.email);sqlx::query("UPDATE users SET name = ?, email = ? WHERE id = ?").bind(new_name).bind(new_email).bind(id).execute(&*state.pool).await?;let user = sqlx::query_as::<_, User>("SELECT id, name, email, created_at FROM users WHERE id = ?").bind(id).fetch_one(&*state.pool).await?;Ok(Json(user))
}
// DELETE /users/:id
async fn delete_user(Path(id): Path, State(state): State) -> Result {let res = sqlx::query("DELETE FROM users WHERE id = ?").bind(id).execute(&*state.pool).await?;if res.rows_affected() == 0 {Err(ApiError::NotFound)} else {Ok(StatusCode::NO_CONTENT)}
}
说明:
使用
axum::State(这里是State<AppState>)传递 MySQL 池。
sqlx::query_as::<_, User>(...)+FromRow自动把行映射为User。错误统一用
ApiError转成 HTTP 响应。
6. 运行项目
在项目根目录:
# 加载 .env(如果你用的是 bash/zsh)
export DATABASE_URL="mysql://username:password@127.0.0.1:3306/rust_api"
export BIND_ADDR="127.0.0.1:3000"
cargo run
启动后会监听 127.0.0.1:3000(或 .env 中的 BIND_ADDR)。
7. 测试 API(curl 示例)
创建用户:
curl -X POST http://127.0.0.1:3000/users -H "Content-Type: application/json" \-d '{"name":"Alice","email":"alice@example.com"}'
列表:
curl http://127.0.0.1:3000/users
详情:
curl http://127.0.0.1:3000/users/1
更新:
curl -X PUT http://127.0.0.1:3000/users/1 -H "Content-Type: application/json" \-d '{"name":"Alice Updated"}'
删除:
curl -X DELETE http://127.0.0.1:3000/users/1
8. 额外建议与注意事项
连接池配置:
max_connections(5)只是示例,按你应用并发调整。迁移管理:生产推荐使用迁移工具(如
sqlx的sqlx-cli或diesel_cli、refinery)来管理数据库 schema。sqlx有sqlx migrate功能。事务:如果涉及多表或复合操作,使用
pool.begin()获取事务并commit()/rollback()。验证:输入需要校验(email 格式、长度等),可用
validatorcrate。安全:别把明文密码写死到代码或提交到 git,使用环境变量或 secret 管理。
日志与监控:可加
tracing/tower-http中间件记录请求/错误。编译提示:
sqlx的macros可能需要DATABASE_URL在编译时可访问(用于编译时检查),若造成编译问题,可在Cargo.toml移除macros特性或给sqlx提供离线配置。