第一个功能:从骨架到可用接口
这一页带你做一个真正能访问的接口,而不是停留在“生成了几个文件”。
目标很明确:做出一个 /users 接口,并返回一组演示数据。
第 1 步:生成 user 资源
在项目根目录执行:
stratix generate resource user
执行后,通常会得到这些文件:
src/controllers/UserController.tssrc/services/UserService.tssrc/repositories/UserRepository.tssrc/repositories/interfaces/IUserRepository.ts
这四个文件正好对应了 Stratix 最常见的一条业务链:
请求 -> Controller -> Service -> Repository -> 返回数据
第 2 步:先看生成结果是什么意思
UserController.ts
它负责暴露 HTTP 接口。资源生成器会自动把 user 变成复数路由,因此默认路径是:
/users
UserService.ts
它负责业务编排。默认会调用 userRepository.findAll()。
UserRepository.ts
它默认只是一个占位实现,通常会返回空数组。这是正常的,因为脚手架不知道你的真实业务数据从哪里来。
第 3 步:把 repository 改成真正返回数据
先修改接口文件:
export interface IUserRecord {
id: string;
name: string;
createdAt: string;
}
export interface IUserRepository {
findAll(): Promise<IUserRecord[]>;
}
然后实现一个最简单的演示版 repository:
import type { Logger } from '@stratix/core';
import type {
IUserRecord,
IUserRepository
} from './interfaces/IUserRepository.js';
export default class UserRepository implements IUserRepository {
constructor(private readonly logger: Logger) {}
async findAll(): Promise<IUserRecord[]> {
this.logger.debug('Listing user records.');
return [
{
id: 'u_001',
name: 'Alice',
createdAt: new Date().toISOString()
},
{
id: 'u_002',
name: 'Bob',
createdAt: new Date().toISOString()
}
];
}
}
这一步的目的不是“真的接数据库”,而是先把整条链跑通。
第 4 步:确认 service 不要越层
UserService.ts 的默认结构通常已经是对的。你只需要确认它没有直接碰数据库,而是继续通过 repository 拿数据:
import type { Logger } from '@stratix/core';
import type UserRepository from '../repositories/UserRepository.js';
import type { IUserRecord } from '../repositories/interfaces/IUserRepository.js';
export default class UserService {
constructor(
private readonly logger: Logger,
private readonly userRepository: UserRepository
) {}
async list(): Promise<IUserRecord[]> {
this.logger.debug('Listing user records.');
return this.userRepository.findAll();
}
}
第 5 步:确认 controller 只做协议层工作
控制器应该只负责请求和响应,不要在里面写业务规则:
import {
Controller,
Get,
type FastifyReply,
type FastifyRequest
} from '@stratix/core';
import type UserService from '../services/UserService.js';
@Controller()
export default class UserController {
constructor(private readonly userService: UserService) {}
@Get('/users')
async list(_request: FastifyRequest, reply: FastifyReply): Promise<void> {
const data = await this.userService.list();
reply.status(200).send({
success: true,
data
});
}
}
注意这里有两个关键点:
@Controller()不带前缀- 路由路径直接写在
@Get('/users')
第 6 步:启动并验证
执行:
pnpm dev
然后另开一个终端访问:
curl http://127.0.0.1:3000/users
如果一切正常,你应该能得到类似下面的响应:
{
"success": true,
"data": [
{
"id": "u_001",
"name": "Alice",
"createdAt": "2026-03-28T00:00:00.000Z"
},
{
"id": "u_002",
"name": "Bob",
"createdAt": "2026-03-28T00:00:00.000Z"
}
]
}
第 7 步:如果你要接数据库,下一步怎么做
演示数据跑通之后,真实项目的下一步通常是:
stratix add preset database- 先读一遍
database-quickstart.md,确认数据库配置是真的能被项目读到 - 再读
database-crud.md,把UserRepository改成基于@stratix/database的真正仓储实现
如果只是单表 CRUD,优先用普通 repository 即可。
如果你要处理多表一致性、工作流状态迁移、claim/checkpoint/finalize 这类长流程单元,再考虑:
stratix generate business-repository order
你现在已经完成了什么
如果你做到这里,说明你已经不是“只会建骨架”的状态了,而是已经掌握了 Stratix 最核心的一条开发路径:
- 会生成资源
- 会分清 controller / service / repository 各自职责
- 会让一个接口真正返回业务数据
- 知道从演示数据切换到数据库时应该去改哪里
- 知道下一步应该进入数据库接入和真实 CRUD 实战,而不是继续往演示数据上堆逻辑