@webiny/mcp 0.0.0-unstable.f6dc066313
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Extension.d.ts +2 -0
- package/Extension.js +11 -0
- package/Extension.js.map +1 -0
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/agents/claude.d.ts +15 -0
- package/agents/claude.js +33 -0
- package/agents/claude.js.map +1 -0
- package/agents/cline.d.ts +17 -0
- package/agents/cline.js +29 -0
- package/agents/cline.js.map +1 -0
- package/agents/copilot.d.ts +17 -0
- package/agents/copilot.js +64 -0
- package/agents/copilot.js.map +1 -0
- package/agents/cursor.d.ts +15 -0
- package/agents/cursor.js +33 -0
- package/agents/cursor.js.map +1 -0
- package/agents/instructions.d.ts +7 -0
- package/agents/instructions.js +13 -0
- package/agents/instructions.js.map +1 -0
- package/agents/shared.d.ts +41 -0
- package/agents/shared.js +124 -0
- package/agents/shared.js.map +1 -0
- package/agents/windsurf.d.ts +15 -0
- package/agents/windsurf.js +33 -0
- package/agents/windsurf.js.map +1 -0
- package/cli/ConfigureMcp.d.ts +15 -0
- package/cli/ConfigureMcp.js +57 -0
- package/cli/ConfigureMcp.js.map +1 -0
- package/cli/McpServer.d.ts +12 -0
- package/cli/McpServer.js +239 -0
- package/cli/McpServer.js.map +1 -0
- package/index.d.ts +1 -0
- package/index.js +3 -0
- package/index.js.map +1 -0
- package/package.json +49 -0
- package/skills/admin-ui-extensions/SKILL.md +267 -0
- package/skills/cli-extensions/SKILL.md +133 -0
- package/skills/content-models/SKILL.md +306 -0
- package/skills/custom-graphql-api/SKILL.md +199 -0
- package/skills/dependency-injection/SKILL.md +252 -0
- package/skills/infrastructure-extensions/SKILL.md +192 -0
- package/skills/lifecycle-events/SKILL.md +203 -0
- package/skills/local-development/SKILL.md +245 -0
- package/skills/project-structure/SKILL.md +156 -0
- package/skills/webiny-sdk/SKILL.md +271 -0
- package/skills/website-builder/SKILL.md +384 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: webiny-content-models
|
|
3
|
+
context: webiny-extensions
|
|
4
|
+
description: >
|
|
5
|
+
Creating Headless CMS content models via code using the ModelFactory pattern.
|
|
6
|
+
Use this skill when the developer wants to create, modify, or understand content model
|
|
7
|
+
definitions, define fields and validators, set up reference fields between models,
|
|
8
|
+
configure field layouts, or work with the ModelFactory builder API. Also covers field types
|
|
9
|
+
(text, number, boolean, datetime, file, ref, object, richText) and validation (required,
|
|
10
|
+
unique, email, pattern, minLength, maxLength, gte, predefinedValues).
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Creating Content Models via Code
|
|
14
|
+
|
|
15
|
+
## TL;DR
|
|
16
|
+
|
|
17
|
+
Content models are created using the `ModelFactory` pattern. You define a class implementing `ModelFactory.Interface`, use the fluent `ModelFactory.Builder` API to declare fields, validators, layout, and API names, then export with `ModelFactory.createImplementation()`. Register in `webiny.config.tsx` as `<Api.Extension>`.
|
|
18
|
+
|
|
19
|
+
## The ModelFactory Pattern
|
|
20
|
+
|
|
21
|
+
Every code-based content model follows the same structure:
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { ModelFactory } from "webiny/api/cms/model";
|
|
25
|
+
|
|
26
|
+
class MyModelImpl implements ModelFactory.Interface {
|
|
27
|
+
async execute(builder: ModelFactory.Builder) {
|
|
28
|
+
return [
|
|
29
|
+
builder
|
|
30
|
+
.public({ modelId: "myModel", name: "My Model", group: "ungrouped" })
|
|
31
|
+
.description("Description of the model")
|
|
32
|
+
.fields(fields => ({
|
|
33
|
+
// field definitions here
|
|
34
|
+
}))
|
|
35
|
+
.layout([/* row definitions */])
|
|
36
|
+
.titleFieldId("fieldId")
|
|
37
|
+
.singularApiName("MyModel")
|
|
38
|
+
.pluralApiName("MyModels")
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const MyModel = ModelFactory.createImplementation({
|
|
44
|
+
implementation: MyModelImpl,
|
|
45
|
+
dependencies: []
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Register in `webiny.config.tsx`:
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
<Api.Extension src={"/extensions/MyModel.ts"} />
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Model Configuration Methods
|
|
56
|
+
|
|
57
|
+
| Method | Purpose |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `.public({ modelId, name, group })` | Creates a public model (accessible via Read API). `modelId` is the internal DB identifier. `group` organizes it in the Admin sidebar. |
|
|
60
|
+
| `.description("...")` | Model description shown in Admin UI |
|
|
61
|
+
| `.fields(fields => ({ ... }))` | Define all fields using the fluent field builder |
|
|
62
|
+
| `.layout([["field1", "field2"], ["field3"]])` | Arrange fields in rows in the Admin editor. Each inner array is one row. |
|
|
63
|
+
| `.titleFieldId("name")` | Which field to use as the entry's display title |
|
|
64
|
+
| `.descriptionFieldId("message")` | Which field to use as the entry's description |
|
|
65
|
+
| `.singularApiName("Product")` | Singular name for GraphQL queries (e.g., `getProduct`) |
|
|
66
|
+
| `.pluralApiName("Products")` | Plural name for GraphQL queries (e.g., `listProducts`) |
|
|
67
|
+
|
|
68
|
+
## Field Types
|
|
69
|
+
|
|
70
|
+
| Builder Method | Description | Common Renderers |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| `fields.text()` | Single-line text | `"textInput"` |
|
|
73
|
+
| `fields.longText()` | Multi-line text | `"textarea"` |
|
|
74
|
+
| `fields.richText()` | Rich text (Lexical) | `"lexicalTextInput"` |
|
|
75
|
+
| `fields.number()` | Numeric value | `"numberInput"` |
|
|
76
|
+
| `fields.boolean()` | True/false toggle | `"boolean"` |
|
|
77
|
+
| `fields.datetime()` | Date/time picker | `"dateTimeInput"` |
|
|
78
|
+
| `fields.file()` | File/image attachment | `"fileInput"` |
|
|
79
|
+
| `fields.ref()` | Reference to another model | `"refDialogSingle"`, `"refAdvancedMultiple"` |
|
|
80
|
+
| `fields.object()` | Nested object with sub-fields | `"objectInput"` |
|
|
81
|
+
|
|
82
|
+
## Field Validators (Chainable)
|
|
83
|
+
|
|
84
|
+
| Validator | Description | Example |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `.required("msg")` | Field is required | `.required("Name is required")` |
|
|
87
|
+
| `.unique()` | Value must be unique across entries | `.unique()` |
|
|
88
|
+
| `.email()` | Must be a valid email | `.email()` |
|
|
89
|
+
| `.pattern(regex, msg)` | Must match a regex | `.pattern("^[a-z0-9-]+$", "Lowercase and hyphens only")` |
|
|
90
|
+
| `.minLength(n)` | Minimum string length | `.minLength(2)` |
|
|
91
|
+
| `.maxLength(n)` | Maximum string length | `.maxLength(100)` |
|
|
92
|
+
| `.gte(n, msg)` | Greater than or equal (numbers) | `.gte(0, "Must be non-negative")` |
|
|
93
|
+
| `.predefinedValues([...])` | Restrict to predefined options | `.predefinedValues([{ label: "Work", value: "work" }])` |
|
|
94
|
+
|
|
95
|
+
## Field Configuration (Chainable)
|
|
96
|
+
|
|
97
|
+
| Method | Description |
|
|
98
|
+
|---|---|
|
|
99
|
+
| `.renderer("rendererName")` | Set the Admin UI renderer |
|
|
100
|
+
| `.label("Display Name")` | Field label in the editor |
|
|
101
|
+
| `.help("Helper text")` | Helper text shown below the field |
|
|
102
|
+
| `.list()` | Make the field accept multiple values (arrays) |
|
|
103
|
+
| `.models([{ modelId: "..." }])` | For `ref()` fields: which models can be referenced |
|
|
104
|
+
|
|
105
|
+
## Full Examples
|
|
106
|
+
|
|
107
|
+
### Product Category Model
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// extensions/ProductCategoryModel.ts
|
|
111
|
+
import { ModelFactory } from "webiny/api/cms/model";
|
|
112
|
+
|
|
113
|
+
export const PRODUCT_CATEGORY_MODEL_ID = "productCategory";
|
|
114
|
+
|
|
115
|
+
class ProductCategoryModelImpl implements ModelFactory.Interface {
|
|
116
|
+
async execute(builder: ModelFactory.Builder) {
|
|
117
|
+
return [
|
|
118
|
+
builder
|
|
119
|
+
.public({
|
|
120
|
+
modelId: PRODUCT_CATEGORY_MODEL_ID,
|
|
121
|
+
name: "Product Category",
|
|
122
|
+
group: "ungrouped"
|
|
123
|
+
})
|
|
124
|
+
.description("Product categories for organizing products")
|
|
125
|
+
.fields(fields => ({
|
|
126
|
+
name: fields
|
|
127
|
+
.text()
|
|
128
|
+
.renderer("textInput")
|
|
129
|
+
.label("Name")
|
|
130
|
+
.help("Name of the product category")
|
|
131
|
+
.required("Name is required")
|
|
132
|
+
.minLength(2)
|
|
133
|
+
.maxLength(100),
|
|
134
|
+
slug: fields
|
|
135
|
+
.text()
|
|
136
|
+
.renderer("textInput")
|
|
137
|
+
.label("Slug")
|
|
138
|
+
.help("URL-friendly identifier")
|
|
139
|
+
.required("Slug is required")
|
|
140
|
+
.unique(),
|
|
141
|
+
description: fields
|
|
142
|
+
.longText()
|
|
143
|
+
.renderer("textarea")
|
|
144
|
+
.label("Description")
|
|
145
|
+
.minLength(10)
|
|
146
|
+
}))
|
|
147
|
+
.layout([["name", "slug"], ["description"]])
|
|
148
|
+
.titleFieldId("name")
|
|
149
|
+
.singularApiName("ProductCategory")
|
|
150
|
+
.pluralApiName("ProductCategories")
|
|
151
|
+
];
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export const ProductCategoryModel = ModelFactory.createImplementation({
|
|
156
|
+
implementation: ProductCategoryModelImpl,
|
|
157
|
+
dependencies: []
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Product Model (with Reference Field)
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// extensions/ProductModel.ts
|
|
165
|
+
import { ModelFactory } from "webiny/api/cms/model";
|
|
166
|
+
|
|
167
|
+
export const PRODUCT_MODEL_ID = "product";
|
|
168
|
+
|
|
169
|
+
class ProductModelImpl implements ModelFactory.Interface {
|
|
170
|
+
async execute(builder: ModelFactory.Builder) {
|
|
171
|
+
return [
|
|
172
|
+
builder
|
|
173
|
+
.public({
|
|
174
|
+
modelId: PRODUCT_MODEL_ID,
|
|
175
|
+
name: "Product",
|
|
176
|
+
group: "ungrouped"
|
|
177
|
+
})
|
|
178
|
+
.description("Products for our e-commerce store")
|
|
179
|
+
.fields(fields => ({
|
|
180
|
+
name: fields
|
|
181
|
+
.text()
|
|
182
|
+
.renderer("textInput")
|
|
183
|
+
.label("Name")
|
|
184
|
+
.help("Product name")
|
|
185
|
+
.required("Name is required"),
|
|
186
|
+
sku: fields
|
|
187
|
+
.text()
|
|
188
|
+
.renderer("textInput")
|
|
189
|
+
.label("SKU")
|
|
190
|
+
.help("Stock Keeping Unit - unique product identifier")
|
|
191
|
+
.required("SKU is required")
|
|
192
|
+
.unique(),
|
|
193
|
+
description: fields
|
|
194
|
+
.longText()
|
|
195
|
+
.renderer("textarea")
|
|
196
|
+
.label("Description")
|
|
197
|
+
.help("Detailed product description"),
|
|
198
|
+
price: fields
|
|
199
|
+
.number()
|
|
200
|
+
.renderer("numberInput")
|
|
201
|
+
.label("Price")
|
|
202
|
+
.required("Price is required")
|
|
203
|
+
.gte(0, "Price must be greater than or equal to 0"),
|
|
204
|
+
category: fields
|
|
205
|
+
.ref()
|
|
206
|
+
.renderer("refDialogSingle")
|
|
207
|
+
.label("Category")
|
|
208
|
+
.models([{ modelId: "productCategory" }])
|
|
209
|
+
}))
|
|
210
|
+
.layout([["name"], ["sku"], ["category"], ["description"], ["price"]])
|
|
211
|
+
.titleFieldId("name")
|
|
212
|
+
.singularApiName("Product")
|
|
213
|
+
.pluralApiName("Products")
|
|
214
|
+
];
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export const ProductModel = ModelFactory.createImplementation({
|
|
219
|
+
implementation: ProductModelImpl,
|
|
220
|
+
dependencies: []
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Contact Submission Model (with Predefined Values)
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
// extensions/contactSubmission/ContactSubmissionModel.ts
|
|
228
|
+
import { ModelFactory } from "webiny/api/cms/model";
|
|
229
|
+
|
|
230
|
+
export const CONTACT_SUBMISSION_MODEL_ID = "contactSubmission";
|
|
231
|
+
|
|
232
|
+
class ContactSubmissionModelImpl implements ModelFactory.Interface {
|
|
233
|
+
async execute(builder: ModelFactory.Builder) {
|
|
234
|
+
return [
|
|
235
|
+
builder
|
|
236
|
+
.public({
|
|
237
|
+
modelId: CONTACT_SUBMISSION_MODEL_ID,
|
|
238
|
+
name: "Contact Submission",
|
|
239
|
+
group: "ungrouped"
|
|
240
|
+
})
|
|
241
|
+
.description("Stores contact form submissions from the website")
|
|
242
|
+
.fields(fields => ({
|
|
243
|
+
name: fields
|
|
244
|
+
.text()
|
|
245
|
+
.renderer("textInput")
|
|
246
|
+
.label("Name")
|
|
247
|
+
.help("Enter your full name")
|
|
248
|
+
.required("Name is required")
|
|
249
|
+
.minLength(2)
|
|
250
|
+
.maxLength(100),
|
|
251
|
+
email: fields
|
|
252
|
+
.text()
|
|
253
|
+
.renderer("textInput")
|
|
254
|
+
.label("Email")
|
|
255
|
+
.help("Enter a valid email address")
|
|
256
|
+
.required("Email is required")
|
|
257
|
+
.email(),
|
|
258
|
+
message: fields
|
|
259
|
+
.longText()
|
|
260
|
+
.renderer("textarea")
|
|
261
|
+
.label("Message")
|
|
262
|
+
.help("Enter your message...")
|
|
263
|
+
.required("Message is required")
|
|
264
|
+
.minLength(10)
|
|
265
|
+
.maxLength(1000),
|
|
266
|
+
emailType: fields
|
|
267
|
+
.text()
|
|
268
|
+
.renderer("radioButtons")
|
|
269
|
+
.label("Email Type")
|
|
270
|
+
.help("Automatically classified as Work or Personal")
|
|
271
|
+
.predefinedValues([
|
|
272
|
+
{ label: "Work", value: "work" },
|
|
273
|
+
{ label: "Personal", value: "personal" }
|
|
274
|
+
])
|
|
275
|
+
}))
|
|
276
|
+
.layout([["name", "email"], ["message"], ["emailType"]])
|
|
277
|
+
.titleFieldId("name")
|
|
278
|
+
.descriptionFieldId("message")
|
|
279
|
+
.singularApiName("ContactSubmission")
|
|
280
|
+
.pluralApiName("ContactSubmissions")
|
|
281
|
+
];
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export const ContactSubmissionModel = ModelFactory.createImplementation({
|
|
286
|
+
implementation: ContactSubmissionModelImpl,
|
|
287
|
+
dependencies: []
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Quick Reference
|
|
292
|
+
|
|
293
|
+
```
|
|
294
|
+
Import: import { ModelFactory } from "webiny/api/cms/model";
|
|
295
|
+
Interface: ModelFactory.Interface
|
|
296
|
+
Builder: ModelFactory.Builder
|
|
297
|
+
Export: ModelFactory.createImplementation({ implementation, dependencies })
|
|
298
|
+
Register: <Api.Extension src={"/extensions/MyModel.ts"} />
|
|
299
|
+
Deploy: yarn webiny deploy api (or use watch mode)
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Related Skills
|
|
303
|
+
|
|
304
|
+
- `dependency-injection` -- The `createImplementation` pattern used here
|
|
305
|
+
- `lifecycle-events` -- Hook into create/update/delete events on your models
|
|
306
|
+
- `webiny-sdk` -- Query and write data to your models from external apps
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: webiny-custom-graphql-api
|
|
3
|
+
context: webiny-extensions
|
|
4
|
+
description: >
|
|
5
|
+
Adding custom GraphQL queries and mutations using GraphQLSchemaFactory.
|
|
6
|
+
Use this skill when the developer wants to add custom GraphQL endpoints, create custom
|
|
7
|
+
queries or mutations, add business logic to the API layer, build custom resolvers,
|
|
8
|
+
or inject backend services (identity, tenancy, CMS use-cases) into their GraphQL schema.
|
|
9
|
+
Covers the full pattern from simple queries to complex resolvers with dependency injection.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Custom GraphQL API
|
|
13
|
+
|
|
14
|
+
## TL;DR
|
|
15
|
+
|
|
16
|
+
Add custom GraphQL queries and mutations using the `GraphQLSchemaFactory` pattern. Define `typeDefs` and `resolvers`, inject backend services (identity, tenancy, CMS use-cases) via constructor DI, and export with `GraphQLSchemaFactory.createImplementation()`. Register in `webiny.config.tsx` as `<Api.Extension>`.
|
|
17
|
+
|
|
18
|
+
## The GraphQLSchemaFactory Pattern
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
// extensions/MyGraphQLSchema.ts
|
|
22
|
+
import { GraphQLSchemaFactory } from "webiny/api/graphql";
|
|
23
|
+
|
|
24
|
+
class SchemaImpl implements GraphQLSchemaFactory.Interface {
|
|
25
|
+
execute(): GraphQLSchemaFactory.Return {
|
|
26
|
+
return [
|
|
27
|
+
{
|
|
28
|
+
typeDefs: /* GraphQL */ `
|
|
29
|
+
type Query {
|
|
30
|
+
hello: String
|
|
31
|
+
}
|
|
32
|
+
`,
|
|
33
|
+
resolvers: {
|
|
34
|
+
Query: {
|
|
35
|
+
hello: () => "Hello, World!"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default GraphQLSchemaFactory.createImplementation({
|
|
44
|
+
implementation: SchemaImpl,
|
|
45
|
+
dependencies: []
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Register in `webiny.config.tsx`:
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
<Api.Extension src={"/extensions/MyGraphQLSchema.ts"} />
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Using Dependency Injection
|
|
56
|
+
|
|
57
|
+
Inject backend services to access user identity, tenant info, or CMS data:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
// extensions/MyGraphQLSchema.ts
|
|
61
|
+
import { GraphQLSchemaFactory } from "webiny/api/graphql";
|
|
62
|
+
import { IdentityContext } from "webiny/api/security";
|
|
63
|
+
|
|
64
|
+
class SchemaImpl implements GraphQLSchemaFactory.Interface {
|
|
65
|
+
constructor(private identityContext: IdentityContext.Interface) {}
|
|
66
|
+
|
|
67
|
+
execute(): GraphQLSchemaFactory.Return {
|
|
68
|
+
return [
|
|
69
|
+
{
|
|
70
|
+
typeDefs: /* GraphQL */ `
|
|
71
|
+
type Query {
|
|
72
|
+
whoAmI: String
|
|
73
|
+
}
|
|
74
|
+
`,
|
|
75
|
+
resolvers: {
|
|
76
|
+
Query: {
|
|
77
|
+
whoAmI: () => {
|
|
78
|
+
const identity = this.identityContext.getIdentity();
|
|
79
|
+
return `Hello, ${identity.displayName}!`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export default GraphQLSchemaFactory.createImplementation({
|
|
89
|
+
implementation: SchemaImpl,
|
|
90
|
+
dependencies: [IdentityContext]
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Injecting CMS Use-Cases
|
|
95
|
+
|
|
96
|
+
You can inject Headless CMS features to read/write content entries from within your custom resolvers:
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { GraphQLSchemaFactory } from "webiny/api/graphql";
|
|
100
|
+
import { ListLatestEntriesUseCase } from "@webiny/api-headless-cms/features/contentEntry/ListEntries";
|
|
101
|
+
import { GetModelUseCase } from "@webiny/api-headless-cms/features/contentModel/GetModel";
|
|
102
|
+
|
|
103
|
+
class SchemaImpl implements GraphQLSchemaFactory.Interface {
|
|
104
|
+
constructor(
|
|
105
|
+
private listEntries: ListLatestEntriesUseCase.Interface,
|
|
106
|
+
private getModel: GetModelUseCase.Interface
|
|
107
|
+
) {}
|
|
108
|
+
|
|
109
|
+
execute(): GraphQLSchemaFactory.Return {
|
|
110
|
+
return [
|
|
111
|
+
{
|
|
112
|
+
typeDefs: /* GraphQL */ `
|
|
113
|
+
type ProductSummary {
|
|
114
|
+
totalCount: Int
|
|
115
|
+
}
|
|
116
|
+
type Query {
|
|
117
|
+
productSummary: ProductSummary
|
|
118
|
+
}
|
|
119
|
+
`,
|
|
120
|
+
resolvers: {
|
|
121
|
+
Query: {
|
|
122
|
+
productSummary: async () => {
|
|
123
|
+
const model = await this.getModel.execute("product");
|
|
124
|
+
const result = await this.listEntries.execute(model, {
|
|
125
|
+
limit: 1000
|
|
126
|
+
});
|
|
127
|
+
return { totalCount: result.items.length };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export default GraphQLSchemaFactory.createImplementation({
|
|
137
|
+
implementation: SchemaImpl,
|
|
138
|
+
dependencies: [ListLatestEntriesUseCase, GetModelUseCase]
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Available Injectable Services
|
|
143
|
+
|
|
144
|
+
### Core Features
|
|
145
|
+
|
|
146
|
+
| Feature | Import Path | Purpose |
|
|
147
|
+
|---|----------------------------------------------|---|
|
|
148
|
+
| `IdentityContext` | `"webiny/api/security"` | Access current user identity and permissions |
|
|
149
|
+
| `TenantContext` | `"@webiny/api-core/features/TenantContext"` | Access current tenant information |
|
|
150
|
+
| `EventPublisher` | `"@webiny/api-core/features/EventPublisher"` | Publish domain events |
|
|
151
|
+
| `WcpContext` | `"@webiny/api-core/features/WcpContext"` | Webiny Control Panel integration |
|
|
152
|
+
| `Logger` | `"webiny/api/logger"` | Logging (persists to CloudWatch) |
|
|
153
|
+
| `BuildParams` | `"webiny/api/build-params"` | Access build-time parameters |
|
|
154
|
+
|
|
155
|
+
### Headless CMS Use-Cases
|
|
156
|
+
|
|
157
|
+
| Feature | Import Path | Purpose |
|
|
158
|
+
|---|---|---|
|
|
159
|
+
| `GetEntryByIdUseCase` | `"@webiny/api-headless-cms/features/contentEntry/GetEntryById"` | Fetch entry by exact revision ID |
|
|
160
|
+
| `GetEntryUseCase` | `"@webiny/api-headless-cms/features/contentEntry/GetEntry"` | Get entry by query (where + sort) |
|
|
161
|
+
| `ListLatestEntriesUseCase` | `"@webiny/api-headless-cms/features/contentEntry/ListEntries"` | List latest entries |
|
|
162
|
+
| `ListPublishedEntriesUseCase` | `"@webiny/api-headless-cms/features/contentEntry/ListEntries"` | List published entries |
|
|
163
|
+
| `CreateEntryUseCase` | `"@webiny/api-headless-cms/features/contentEntry/CreateEntry"` | Create a new entry |
|
|
164
|
+
| `UpdateEntryUseCase` | `"@webiny/api-headless-cms/features/contentEntry/UpdateEntry"` | Update an existing entry |
|
|
165
|
+
| `DeleteEntryUseCase` | `"@webiny/api-headless-cms/features/contentEntry/DeleteEntry"` | Delete an entry |
|
|
166
|
+
| `GetModelUseCase` | `"@webiny/api-headless-cms/features/contentModel/GetModel"` | Retrieve a content model by ID |
|
|
167
|
+
| `ListModelsUseCase` | `"@webiny/api-headless-cms/features/contentModel/ListModels"` | List all accessible models |
|
|
168
|
+
|
|
169
|
+
### Settings
|
|
170
|
+
|
|
171
|
+
| Feature | Import Path | Purpose |
|
|
172
|
+
|---|---|---|
|
|
173
|
+
| `GetSettings` | `"@webiny/api-core/features/settings/GetSettings"` | Retrieve settings by name |
|
|
174
|
+
| `UpdateSettings` | `"@webiny/api-core/features/settings/UpdateSettings"` | Create or update settings |
|
|
175
|
+
|
|
176
|
+
### Tenancy
|
|
177
|
+
|
|
178
|
+
| Feature | Import Path | Purpose |
|
|
179
|
+
|---|---|---|
|
|
180
|
+
| `GetTenantByIdUseCase` | `"@webiny/api-core/features/tenancy/GetTenantById"` | Fetch a tenant by ID |
|
|
181
|
+
| `CreateTenantUseCase` | `"@webiny/api-core/features/tenancy/CreateTenant"` | Create a new tenant |
|
|
182
|
+
| `UpdateTenantUseCase` | `"@webiny/api-core/features/tenancy/UpdateTenant"` | Update a tenant |
|
|
183
|
+
| `DeleteTenantUseCase` | `"@webiny/api-core/features/tenancy/DeleteTenant"` | Delete a tenant |
|
|
184
|
+
|
|
185
|
+
## Quick Reference
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
Import: import { GraphQLSchemaFactory } from "webiny/api/graphql";
|
|
189
|
+
Interface: GraphQLSchemaFactory.Interface
|
|
190
|
+
Return type: GraphQLSchemaFactory.Return
|
|
191
|
+
Export: GraphQLSchemaFactory.createImplementation({ implementation, dependencies })
|
|
192
|
+
Register: <Api.Extension src={"/extensions/MyGraphQLSchema.ts"} />
|
|
193
|
+
Deploy: yarn webiny deploy api
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Related Skills
|
|
197
|
+
|
|
198
|
+
- `dependency-injection` -- Full DI reference for all injectable services
|
|
199
|
+
- `project-structure` -- How to register extensions in `webiny.config.tsx`
|