site logo

Marico's space

无需集成代码调用 TypeScript Backend —— 使用 Graftcode 的简易任务追踪器

前端技术 2026-08-21 14:52:56 7

最近折腾了 Graftcode 这个工具,踩了几个坑,这篇把经验说清楚。做前端的同学估计都有同感:每次搭个项目,光是前后端通信这块就要写一堆重复代码——定义接口、写请求封装、处理响应格式、维护类型定义。REST API 那套流程熟悉得很,但确实繁琐。

Graftcode 这个工具解决的就是这个问题:不用写 API 路由、不用生成客户端,直接把后端方法暴露出来,前端像调用本地库一样调用。我用它搭了个任务追踪器,整个过程挺顺的,下面把步骤和思路理一下。

前置条件

动手之前先确认环境准备好:

  • Node 最新版装好
  • 有 React 和 TypeScript 基础
  • 了解 API 和 fetch 请求的基本用法(方便对比理解 Graftcode 的差异)
  • 有个 Graftcode 账号
  • 本地装了 Graftcode Gateway

为什么任务类应用通常离不开 API?

任务追踪器需要增删改查这类基础逻辑。大多数开发者习惯把后端功能通过 API 暴露出来,前端再通过 HTTP 客户端去调用。这个模式确实成熟,但问题是——哪怕是一个很小的任务管理功能,也要经过好几个层面才能真正执行到业务逻辑。

比如创建任务时,前端用 fetch 发起请求,后端收到请求后经过路由层、控制器、业务逻辑层,可能还要读写数据库,最后才返回结果。这套流程跑得通,但每加一个功能就得维护一套对应的路由、请求校验、响应处理、文档更新和前端集成工作。随着项目变大,通信层本身就成了需要管理的复杂系统。

用传统 API 方式的话,端点大概长这样:

  • POST /tasks
  • GET /tasks
  • PATCH /tasks/{id}
  • DELETE /tasks/{id}

认识 Graftcode

这次换了个思路,不走传统 API 那一套,用 Graftcode 把后端功能直接暴露给前端调用。Graftcode 的核心思路是把后端当作一个包来运行,而不是一堆 HTTP 端点。

目标不是做个多复杂的任务管理应用,而是演示怎么让 React 前端直接调用后端逻辑,全程不写任何 API 路由。

Task Tracker output

实战:Graftcode 任务追踪器

Graftcode 特别适合前后端一起开发、并且需要强类型整合的场景。不用维护 API 路由、请求模型和生成的客户端,直接用生成的包来消费后端功能。内网工具、全栈应用、原型项目都很适合。

这次要搭一个不写 API 路由、不写控制器、不写集成样板代码的任务追踪器。先建个项目:

npm create vite@latest graftcode-todolist-app -- --template react-ts
cd graftcode-todolist-app
npm install
npm install tailwindcss @tailwindcss/vite
npm install vite-plugin-node-polyfills

更新 vite.config.ts 启用 polyfills:

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
import { nodePolyfills } from "vite-plugin-node-polyfills";
import { resolve } from "path"; export default defineConfig({ plugins: [ react(), tailwindcss(), nodePolyfills({ exclude: ["crypto"] }), ], resolve: { alias: { crypto: resolve("./src/crypto-shim.js"), }, },
});

在 src/index.css 里加一行:

@import "tailwindcss";

然后在 src/ 目录创建 crypto-shim.js:

export const randomUUID = () => window.crypto.randomUUID();
export default { randomUUID };

创建后端模块

现在来处理后端逻辑。在项目目录下创建 task-backend/ 目录并初始化:

mkdir task-backend
cd task-backend
npm init -y
mkdir backend

修改 package.json,指定入口文件:

{ "name": "task-backend", "version": "1.0.0", "main": "./backend/index.ts"
}

在 backend/ 目录创建 index.ts,写入核心逻辑:

import { randomUUID } from "crypto"; interface Task { id: string; title: string; description: string; completed: boolean; createdAt: number;
} export class TaskService { private static tasks: Task[] = []; static createTask( title: string, description: string = "" ): string { const task: Task = { id: randomUUID(), title, description, completed: false, createdAt: Date.now(), }; TaskService.tasks.push(task); return JSON.stringify(task); } static getTasks(): string { return JSON.stringify(TaskService.tasks); } static completeTask(id: string): string { const task = TaskService.tasks.find( (task) => task.id === id ); if (!task) { return JSON.stringify(null); } task.completed = true; return JSON.stringify(task); } static deleteTask(id: string): boolean { const before = TaskService.tasks.length; TaskService.tasks = TaskService.tasks.filter( (task) => task.id !== id ); return TaskService.tasks.length < before; }
}

这就是整个后端逻辑了。没写 API,没定义路由,就是一个普普通通的类。因为所有方法都挂在一个导出的类上,Graftcode 可以直接把这些方法暴露出去,不需要我们额外写控制器或者路由包装。

服务暴露了四个方法:

  • createTask()
  • getTasks()
  • deleteTask()
  • completeTask()

通过 Graftcode Gateway 暴露服务

后端逻辑写好了,接下来要让其他应用能用上。Graftcode Gateway(简称 gg)是干这个的运行时,它把应用里的公开方法暴露出来供调用。

Gateway 可以用 Docker 镜像启动,或者直接从 GitHub releases 页面下载。启动命令:

gg ./package.json --projectKey YOUR_PROJECT_KEY --port 8080

项目 key 在 Graftcode 仪表盘里拿:进 Vision 面板,创建一个新 Gateway,部署选项里点"I already have Graftcode Gateway"按钮,会自动生成一个项目 key 给你。

Deploy logic

Gateway 读取 package.json,找到 main 入口,加载 TaskService,自动注册所有方法。运行后会打印一条安装命令,复制下来:

npm install --registry https://grft.dev/YOUR_PROJECT_ID__graftcode @graft/npm-task-backend@1.0.0

包名是 Gateway 自动生成的,命令要原样复制,同时记下 URL 里的项目 ID。

Gateway 跑起来后,打开终端输出里的 Graftcode Vision 链接,可以在这里查看后端的类、方法、安装说明等信息。

用 Graftcode Vision 探索后端

Graftcode Vision 是后端的发现层。以前看 Swagger 文档或者手动查端点定义,现在直接在这里看实际的类、包和方法。

Vision 加载后能看到 TaskService 类和它提供的方法。可以查看:

  • 安装和配置说明
  • 可用方法
  • 参数
  • 返回值类型
  • 方法描述
Methods in Graftcode

安装生成的包

回到前端项目根目录,配置 @graft 镜像源,让 npm 知道去哪找生成的包:

echo "@graft:registry=https://grft.dev/YOUR_PROJECT_ID__graftcode" >> .npmrc

记得把 YOUR_PROJECT_ID 换成自己的实际项目 ID。然后用 Gateway 启动时打印的 exact 命令安装包:

npm install --registry https://grft.dev/YOUR_PROJECT_ID__graftcode @graft/npm-task-backend@1.0.0

在连接前端之前,还可以用 Vision 直接测试各个方法,验证功能是否正常。

搭建 React 前端

后端搞定,现在写前端。在 App.tsx 里导入生成的包,配置 Gateway 地址。Vision 页面的配置区域有详细的导入说明:

import { GraftConfig } from "@graft/npm-task-backend";
GraftConfig.host = `ws://localhost:8080/ws`;

GraftConfig.host 就是 Gateway 的 WebSocket 地址,确保后端在另一个终端里跑着。这就是全部的连接配置,不需要 base URL,不需要 HTTP 客户端,不需要中间件。

配置完之后,前端直接调用后端方法,跟调普通函数一样。在 App.tsx 里试试:

import { GraftConfig, TaskService } from "@graft/npm-task-backend"; GraftConfig.host = "ws://localhost:8080/ws"; const fetchTasks = async () => { const tasks = await TaskService.getTasks(); return tasks;
};

前端加载时自动调用 fetchTasks() 从 TaskService.getTasks() 获取任务列表。后端连接建立后,完整的 App.tsx 如下:

import { useState, useEffect } from "react";
import { GraftConfig, TaskService } from "@graft/npm-task-backend"; GraftConfig.host = "ws://localhost:8080/ws"; interface Task { id: string; title: string; description: string; completed: boolean; createdAt: number;
} export default function App() { const [tasks, setTasks] = useState<Task[]>([]); const [title, setTitle] = useState(""); const [description, setDescription] = useState(""); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState<string | null>(null); const [actionLoading, setActionLoading] = useState<string | null>(null); const fetchTasks = async () => { try { let data: any = await TaskService.getTasks(); if (typeof data === "string") { try { data = JSON.parse(data); } catch