@akanjs/cli 0.9.60-canary.8 → 1.0.0
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/cjs/index.js +16 -47
- package/cjs/src/guidelines/scalarConstant/scalarConstant.generate.json +24 -20
- package/cjs/src/guidelines/scalarConstant/scalarConstant.instruction.md +284 -326
- package/cjs/src/guidelines/scalarDictionary/scalarDictionary.generate.json +32 -32
- package/cjs/src/guidelines/scalarDictionary/scalarDictionary.instruction.md +175 -249
- package/cjs/src/templates/app/app/[lang]/page.js +19 -36
- package/cjs/src/templates/workspaceRoot/package.json.template +5 -5
- package/esm/index.js +16 -47
- package/esm/src/guidelines/scalarConstant/scalarConstant.generate.json +24 -20
- package/esm/src/guidelines/scalarConstant/scalarConstant.instruction.md +284 -326
- package/esm/src/guidelines/scalarDictionary/scalarDictionary.generate.json +32 -32
- package/esm/src/guidelines/scalarDictionary/scalarDictionary.instruction.md +175 -249
- package/esm/src/templates/app/app/[lang]/page.js +19 -36
- package/esm/src/templates/workspaceRoot/package.json.template +5 -5
- package/package.json +5 -4
- package/src/guidelines/scalarConstant/scalarConstant.instruction.md +284 -326
- package/src/guidelines/scalarDictionary/scalarDictionary.instruction.md +175 -249
- package/src/library/library.command.d.ts +0 -2
- package/src/library/library.runner.d.ts +0 -2
- package/src/library/library.script.d.ts +0 -2
- package/src/scalar/scalar.command.d.ts +1 -1
- package/cjs/src/guidelines/fieldDecorator/fieldDecorator.generate.json +0 -135
- package/cjs/src/guidelines/fieldDecorator/fieldDecorator.instruction.md +0 -606
- package/esm/src/guidelines/fieldDecorator/fieldDecorator.generate.json +0 -135
- package/esm/src/guidelines/fieldDecorator/fieldDecorator.instruction.md +0 -606
- package/src/guidelines/fieldDecorator/fieldDecorator.instruction.md +0 -606
|
@@ -1,606 +0,0 @@
|
|
|
1
|
-
# Field Decorator Usage Guide for Akan.js
|
|
2
|
-
|
|
3
|
-
## Purpose of Field Decorators
|
|
4
|
-
|
|
5
|
-
Field decorators in Akan.js serve as the foundation for data modeling by:
|
|
6
|
-
|
|
7
|
-
- Defining properties in model classes with type safety
|
|
8
|
-
- Configuring GraphQL schema generation for API endpoints
|
|
9
|
-
- Setting up MongoDB schema properties and validation
|
|
10
|
-
- Enabling search indexing and optimization
|
|
11
|
-
- Managing relationships between data models
|
|
12
|
-
- Controlling data visibility and access patterns
|
|
13
|
-
|
|
14
|
-
## Decorator Types and Their Uses
|
|
15
|
-
|
|
16
|
-
| Decorator | Purpose | Database | GraphQL | Default Behavior |
|
|
17
|
-
| ------------------ | ------------------------------------- | ------------- | ---------- | ----------------------------------- |
|
|
18
|
-
| `@Field.Prop()` | Standard visible property | ✅ Stored | ✅ Exposed | Included in all operations |
|
|
19
|
-
| `@Field.Hidden()` | Internal data not exposed in API | ✅ Stored | ❌ Hidden | Excluded from GraphQL schema |
|
|
20
|
-
| `@Field.Secret()` | Sensitive data with restricted access | ✅ Stored | ✅ Exposed | Not selected by default in queries |
|
|
21
|
-
| `@Field.Resolve()` | Computed property (no DB storage) | ❌ Not stored | ✅ Exposed | Calculated at runtime via resolvers |
|
|
22
|
-
|
|
23
|
-
## Basic Syntax and Structure
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
@Field.Prop(() => Type, { ...options })
|
|
27
|
-
propertyName: TypeScriptType;
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### Key Components
|
|
31
|
-
|
|
32
|
-
1. **Decorator Function**: Determines field behavior (`Field.Prop`, `Field.Hidden`, etc.)
|
|
33
|
-
2. **Type Arrow Function**: Specifies GraphQL/MongoDB type using `() => Type` syntax
|
|
34
|
-
3. **Options Object**: Configures field behavior with various options
|
|
35
|
-
4. **TypeScript Type**: Defines the type for static analysis and development
|
|
36
|
-
|
|
37
|
-
## Field.Prop for Standard Properties
|
|
38
|
-
|
|
39
|
-
Used for typical model properties that should be stored in the database and exposed in the GraphQL API.
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
// String property
|
|
43
|
-
@Field.Prop(() => String)
|
|
44
|
-
name: string;
|
|
45
|
-
|
|
46
|
-
// Number property with validation
|
|
47
|
-
@Field.Prop(() => Int, { min: 0, max: 100 })
|
|
48
|
-
score: number;
|
|
49
|
-
|
|
50
|
-
// Boolean property with default
|
|
51
|
-
@Field.Prop(() => Boolean, { default: false })
|
|
52
|
-
isActive: boolean;
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Field.Hidden for Database-Only Properties
|
|
56
|
-
|
|
57
|
-
Used for properties that should be stored in the database but not exposed in the GraphQL API.
|
|
58
|
-
|
|
59
|
-
```typescript
|
|
60
|
-
// Internal tracking data
|
|
61
|
-
@Field.Hidden(() => String)
|
|
62
|
-
internalId: string;
|
|
63
|
-
|
|
64
|
-
// Technical metadata
|
|
65
|
-
@Field.Hidden(() => JSON, { default: {} })
|
|
66
|
-
systemMetadata: Record<string, any>;
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## Field.Secret for Sensitive Data
|
|
70
|
-
|
|
71
|
-
Used for sensitive properties that are stored but not selected by default in queries.
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
74
|
-
// Personal information
|
|
75
|
-
@Field.Secret(() => String, { nullable: true })
|
|
76
|
-
phoneNumber: string | null;
|
|
77
|
-
|
|
78
|
-
// Security data
|
|
79
|
-
@Field.Secret(() => String)
|
|
80
|
-
apiKey: string;
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## Field.Resolve for Computed Properties
|
|
84
|
-
|
|
85
|
-
Used for properties that are calculated at runtime and not stored in the database.
|
|
86
|
-
|
|
87
|
-
```typescript
|
|
88
|
-
// Computed property from other fields
|
|
89
|
-
@Field.Resolve(() => String)
|
|
90
|
-
get fullName(): string {
|
|
91
|
-
return `${this.firstName} ${this.lastName}`;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Calculated numeric value
|
|
95
|
-
@Field.Resolve(() => Int)
|
|
96
|
-
get age(): number {
|
|
97
|
-
return dayjs().diff(this.birthDate, 'year');
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
## Field Options Reference
|
|
102
|
-
|
|
103
|
-
| Option | Type | Description | Default | Example |
|
|
104
|
-
| ------------ | ----------------------------------- | ------------------------------------ | ------- | ---------------------------------- |
|
|
105
|
-
| `default` | `any` | Default value | `null` | `{ default: "active" }` |
|
|
106
|
-
| `nullable` | `boolean` | Allows null values | `false` | `{ nullable: true }` |
|
|
107
|
-
| `enum` | `Enum` | Restricts to enum values | - | `{ enum: StatusEnum }` |
|
|
108
|
-
| `immutable` | `boolean` | Prevents modification after creation | `false` | `{ immutable: true }` |
|
|
109
|
-
| `min` | `number` | Minimum value (numbers) | - | `{ min: 0 }` |
|
|
110
|
-
| `max` | `number` | Maximum value (numbers) | - | `{ max: 100 }` |
|
|
111
|
-
| `minlength` | `number` | Minimum length (strings) | - | `{ minlength: 3 }` |
|
|
112
|
-
| `maxlength` | `number` | Maximum length (strings) | - | `{ maxlength: 255 }` |
|
|
113
|
-
| `type` | `"email"`, `"url"`, `"password"` | Special validation type | - | `{ type: "email" }` |
|
|
114
|
-
| `validate` | `function` | Custom validation function | - | `{ validate: isEmail }` |
|
|
115
|
-
| `select` | `boolean` | Include in default queries | `true` | `{ select: false }` |
|
|
116
|
-
| `ref` | `string` | Reference to another model | - | `{ ref: "User" }` |
|
|
117
|
-
| `refPath` | `string` | Dynamic reference path | - | `{ refPath: "modelType" }` |
|
|
118
|
-
| `refType` | `"child"`, `"parent"`, `"relation"` | Relationship type | - | `{ refType: "child" }` |
|
|
119
|
-
| `of` | `GqlScalar` | Type for Map values | - | `{ of: String }` |
|
|
120
|
-
| `text` | `"search"`, `"filter"` | Search indexing behavior | - | `{ text: "search" }` |
|
|
121
|
-
| `query` | `QueryOf<any>` | Default MongoDB query | - | `{ query: { active: true } }` |
|
|
122
|
-
| `accumulate` | `AccumulatorOperator` | MongoDB aggregation | - | `{ accumulate: { $sum: 1 } }` |
|
|
123
|
-
| `example` | `any` | Example value for docs | - | `{ example: "example@email.com" }` |
|
|
124
|
-
|
|
125
|
-
## Working with Primitive Types
|
|
126
|
-
|
|
127
|
-
### String Fields
|
|
128
|
-
|
|
129
|
-
```typescript
|
|
130
|
-
// Basic string
|
|
131
|
-
@Field.Prop(() => String)
|
|
132
|
-
name: string;
|
|
133
|
-
|
|
134
|
-
// With length constraints
|
|
135
|
-
@Field.Prop(() => String, { minlength: 3, maxlength: 50 })
|
|
136
|
-
username: string;
|
|
137
|
-
|
|
138
|
-
// With special validation type
|
|
139
|
-
@Field.Prop(() => String, { type: "email" })
|
|
140
|
-
email: string;
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### Number Fields
|
|
144
|
-
|
|
145
|
-
```typescript
|
|
146
|
-
// Integer
|
|
147
|
-
@Field.Prop(() => Int)
|
|
148
|
-
count: number;
|
|
149
|
-
|
|
150
|
-
// Float with range
|
|
151
|
-
@Field.Prop(() => Float, { min: 0, max: 1 })
|
|
152
|
-
percentage: number;
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Boolean Fields
|
|
156
|
-
|
|
157
|
-
```typescript
|
|
158
|
-
// Boolean with default
|
|
159
|
-
@Field.Prop(() => Boolean, { default: false })
|
|
160
|
-
isPublished: boolean;
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
## Working with Enums
|
|
164
|
-
|
|
165
|
-
```typescript
|
|
166
|
-
// Define enum with enumOf
|
|
167
|
-
export const Status = enumOf(["active", "inactive", "pending"] as const);
|
|
168
|
-
export type Status = enumOf<typeof Status>;
|
|
169
|
-
|
|
170
|
-
// Use in field
|
|
171
|
-
@Field.Prop(() => String, { enum: Status, default: "active" })
|
|
172
|
-
status: Status;
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
## Working with Arrays and Nested Structures
|
|
176
|
-
|
|
177
|
-
### Simple Arrays
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
// String array
|
|
181
|
-
@Field.Prop(() => [String], { default: [] })
|
|
182
|
-
tags: string[];
|
|
183
|
-
|
|
184
|
-
// Number array
|
|
185
|
-
@Field.Prop(() => [Int], { default: [] })
|
|
186
|
-
scores: number[];
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### Nested Arrays
|
|
190
|
-
|
|
191
|
-
```typescript
|
|
192
|
-
// 2D array
|
|
193
|
-
@Field.Prop(() => [[Int]], { default: [] })
|
|
194
|
-
matrix: number[][];
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### Arrays of Custom Types
|
|
198
|
-
|
|
199
|
-
```typescript
|
|
200
|
-
// Array of references
|
|
201
|
-
@Field.Prop(() => [LightComment], { ref: "Comment" })
|
|
202
|
-
comments: LightComment[];
|
|
203
|
-
|
|
204
|
-
// Array of embedded scalars
|
|
205
|
-
@Field.Prop(() => [Address])
|
|
206
|
-
addresses: Address[];
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
## Reference Fields and Relationships
|
|
210
|
-
|
|
211
|
-
### Single References
|
|
212
|
-
|
|
213
|
-
```typescript
|
|
214
|
-
// Basic reference
|
|
215
|
-
@Field.Prop(() => LightUser, { ref: "User" })
|
|
216
|
-
owner: LightUser;
|
|
217
|
-
|
|
218
|
-
// Optional reference
|
|
219
|
-
@Field.Prop(() => LightCategory, { ref: "Category", nullable: true })
|
|
220
|
-
category: LightCategory | null;
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
### Dynamic References
|
|
224
|
-
|
|
225
|
-
```typescript
|
|
226
|
-
// Reference determined by path
|
|
227
|
-
@Field.Prop(() => String, { enum: ModelType })
|
|
228
|
-
modelType: ModelType;
|
|
229
|
-
|
|
230
|
-
@Field.Prop(() => LightModel, { refPath: "modelType" })
|
|
231
|
-
model: LightModel;
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
### Relationship Types
|
|
235
|
-
|
|
236
|
-
```typescript
|
|
237
|
-
// Parent-child relationship
|
|
238
|
-
@Field.Prop(() => LightProject, { ref: "Project", refType: "parent" })
|
|
239
|
-
project: LightProject;
|
|
240
|
-
|
|
241
|
-
// Many-to-many relationship
|
|
242
|
-
@Field.Prop(() => [LightTag], { ref: "Tag", refType: "relation" })
|
|
243
|
-
tags: LightTag[];
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
## Date and Special Type Handling
|
|
247
|
-
|
|
248
|
-
### Date Fields
|
|
249
|
-
|
|
250
|
-
```typescript
|
|
251
|
-
// Current date default
|
|
252
|
-
@Field.Prop(() => Date, { default: () => dayjs() })
|
|
253
|
-
createdAt: Dayjs;
|
|
254
|
-
|
|
255
|
-
// Immutable timestamp
|
|
256
|
-
@Field.Prop(() => Date, { default: () => dayjs(), immutable: true })
|
|
257
|
-
createdAt: Dayjs;
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
### JSON and Map Types
|
|
261
|
-
|
|
262
|
-
```typescript
|
|
263
|
-
// JSON object
|
|
264
|
-
@Field.Prop(() => JSON, { default: {} })
|
|
265
|
-
metadata: Record<string, any>;
|
|
266
|
-
|
|
267
|
-
// Map type
|
|
268
|
-
@Field.Prop(() => Map, { of: String, default: new Map() })
|
|
269
|
-
translations: Map<string, string>;
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
## Validation and Constraints
|
|
273
|
-
|
|
274
|
-
### Built-in Validation
|
|
275
|
-
|
|
276
|
-
```typescript
|
|
277
|
-
// Numeric range
|
|
278
|
-
@Field.Prop(() => Int, { min: 1, max: 10 })
|
|
279
|
-
rating: number;
|
|
280
|
-
|
|
281
|
-
// String length
|
|
282
|
-
@Field.Prop(() => String, { minlength: 8, maxlength: 100 })
|
|
283
|
-
password: string;
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
### Custom Validation
|
|
287
|
-
|
|
288
|
-
```typescript
|
|
289
|
-
// Custom validation function
|
|
290
|
-
@Field.Prop(() => String, {
|
|
291
|
-
validate: (value, model) => {
|
|
292
|
-
return /^[A-Z][a-z]+$/.test(value);
|
|
293
|
-
}
|
|
294
|
-
})
|
|
295
|
-
properName: string;
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
## Search Optimization
|
|
299
|
-
|
|
300
|
-
### Full-Text Search
|
|
301
|
-
|
|
302
|
-
```typescript
|
|
303
|
-
// Searchable text field
|
|
304
|
-
@Field.Prop(() => String, { text: "search" })
|
|
305
|
-
description: string;
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
### Filter-Only Fields
|
|
309
|
-
|
|
310
|
-
```typescript
|
|
311
|
-
// Filterable but not searchable
|
|
312
|
-
@Field.Prop(() => String, { text: "filter" })
|
|
313
|
-
category: string;
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
## Default Values and Immutability
|
|
317
|
-
|
|
318
|
-
### Static Defaults
|
|
319
|
-
|
|
320
|
-
```typescript
|
|
321
|
-
// Static default value
|
|
322
|
-
@Field.Prop(() => String, { default: "Guest" })
|
|
323
|
-
role: string;
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
### Dynamic Defaults
|
|
327
|
-
|
|
328
|
-
```typescript
|
|
329
|
-
// Function-based default
|
|
330
|
-
@Field.Prop(() => Date, { default: () => dayjs() })
|
|
331
|
-
createdAt: Dayjs;
|
|
332
|
-
|
|
333
|
-
// Complex default value
|
|
334
|
-
@Field.Prop(() => JSON, { default: () => ({ version: 1, settings: {} }) })
|
|
335
|
-
config: Record<string, any>;
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
### Immutable Fields
|
|
339
|
-
|
|
340
|
-
```typescript
|
|
341
|
-
// Cannot be changed after creation
|
|
342
|
-
@Field.Prop(() => String, { immutable: true })
|
|
343
|
-
code: string;
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
## Integration with Model Types
|
|
347
|
-
|
|
348
|
-
### Input Models
|
|
349
|
-
|
|
350
|
-
```typescript
|
|
351
|
-
export class ProductInput {
|
|
352
|
-
@Field.Prop(() => String)
|
|
353
|
-
name: string;
|
|
354
|
-
|
|
355
|
-
@Field.Prop(() => Float, { min: 0 })
|
|
356
|
-
price: number;
|
|
357
|
-
}
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
### Object Models
|
|
361
|
-
|
|
362
|
-
```typescript
|
|
363
|
-
export class ProductObject extends via(ProductInput) {
|
|
364
|
-
@Field.Prop(() => Date, { default: dayjs() })
|
|
365
|
-
createdAt: Dayjs;
|
|
366
|
-
}
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
### Light Models
|
|
370
|
-
|
|
371
|
-
```typescript
|
|
372
|
-
export class LightProduct extends via(ProductObject, ["name", "price", "status"] as const) {}
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
### Full Models
|
|
376
|
-
|
|
377
|
-
```typescript
|
|
378
|
-
export class Product extends via(ProductObject, LightProduct) {}
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
### Scalar Models
|
|
382
|
-
|
|
383
|
-
```typescript
|
|
384
|
-
export class Address {
|
|
385
|
-
@Field.Prop(() => String)
|
|
386
|
-
street: string;
|
|
387
|
-
|
|
388
|
-
@Field.Prop(() => String)
|
|
389
|
-
city: string;
|
|
390
|
-
}
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
## Best Practices
|
|
394
|
-
|
|
395
|
-
### Type Safety
|
|
396
|
-
|
|
397
|
-
Always ensure TypeScript types match GraphQL types:
|
|
398
|
-
|
|
399
|
-
```typescript
|
|
400
|
-
// ✅ Correct
|
|
401
|
-
@Field.Prop(() => Int)
|
|
402
|
-
count: number;
|
|
403
|
-
|
|
404
|
-
// ❌ Incorrect (type mismatch)
|
|
405
|
-
@Field.Prop(() => String)
|
|
406
|
-
count: number;
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
### Nullable Fields
|
|
410
|
-
|
|
411
|
-
Always add both the decorator option and TypeScript union type:
|
|
412
|
-
|
|
413
|
-
```typescript
|
|
414
|
-
// ✅ Correct
|
|
415
|
-
@Field.Prop(() => String, { nullable: true })
|
|
416
|
-
middleName: string | null;
|
|
417
|
-
|
|
418
|
-
// ❌ Incorrect (missing union type)
|
|
419
|
-
@Field.Prop(() => String, { nullable: true })
|
|
420
|
-
middleName: string;
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
### Default Values
|
|
424
|
-
|
|
425
|
-
Provide sensible defaults for required fields:
|
|
426
|
-
|
|
427
|
-
```typescript
|
|
428
|
-
// ✅ Correct
|
|
429
|
-
@Field.Prop(() => [String], { default: [] })
|
|
430
|
-
tags: string[];
|
|
431
|
-
|
|
432
|
-
// ❌ Incorrect (missing default for array)
|
|
433
|
-
@Field.Prop(() => [String])
|
|
434
|
-
tags: string[];
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
### Sensitive Data
|
|
438
|
-
|
|
439
|
-
Use `Field.Secret` for sensitive information:
|
|
440
|
-
|
|
441
|
-
```typescript
|
|
442
|
-
// ✅ Correct
|
|
443
|
-
@Field.Secret(() => String)
|
|
444
|
-
apiKey: string;
|
|
445
|
-
|
|
446
|
-
// ❌ Incorrect (exposes sensitive data by default)
|
|
447
|
-
@Field.Prop(() => String)
|
|
448
|
-
apiKey: string;
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
### Array Type Syntax
|
|
452
|
-
|
|
453
|
-
Use bracket notation instead of generics:
|
|
454
|
-
|
|
455
|
-
```typescript
|
|
456
|
-
// ✅ Correct
|
|
457
|
-
@Field.Prop(() => [String])
|
|
458
|
-
tags: string[];
|
|
459
|
-
|
|
460
|
-
// ❌ Incorrect (using generics)
|
|
461
|
-
@Field.Prop(() => Array<String>)
|
|
462
|
-
tags: string[];
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
### Date Handling
|
|
466
|
-
|
|
467
|
-
Always use `Dayjs` type for date fields:
|
|
468
|
-
|
|
469
|
-
```typescript
|
|
470
|
-
// ✅ Correct
|
|
471
|
-
@Field.Prop(() => Date)
|
|
472
|
-
createdAt: Dayjs;
|
|
473
|
-
|
|
474
|
-
// ❌ Incorrect (using JavaScript Date)
|
|
475
|
-
@Field.Prop(() => Date)
|
|
476
|
-
createdAt: Date;
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
## Troubleshooting Common Issues
|
|
480
|
-
|
|
481
|
-
### Field Not Appearing in GraphQL
|
|
482
|
-
|
|
483
|
-
**Problem**: Field is defined but not showing up in GraphQL schema
|
|
484
|
-
**Possible Causes**:
|
|
485
|
-
|
|
486
|
-
- Using `@Field.Hidden()` instead of `@Field.Prop()`
|
|
487
|
-
- Field has `{ select: false }` option
|
|
488
|
-
- GraphQL schema not regenerated after changes
|
|
489
|
-
|
|
490
|
-
**Solution**:
|
|
491
|
-
|
|
492
|
-
```typescript
|
|
493
|
-
// Change this
|
|
494
|
-
@Field.Hidden(() => String)
|
|
495
|
-
name: string;
|
|
496
|
-
|
|
497
|
-
// To this
|
|
498
|
-
@Field.Prop(() => String)
|
|
499
|
-
name: string;
|
|
500
|
-
```
|
|
501
|
-
|
|
502
|
-
### Default Value Not Applied
|
|
503
|
-
|
|
504
|
-
**Problem**: Default value isn't set when creating new documents
|
|
505
|
-
**Possible Causes**:
|
|
506
|
-
|
|
507
|
-
- Default value type doesn't match field type
|
|
508
|
-
- Default function throws an error
|
|
509
|
-
- Middleware overriding the value
|
|
510
|
-
|
|
511
|
-
**Solution**:
|
|
512
|
-
|
|
513
|
-
```typescript
|
|
514
|
-
// Ensure type consistency
|
|
515
|
-
@Field.Prop(() => Int, { default: 0 }) // number default for number field
|
|
516
|
-
count: number;
|
|
517
|
-
```
|
|
518
|
-
|
|
519
|
-
### Validation Errors
|
|
520
|
-
|
|
521
|
-
**Problem**: Unexpected validation failures
|
|
522
|
-
**Possible Causes**:
|
|
523
|
-
|
|
524
|
-
- Value outside min/max range
|
|
525
|
-
- String length outside minlength/maxlength
|
|
526
|
-
- Custom validation function returning false
|
|
527
|
-
|
|
528
|
-
**Solution**:
|
|
529
|
-
|
|
530
|
-
```typescript
|
|
531
|
-
// Check constraints
|
|
532
|
-
@Field.Prop(() => String, {
|
|
533
|
-
minlength: 3,
|
|
534
|
-
validate: (value) => /^[A-Z]/.test(value) // Must start with uppercase
|
|
535
|
-
})
|
|
536
|
-
username: string;
|
|
537
|
-
```
|
|
538
|
-
|
|
539
|
-
### Enum Values Not Working
|
|
540
|
-
|
|
541
|
-
**Problem**: Enum validation not working properly
|
|
542
|
-
**Possible Causes**:
|
|
543
|
-
|
|
544
|
-
- Enum not created with `enumOf()`
|
|
545
|
-
- Enum values don't match expected format
|
|
546
|
-
- Missing `{ enum: YourEnum }` in options
|
|
547
|
-
|
|
548
|
-
**Solution**:
|
|
549
|
-
|
|
550
|
-
```typescript
|
|
551
|
-
// Proper enum definition
|
|
552
|
-
export const Status = enumOf(["active", "inactive"] as const);
|
|
553
|
-
export type Status = enumOf<typeof Status>;
|
|
554
|
-
|
|
555
|
-
// Correct field definition
|
|
556
|
-
@Field.Prop(() => String, { enum: Status, default: "active" })
|
|
557
|
-
status: Status;
|
|
558
|
-
```
|
|
559
|
-
|
|
560
|
-
### Search Not Working
|
|
561
|
-
|
|
562
|
-
**Problem**: Full-text search not working on fields
|
|
563
|
-
**Possible Causes**:
|
|
564
|
-
|
|
565
|
-
- Missing `{ text: "search" }` option
|
|
566
|
-
- Search daemon not running
|
|
567
|
-
- Model not marked for indexing
|
|
568
|
-
|
|
569
|
-
**Solution**:
|
|
570
|
-
|
|
571
|
-
```typescript
|
|
572
|
-
// Add text search option
|
|
573
|
-
@Field.Prop(() => String, { text: "search" })
|
|
574
|
-
description: string;
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
### Circular Dependencies
|
|
578
|
-
|
|
579
|
-
**Problem**: Circular import errors when referencing models
|
|
580
|
-
**Possible Causes**:
|
|
581
|
-
|
|
582
|
-
- Models directly importing each other
|
|
583
|
-
- Using Full models instead of Light models for references
|
|
584
|
-
|
|
585
|
-
**Solution**:
|
|
586
|
-
|
|
587
|
-
```typescript
|
|
588
|
-
// Use Light models for references
|
|
589
|
-
@Field.Prop(() => LightUser, { ref: "User" }) // Not FullUser
|
|
590
|
-
owner: LightUser;
|
|
591
|
-
```
|
|
592
|
-
|
|
593
|
-
## Summary Checklist
|
|
594
|
-
|
|
595
|
-
1. **Choose the right decorator**: `Field.Prop`, `Field.Hidden`, `Field.Secret`, or `Field.Resolve`
|
|
596
|
-
2. **Use correct type syntax**: `() => Type` or `() => [Type]` for arrays
|
|
597
|
-
3. **Match TypeScript types**: Ensure decorator type matches property type
|
|
598
|
-
4. **Handle nullability**: Use `{ nullable: true }` and `Type | null` together
|
|
599
|
-
5. **Provide defaults**: Set appropriate default values, especially for arrays
|
|
600
|
-
6. **Add validation**: Use min/max, minlength/maxlength, or custom validation
|
|
601
|
-
7. **Protect sensitive data**: Use `Field.Secret` for sensitive information
|
|
602
|
-
8. **Optimize for search**: Add `{ text: "search" }` to searchable fields
|
|
603
|
-
9. **Mark immutable fields**: Use `{ immutable: true }` for unchangeable fields
|
|
604
|
-
10. **Use proper references**: Specify `ref`, `refPath`, or `refType` for relationships
|
|
605
|
-
|
|
606
|
-
Following these guidelines will ensure your field decorators work correctly across the entire Akan.js framework ecosystem, from GraphQL APIs to MongoDB persistence and search functionality.
|