当前位置: 首页 > news >正文

Next.JS环境搭建,对接Rust的RESTful API

我来一步步教你在 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 启动项目

  1. 打开 .vscode/launch.json(若没有则新建)

  2. 填入以下内容:

{"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 作为运行时。

我会给出:

  1. 创建项目与依赖(Cargo.toml)。

  2. 数据库建表 SQL。

  3. 完整代码(src/main.rs + 辅助模块)。

  4. 环境变量配置与运行示例(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"] }

说明:sqlxmacros 会在编译时为 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<MySql>;#[derive(Clone)]
struct AppState {pool: Arc<DBPool>,
}#[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<String>,email: Option<String>,
}#[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<AppState>) -> Result<Json<Vec<User>>, 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<i32>, State(state): State<AppState>) -> Result<Json<User>, 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<AppState>, Json(payload): Json<NewUser>) -> Result<(StatusCode, Json<User>), 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<i32>, State(state): State<AppState>, Json(payload): Json<UpdateUser>) -> Result<Json<User>, 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<i32>, State(state): State<AppState>) -> Result<StatusCode, ApiError> {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. 额外建议与注意事项

  1. 连接池配置max_connections(5) 只是示例,按你应用并发调整。

  2. 迁移管理:生产推荐使用迁移工具(如 sqlxsqlx-clidiesel_clirefinery)来管理数据库 schema。sqlxsqlx migrate 功能。

  3. 事务:如果涉及多表或复合操作,使用 pool.begin() 获取事务并 commit()/rollback()

  4. 验证:输入需要校验(email 格式、长度等),可用 validator crate。

  5. 安全:别把明文密码写死到代码或提交到 git,使用环境变量或 secret 管理。

  6. 日志与监控:可加 tracing / tower-http 中间件记录请求/错误。

  7. 编译提示sqlxmacros 可能需要 DATABASE_URL 在编译时可访问(用于编译时检查),若造成编译问题,可在 Cargo.toml 移除 macros 特性或给 sqlx 提供离线配置。


http://www.dtcms.com/a/503549.html

相关文章:

  • 目前流行的网站分辨率做多大自己做网站需要备份么
  • NativeScript-Vue 开发指南:直接使用 Vue构建原生移动应用
  • 珠海市横琴建设局网站html网站自带字体怎么做
  • 看汽车图片的网站可以做壁纸差异基因做聚类分析网站
  • 了解制造过程中的BOM类型
  • 小九源码-springboot088-宾馆客房管理系统
  • 数字芯片的版图和制造--被称为3极管的晶体管却有4极!
  • 专门做优惠劵的网站专业的销售网站
  • 从0到1学习Qt -- 内存管理与乱码问题
  • html`<mark>`
  • C++ stack 和 queue
  • 洛谷 P1035:[NOIP 2002 普及组] 级数求和 ← double
  • C程序中的大规模程序设计:从模块化到抽象数据类型
  • 响应式网站高度如何计算seo自动点击排名
  • 企业网站引导页模板江西门户网站建设
  • Prim 算法和 Kruskal 算法应用场景
  • 雷电模拟器环境配置
  • 南沙移动网站建设中元建设集团网站
  • 公司网站百度推广wordpress没中文插件
  • 手写Spring第7弹:Spring IoC容器深度解析:XML配置的完整指南
  • java的异常体系
  • pybullet
  • Filebeat、ELK安装与数据同步
  • 嵌入式开发学习日志40——stm32之I2C协议层
  • 网站建设公司小江可以做试题的网站
  • Android 四大组件桥梁 —— Intent (意图) 详解
  • 小鱼在线网站建设网站注册步骤
  • wordpress 视频站模板wordpress和网盘结合
  • Orleans流背压控制机制深度分析
  • Java并发之队列同步器AQS原理