umbraco-repository-pattern by umbraco/umbraco-cms-backoffice-skills
npx skills add https://github.com/umbraco/umbraco-cms-backoffice-skills --skill umbraco-repository-pattern仓库是后台管理界面进行数据请求和更新通知的入口点,它抽象了来自不同数据源(服务器、离线数据库、存储、Signal-R)的数据访问。它提供了一种结构化的方式来管理数据操作,将业务逻辑与直接数据访问分离,从而便于维护和扩展。仓库在幕后使用数据源,允许使用者处理数据而无需了解数据存储的位置或方式。
切勿 在自定义 API 调用中使用原始的 fetch()。这将导致 401 未授权错误。
始终 使用配置了 Umbraco 身份验证上下文的生成式 OpenAPI 客户端。有关设置说明,请参阅 umbraco-openapi-client 技能。
在实现之前,请务必获取最新文档:
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { MyRepository } from './my-repository.js';
const repositoryManifest = {
type: 'repository',
alias: 'My.Repository',
name: 'My Repository',
api: MyRepository,
};
umbExtensionsRegistry.register(repositoryManifest);
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
// 从生成的 OpenAPI 客户端导入(参见 umbraco-openapi-client 技能)
import { MyExtensionService } from '../api/index.js';
export class MyRepository extends UmbControllerBase {
constructor(host: UmbControllerHost) {
super(host);
}
async getAll() {
// 使用生成的 OpenAPI 客户端 - 自动处理身份验证
const response = await MyExtensionService.getAll();
return response.data;
}
async getById(id: string) {
const response = await MyExtensionService.getById({ path: { id } });
return response.data;
}
async create(data: any) {
const response = await MyExtensionService.create({ body: data });
return response.data;
}
async update(id: string, data: any) {
const response = await MyExtensionService.update({
path: { id },
body: data,
});
return response.data;
}
async delete(id: string) {
await MyExtensionService.delete({ path: { id } });
}
}
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { MyRepository } from './my-repository.js';
export class MyContext extends UmbContextBase<MyContext> {
#repository = new MyRepository(this);
#items = new UmbArrayState([]);
readonly items = this.#items.asObservable();
async loadItems() {
const data = await this.#repository.getAll();
this.#items.setValue(data);
}
async createItem(item: any) {
const newItem = await this.#repository.create(item);
this.#items.setValue([...this.#items.getValue(), newItem]);
}
async deleteItem(id: string) {
await this.#repository.delete(id);
this.#items.setValue(
this.#items.getValue().filter(item => item.id !== id)
);
}
}
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { MyDataSource } from './my-data-source.js';
export class MyRepository extends UmbControllerBase {
#dataSource = new MyDataSource(this);
async getAll() {
return await this.#dataSource.getAll();
}
async getById(id: string) {
return await this.#dataSource.getById(id);
}
}
// 数据源 - 使用生成的 OpenAPI 客户端
import { MyExtensionService } from '../api/index.js';
export class MyDataSource extends UmbControllerBase {
async getAll() {
// 使用 OpenAPI 客户端 - 切勿使用原始 fetch()
const response = await MyExtensionService.getAll();
return response.data;
}
async getById(id: string) {
const response = await MyExtensionService.getById({ path: { id } });
return response.data;
}
}
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { UmbTreeDataSource } from '@umbraco-cms/backoffice/tree';
export class MyTreeRepository extends UmbControllerBase {
#dataSource: UmbTreeDataSource;
constructor(host: UmbControllerHost) {
super(host);
this.#dataSource = new MyTreeDataSource(this);
}
async getRootItems() {
return await this.#dataSource.getRootItems();
}
async getChildrenOf(parentKey: string) {
return await this.#dataSource.getChildrenOf(parentKey);
}
async getAncestorsOf(key: string) {
return await this.#dataSource.getAncestorsOf(key);
}
}
仓库 : 数据操作的入口点,抽象数据访问
数据源 : 实际获取/存储数据的底层机制
存储 : 使用状态对象在整个会话期间保存数据的可选层
宿主元素 : 仓库必须与宿主一起实例化,以便进行正确的上下文渲染
关注点分离 :
优势 :
使用模式 :
就是这样!请务必获取最新文档,保持示例简洁,生成完整可运行的代码。
每周安装次数
75
仓库
GitHub 星标数
17
首次出现
2026年2月4日
安全审计
安装于
github-copilot55
cursor24
opencode23
codex23
gemini-cli21
amp21
Repositories are the Backoffice's entry point for data requests and update notifications, abstracting data access from various sources (server, offline database, store, Signal-R). They provide a structured way to manage data operations, separating business logic from direct data access for easier maintenance and scalability. Repositories use data sources behind the scenes, allowing consumers to work with data without knowing where or how it's stored.
NEVER use rawfetch() for custom API calls. This will result in 401 Unauthorized errors.
ALWAYS use a generated OpenAPI client configured with Umbraco's auth context. See the umbraco-openapi-client skill for setup instructions.
Always fetch the latest docs before implementing:
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { MyRepository } from './my-repository.js';
const repositoryManifest = {
type: 'repository',
alias: 'My.Repository',
name: 'My Repository',
api: MyRepository,
};
umbExtensionsRegistry.register(repositoryManifest);
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
// Import from generated OpenAPI client (see umbraco-openapi-client skill)
import { MyExtensionService } from '../api/index.js';
export class MyRepository extends UmbControllerBase {
constructor(host: UmbControllerHost) {
super(host);
}
async getAll() {
// Use generated OpenAPI client - handles auth automatically
const response = await MyExtensionService.getAll();
return response.data;
}
async getById(id: string) {
const response = await MyExtensionService.getById({ path: { id } });
return response.data;
}
async create(data: any) {
const response = await MyExtensionService.create({ body: data });
return response.data;
}
async update(id: string, data: any) {
const response = await MyExtensionService.update({
path: { id },
body: data,
});
return response.data;
}
async delete(id: string) {
await MyExtensionService.delete({ path: { id } });
}
}
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { MyRepository } from './my-repository.js';
export class MyContext extends UmbContextBase<MyContext> {
#repository = new MyRepository(this);
#items = new UmbArrayState([]);
readonly items = this.#items.asObservable();
async loadItems() {
const data = await this.#repository.getAll();
this.#items.setValue(data);
}
async createItem(item: any) {
const newItem = await this.#repository.create(item);
this.#items.setValue([...this.#items.getValue(), newItem]);
}
async deleteItem(id: string) {
await this.#repository.delete(id);
this.#items.setValue(
this.#items.getValue().filter(item => item.id !== id)
);
}
}
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { MyDataSource } from './my-data-source.js';
export class MyRepository extends UmbControllerBase {
#dataSource = new MyDataSource(this);
async getAll() {
return await this.#dataSource.getAll();
}
async getById(id: string) {
return await this.#dataSource.getById(id);
}
}
// Data Source - uses generated OpenAPI client
import { MyExtensionService } from '../api/index.js';
export class MyDataSource extends UmbControllerBase {
async getAll() {
// Use OpenAPI client - NEVER raw fetch()
const response = await MyExtensionService.getAll();
return response.data;
}
async getById(id: string) {
const response = await MyExtensionService.getById({ path: { id } });
return response.data;
}
}
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { UmbTreeDataSource } from '@umbraco-cms/backoffice/tree';
export class MyTreeRepository extends UmbControllerBase {
#dataSource: UmbTreeDataSource;
constructor(host: UmbControllerHost) {
super(host);
this.#dataSource = new MyTreeDataSource(this);
}
async getRootItems() {
return await this.#dataSource.getRootItems();
}
async getChildrenOf(parentKey: string) {
return await this.#dataSource.getChildrenOf(parentKey);
}
async getAncestorsOf(key: string) {
return await this.#dataSource.getAncestorsOf(key);
}
}
Repository : Entry point for data operations, abstracts data access
Data Source : Underlying mechanism that actually fetches/stores data
Store : Optional layer that holds data throughout the session using State objects
Host Element : Repository must be instantiated with a host for proper context rendering
Separation of Concerns :
Benefits :
Usage Pattern :
That's it! Always fetch fresh docs, keep examples minimal, generate complete working code.
Weekly Installs
75
Repository
GitHub Stars
17
First Seen
Feb 4, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
github-copilot55
cursor24
opencode23
codex23
gemini-cli21
amp21
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
120,000 周安装