ワンクリックで
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