一键导入
graphql-extend
How to extend the Deenruv GraphQL API with new types, queries, mutations, and resolvers
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
How to extend the Deenruv GraphQL API with new types, queries, mutations, and resolvers
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
How to add, update, and manage i18n translations in Deenruv plugins and admin UI
Build admin UI components and pages using @deenruv/react-ui-devkit (shadcn/Tailwind-based)
Step-by-step guide to create a new Deenruv plugin with server and UI components
Write E2E tests for Deenruv plugins using Vitest and @deenruv/testing utilities
| name | graphql-extend |
| description | How to extend the Deenruv GraphQL API with new types, queries, mutations, and resolvers |
Use this skill when:
plugin-server/) — NestJS resolvers + gql schema extensionsplugin-ui/) — Zeus typed client for type-safe queries in React// plugin-server/extensions/admin-api.extension.ts
import { gql } from "graphql-tag";
import { DocumentNode } from "graphql";
export const AdminAPIExtension: DocumentNode = gql`
type Feature {
id: ID!
name: String!
enabled: Boolean!
createdAt: DateTime!
}
input CreateFeatureInput {
name: String!
enabled: Boolean
}
type FeatureList implements PaginatedList {
items: [Feature!]!
totalItems: Int!
}
input FeatureFilterParameter {
id: IDOperators
name: StringOperators
createdAt: DateOperators
_and: [FeatureFilterParameter!]
_or: [FeatureFilterParameter!]
}
input FeatureSortParameter {
id: SortOrder
name: SortOrder
createdAt: SortOrder
}
input FeatureListOptions {
skip: Int
take: Int
sort: FeatureSortParameter
filter: FeatureFilterParameter
filterOperator: LogicalOperator
}
extend type Query {
features(options: FeatureListOptions): FeatureList!
feature(id: ID!): Feature
}
extend type Mutation {
createFeature(input: CreateFeatureInput!): Feature!
deleteFeature(id: ID!): DeletionResponse!
}
`;
Conventions: implements PaginatedList for lists, explicit FilterParameter/SortParameter/ListOptions inputs, DeletionResponse for deletes, shared types in shared.extension.ts interpolated via ${SharedAPIExtension}.
// plugin-server/api/feature-admin.resolver.ts
import { Resolver, Query, Mutation, Args } from "@nestjs/graphql";
import { Allow, Ctx, Permission, RequestContext } from "@deenruv/core";
import { ModelTypes } from "../zeus/index.js";
import { FeatureService } from "../services/feature.service.js";
@Resolver()
export class FeatureAdminAPIResolver {
constructor(private readonly featureService: FeatureService) {}
@Query()
@Allow(Permission.ReadSettings)
async features(
@Ctx() ctx: RequestContext,
@Args() args: { options: ModelTypes["FeatureListOptions"] },
) {
return this.featureService.findAll(ctx, args.options);
}
@Query()
@Allow(Permission.ReadSettings)
async feature(@Ctx() ctx: RequestContext, @Args() args: { id: string }) {
return this.featureService.findOne(ctx, args.id);
}
@Mutation()
@Allow(Permission.UpdateSettings)
async createFeature(
@Ctx() ctx: RequestContext,
@Args() args: { input: ModelTypes["CreateFeatureInput"] },
) {
return this.featureService.create(ctx, args.input);
}
}
Conventions: @Allow(Permission.X) on every query/mutation, @Ctx() ctx: RequestContext for user/channel access, ModelTypes["TypeName"] from plugin's zeus/index.js for typed args, @Relations(Entity) relations: RelationPaths<Entity> for relation loading.
// plugin-server/index.ts
import { PluginCommonModule, DeenruvPlugin } from "@deenruv/core";
import { AdminAPIExtension } from "./extensions/admin-api.extension.js";
import { FeatureAdminAPIResolver } from "./api/feature-admin.resolver.js";
import { FeatureService } from "./services/feature.service.js";
import { FeatureEntity } from "./entities/feature.entity.js";
@DeenruvPlugin({
compatibility: "0.0.1",
imports: [PluginCommonModule],
entities: [FeatureEntity],
adminApiExtensions: {
schema: AdminAPIExtension,
resolvers: [FeatureAdminAPIResolver],
},
// shopApiExtensions: { schema: ShopAPIExtension, resolvers: [...] },
providers: [FeatureService],
})
export class FeaturePlugin {}
Server must be running with the plugin loaded:
zeus http://localhost:3000/admin-api ./src/plugin-ui --td
Generates zeus/index.ts, zeus/const.ts, and zeus/typedDocumentNode.ts.
// plugin-ui/graphql/selectors.ts
import { Selector } from "../zeus/index.js";
export const FeatureListSelector = Selector("Feature")({
id: true, name: true, enabled: true, createdAt: true,
});
export const FeatureDetailSelector = Selector("Feature")({
id: true, name: true, enabled: true, createdAt: true,
relatedItems: { id: true, name: true },
});
// plugin-ui/graphql/queries.ts
import { typedGql } from "../zeus/typedDocumentNode.js";
import { $ } from "../zeus/index.js";
import { scalars } from "@deenruv/admin-types";
import { FeatureListSelector, FeatureDetailSelector } from "./selectors.js";
export const FeaturesQuery = typedGql("query", { scalars })({
features: [
{ options: $("options", "FeatureListOptions!") },
{ items: FeatureListSelector, totalItems: true },
],
});
export const FeatureQuery = typedGql("query", { scalars })({
feature: [{ id: $("id", "ID!") }, FeatureDetailSelector],
});
// plugin-ui/graphql/mutations.ts
import { typedGql } from "../zeus/typedDocumentNode.js";
import { $ } from "../zeus/index.js";
import { scalars } from "@deenruv/admin-types";
export const CreateFeatureMutation = typedGql("mutation", { scalars })({
createFeature: [
{ input: $("input", "CreateFeatureInput!") },
{ id: true, name: true },
],
});
// For Boolean returns, select `true` directly
export const DeleteFeatureMutation = typedGql("mutation", { scalars })({
deleteFeature: [{ id: $("id", "ID!") }, { result: true, message: true }],
});
import { useQuery, useMutation, useTranslation } from "@deenruv/react-ui-devkit";
import { FeaturesQuery } from "../graphql/queries";
import { CreateFeatureMutation } from "../graphql/mutations";
export const FeatureList = () => {
const { t } = useTranslation();
const { data, loading } = useQuery(FeaturesQuery, {
variables: { options: { take: 20, skip: 0 } },
});
const [createFeature] = useMutation(CreateFeatureMutation);
// render data.features.items
};
For core framework types (not plugin Zeus), run from repo root:
pnpm codegen
# Generates packages/common/src/generated-types.ts (Admin API)
# Generates packages/common/src/generated-shop-types.ts (Shop API)
Run after modifying core GraphQL schemas. Plugin Zeus types are generated separately as above.
gql tag, types/inputs defined@Resolver(), @Query(), @Mutation() decorators@Allow(Permission.X) on every query and mutation@DeenruvPlugin via adminApiExtensions/shopApiExtensionszeus http://localhost:3000/admin-api ./src/plugin-ui --tdplugin-ui/graphql/useQuery/useMutation from @deenruv/react-ui-devkitpnpm codegen run if core types affected