跳到内容
Sutando
Esc
↑↓导航↵打开⌘J预览

从 Laravel 迁移到 Node.js:用 Sutando 无缝切换

很多 Laravel 开发者想尝试 Node.js,但舍不得 Eloquent。好消息是,Sutando 几乎完美复刻了 Eloquent 的 API,让你可以无缝切换。本文将从实际迁移角度,详细对比 Laravel 和 Node.js + Sutando 的开发体验。

很多 Laravel 开发者想尝试 Node.js,但舍不得 Eloquent。好消息是,Sutando 几乎完美复刻了 Eloquent 的 API,让你可以无缝切换。本文将从实际迁移角度,详细对比 Laravel 和 Node.js + Sutando 的开发体验。

为什么要从 Laravel 迁移到 Node.js?

  • 全栈 TypeScript:前后端共享类型,减少沟通成本
  • 实时能力:WebSocket、SSE 在 Node.js 事件循环中更自然
  • Serverless/Edge:部署到 Cloudflare Workers、Vercel Edge
  • npm 生态:在某些领域(前端工具链、AI SDK)npm 比 Composer 更丰富
  • 团队技能:团队更熟悉 JavaScript

Eloquent vs Sutando 概念映射

Laravel Eloquent Sutando
Model 基类 Model 基类
protected $table table =
protected $casts casts =
protected $fillable 不支持
protected $hidden hidden =
hasMany hasMany
belongsTo belongsTo
hasOne hasOne
belongsToMany belongsToMany
morphTo / morphMany 不支持
with() 预加载 with() 预加载
scope 查询作用域 scope 查询作用域
模型事件 (creating, saving 等) 模型事件(相同)
SoftDeletes trait SoftDeletes 插件
Factory 不支持
Seeder 不支持

代码对比

模型定义

// Laravel
class User extends Model {
    protected $table = 'users';
    protected $casts = ['is_admin' => 'boolean', 'metadata' => 'array'];
    protected $fillable = ['name', 'email', 'password'];
    
    public function posts(): HasMany {
        return $this->hasMany(Post::class);
    }
    
    public function scopeActive($query) {
        return $query->where('active', true);
    }
}
// Sutando
class User extends Model {
  table = 'users';
  casts = { is_admin: 'boolean', metadata: 'json' };
  
  relationPosts() {
    return this.hasMany(Post, 'user_id');
  }
  
  scopeActive(query) {
    return query.where('active', true);
  }
}

查询

// Laravel
$users = User::with('posts.comments')
    ->where('active', true)
    ->active()
    ->orderBy('created_at', 'desc')
    ->limit(10)
    ->get();
// Sutando
const users = await User.query()
  .with('posts.comments')
  .where('active', true)
  .active()
  .orderBy('created_at', 'desc')
  .limit(10)
  .get();

创建

// Laravel
$user = User::create(['name' => 'Alice', 'email' => 'alice@example.com']);
// Sutando
const user = await User.query().create({ name: 'Alice', email: 'alice@example.com' });

模型事件

// Laravel
User::creating(function ($user) {
    $user->password = bcrypt($user->password);
});
// Sutando
User.creating(async (user) => {
  user.password = await bcrypt.hash(user.password, 10);
});

路由迁移

// Laravel
Route::get('/posts', [PostController::class, 'index']);
Route::post('/posts', [PostController::class, 'store']);
// Express
app.get('/posts', async (req, res) => {
  const posts = await Post.query().with('user').published().get();
  res.json(posts);
});

app.post('/posts', async (req, res) => {
  const post = await Post.query().create(req.body);
  res.status(201).json(post);
});

无法直接映射的部分

Laravel 功能 Node.js 替代方案
Blade 模板 React / Vue / 纯 API
认证系统 JWT / Passport.js
FormRequest 验证 Zod / Joi / express-validator
队列(Queue) BullMQ / Cloudflare Queues
邮件(Mail) Nodemailer / Cloudflare Email
Artisan 命令 npm scripts / Commander
Service Container 手动 DI / 框架内置

迁移策略

1. 绞杀者模式(推荐)

不要一次性全部重写。用 Nginx 做反向代理,逐路由迁移:

/api/posts → Node.js 服务
/api/users → 暂时还是 Laravel

2. 共享数据库

Sutando 可以直接操作 Laravel 创建的数据库 schema,无需迁移数据。

3. Session 共享

把 Session 放到 Redis,Laravel 和 Node.js 都能读写,实现渐进式迁移。

4. 从只读 API 开始

先迁移 GET 路由(风险最低),验证通过后再迁移写操作。

实战:迁移一个博客

假设有一个 Laravel 博客,包含 User、Post、Comment 三个模型:

// Node.js + Sutando 等价实现
import { sutando, Model } from 'sutando';
import express from 'express';

sutando.addConnection({
  client: 'mysql2',
  connection: { host: '127.0.0.1', user: 'root', password: '', database: 'blog' }
});

class User extends Model {
  table = 'users';
  hidden = ['password'];
  relationPosts() { return this.hasMany(Post, 'user_id'); }
}

class Post extends Model {
  table = 'posts';
  casts = { published: 'boolean' };
  relationUser() { return this.belongsTo(User, 'user_id'); }
  relationComments() { return this.hasMany(Comment, 'post_id'); }
  scopePublished(query) { return query.where('published', true); }
}

class Comment extends Model {
  table = 'comments';
  relationUser() { return this.belongsTo(User, 'user_id'); }
}

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

app.get('/posts', async (req, res) => {
  const posts = await Post.query()
    .with('user', 'comments.user')
    .published()
    .orderBy('created_at', 'desc')
    .limit(20)
    .get();
  res.json(posts);
});

app.listen(3000);

总结

从 Laravel 迁移到 Node.js 不意味着放弃 Eloquent 的开发体验。Sutando 几乎是 Eloquent 在 Node.js 中的镜像——相同的 API、相同的思维模式、相同的功能集。你可以保持现有数据库,逐路由迁移,团队从第一天就能高效工作。

从 npm install sutando 开始,查看中文文档。