@qlover/create-app 0.7.14 → 0.7.15
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/CHANGELOG.md +23 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/next-app/README.en.md +131 -0
- package/dist/templates/next-app/README.md +115 -20
- package/dist/templates/next-app/docs/en/api.md +387 -0
- package/dist/templates/next-app/docs/en/component.md +544 -0
- package/dist/templates/next-app/docs/en/database.md +496 -0
- package/dist/templates/next-app/docs/en/development-guide.md +727 -0
- package/dist/templates/next-app/docs/en/env.md +563 -0
- package/dist/templates/next-app/docs/en/i18n.md +287 -0
- package/dist/templates/next-app/docs/en/index.md +166 -0
- package/dist/templates/next-app/docs/en/page.md +457 -0
- package/dist/templates/next-app/docs/en/project-structure.md +177 -0
- package/dist/templates/next-app/docs/en/router.md +427 -0
- package/dist/templates/next-app/docs/en/theme.md +532 -0
- package/dist/templates/next-app/docs/en/validator.md +478 -0
- package/dist/templates/next-app/docs/zh/api.md +387 -0
- package/dist/templates/next-app/docs/zh/component.md +544 -0
- package/dist/templates/next-app/docs/zh/database.md +496 -0
- package/dist/templates/next-app/docs/zh/development-guide.md +727 -0
- package/dist/templates/next-app/docs/zh/env.md +563 -0
- package/dist/templates/next-app/docs/zh/i18n.md +287 -0
- package/dist/templates/next-app/docs/zh/index.md +166 -0
- package/dist/templates/next-app/docs/zh/page.md +457 -0
- package/dist/templates/next-app/docs/zh/project-structure.md +177 -0
- package/dist/templates/next-app/docs/zh/router.md +427 -0
- package/dist/templates/next-app/docs/zh/theme.md +532 -0
- package/dist/templates/next-app/docs/zh/validator.md +476 -0
- package/package.json +1 -1
- package/dist/templates/next-app/docs/env.md +0 -94
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
# Database Development Guide
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
1. [Database Architecture Overview](#database-architecture-overview)
|
|
6
|
+
2. [Supabase Implementation](#supabase-implementation)
|
|
7
|
+
3. [Database Interfaces and Abstraction Layer](#database-interfaces-and-abstraction-layer)
|
|
8
|
+
4. [Repository Pattern Implementation](#repository-pattern-implementation)
|
|
9
|
+
5. [MongoDB Implementation Example](#mongodb-implementation-example)
|
|
10
|
+
6. [Best Practices and Examples](#best-practices-and-examples)
|
|
11
|
+
|
|
12
|
+
## Database Architecture Overview
|
|
13
|
+
|
|
14
|
+
### 1. Overall Architecture
|
|
15
|
+
|
|
16
|
+
The project adopts a layered database architecture design:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Application Layer Data Access Layer
|
|
20
|
+
┌──────────────┐ ┌──────────────┐
|
|
21
|
+
│Business Service│ │ DB Interface│
|
|
22
|
+
├──────────────┤ ├──────────────┤
|
|
23
|
+
│Repo Interface │ │ DB Bridge │
|
|
24
|
+
├──────────────┤ ◄─────┤ │
|
|
25
|
+
│Repo Implement │ │Implementation │
|
|
26
|
+
└──────────────┘ └──────────────┘
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 2. Core Components
|
|
30
|
+
|
|
31
|
+
- **Database Interface**: `DBBridgeInterface`
|
|
32
|
+
- **Database Implementation**: `SupabaseBridge`, `MongoDBBridge`, etc.
|
|
33
|
+
- **Repository Interface**: `UserRepositoryInterface`, etc.
|
|
34
|
+
- **Repository Implementation**: `UserRepository`, etc.
|
|
35
|
+
|
|
36
|
+
### 3. Data Models
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// User model example
|
|
40
|
+
interface UserSchema {
|
|
41
|
+
id: number;
|
|
42
|
+
email: string;
|
|
43
|
+
password: string;
|
|
44
|
+
role: string;
|
|
45
|
+
created_at: string;
|
|
46
|
+
updated_at: string;
|
|
47
|
+
email_confirmed_at?: string;
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Supabase Implementation
|
|
52
|
+
|
|
53
|
+
### 1. Database Bridge
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
@injectable()
|
|
57
|
+
export class SupabaseBridge implements DBBridgeInterface {
|
|
58
|
+
protected supabase: SupabaseClient;
|
|
59
|
+
|
|
60
|
+
constructor(
|
|
61
|
+
@inject(I.AppConfig) appConfig: AppConfig,
|
|
62
|
+
@inject(I.Logger) protected logger: LoggerInterface
|
|
63
|
+
) {
|
|
64
|
+
// Initialize Supabase client
|
|
65
|
+
this.supabase = createClient(
|
|
66
|
+
appConfig.supabaseUrl,
|
|
67
|
+
appConfig.supabaseAnonKey
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Query implementation
|
|
72
|
+
async get(event: BridgeEvent): Promise<SupabaseBridgeResponse<unknown>> {
|
|
73
|
+
const { table, fields = '*', where } = event;
|
|
74
|
+
const selectFields = Array.isArray(fields) ? fields.join(',') : fields;
|
|
75
|
+
const handler = this.supabase.from(table).select(selectFields);
|
|
76
|
+
|
|
77
|
+
this.handleWhere(handler, where ?? []);
|
|
78
|
+
|
|
79
|
+
return this.catch(await handler);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Pagination query implementation
|
|
83
|
+
async pagination(event: BridgeEvent): Promise<DBBridgeResponse<unknown[]>> {
|
|
84
|
+
const { table, fields = '*', where, page = 1, pageSize = 10 } = event;
|
|
85
|
+
const selectFields = Array.isArray(fields) ? fields.join(',') : fields;
|
|
86
|
+
|
|
87
|
+
const handler = this.supabase
|
|
88
|
+
.from(table)
|
|
89
|
+
.select(selectFields, { count: 'exact' })
|
|
90
|
+
.range((page - 1) * pageSize, page * pageSize - 1);
|
|
91
|
+
|
|
92
|
+
this.handleWhere(handler, where ?? []);
|
|
93
|
+
|
|
94
|
+
return this.catch(await handler);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// WHERE condition handling
|
|
98
|
+
protected handleWhere(
|
|
99
|
+
handler: PostgrestFilterBuilder<any, any, any>,
|
|
100
|
+
where: Where[]
|
|
101
|
+
): void {
|
|
102
|
+
where.forEach(([field, operation, value]) => {
|
|
103
|
+
const method = whereHandlerMaps[operation];
|
|
104
|
+
if (method) {
|
|
105
|
+
handler[method](field, value);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 2. Query Condition Mapping
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
// Supabase operator mapping
|
|
116
|
+
const whereHandlerMaps = {
|
|
117
|
+
'=': 'eq', // Equal
|
|
118
|
+
'!=': 'neq', // Not equal
|
|
119
|
+
'>': 'gt', // Greater than
|
|
120
|
+
'<': 'lt', // Less than
|
|
121
|
+
'>=': 'gte', // Greater than or equal
|
|
122
|
+
'<=': 'lte' // Less than or equal
|
|
123
|
+
};
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Database Interfaces and Abstraction Layer
|
|
127
|
+
|
|
128
|
+
### 1. Database Bridge Interface
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
export interface DBBridgeInterface {
|
|
132
|
+
// Basic CRUD operations
|
|
133
|
+
add(event: BridgeEvent): Promise<DBBridgeResponse<unknown>>;
|
|
134
|
+
update(event: BridgeEvent): Promise<DBBridgeResponse<unknown>>;
|
|
135
|
+
delete(event: BridgeEvent): Promise<DBBridgeResponse<unknown>>;
|
|
136
|
+
get(event: BridgeEvent): Promise<DBBridgeResponse<unknown>>;
|
|
137
|
+
|
|
138
|
+
// Pagination query
|
|
139
|
+
pagination(event: BridgeEvent): Promise<DBBridgeResponse<unknown[]>>;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Query event definition
|
|
143
|
+
export interface BridgeEvent {
|
|
144
|
+
table: string;
|
|
145
|
+
fields?: string | string[];
|
|
146
|
+
where?: Where[];
|
|
147
|
+
data?: unknown;
|
|
148
|
+
page?: number;
|
|
149
|
+
pageSize?: number;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Unified response format
|
|
153
|
+
export interface DBBridgeResponse<T> {
|
|
154
|
+
error?: unknown;
|
|
155
|
+
data: T;
|
|
156
|
+
pagination?: PaginationInfo;
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 2. Query Condition Types
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
// Operator types
|
|
164
|
+
export type WhereOperation = '=' | '!=' | '>' | '<' | '>=' | '<=';
|
|
165
|
+
|
|
166
|
+
// Query condition type
|
|
167
|
+
export type Where = [string, WhereOperation, string | number];
|
|
168
|
+
|
|
169
|
+
// Pagination information
|
|
170
|
+
export interface PaginationInfo {
|
|
171
|
+
total: number;
|
|
172
|
+
page: number;
|
|
173
|
+
pageSize: number;
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Repository Pattern Implementation
|
|
178
|
+
|
|
179
|
+
### 1. User Repository Interface
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
export interface UserRepositoryInterface {
|
|
183
|
+
// Get all users
|
|
184
|
+
getAll(): Promise<unknown>;
|
|
185
|
+
|
|
186
|
+
// Query user by email
|
|
187
|
+
getUserByEmail(email: string): Promise<UserSchema | null>;
|
|
188
|
+
|
|
189
|
+
// Add user
|
|
190
|
+
add(params: {
|
|
191
|
+
email: string;
|
|
192
|
+
password: string;
|
|
193
|
+
}): Promise<UserSchema[] | null>;
|
|
194
|
+
|
|
195
|
+
// Update user
|
|
196
|
+
updateById(
|
|
197
|
+
id: number,
|
|
198
|
+
params: Partial<Omit<UserSchema, 'id' | 'created_at'>>
|
|
199
|
+
): Promise<void>;
|
|
200
|
+
|
|
201
|
+
// Pagination query
|
|
202
|
+
pagination<UserSchema>(params: {
|
|
203
|
+
page: number;
|
|
204
|
+
pageSize: number;
|
|
205
|
+
}): Promise<PaginationInterface<UserSchema>>;
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### 2. User Repository Implementation
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
@injectable()
|
|
213
|
+
export class UserRepository implements UserRepositoryInterface {
|
|
214
|
+
readonly name = 'fe_users';
|
|
215
|
+
|
|
216
|
+
// Safe field list (excluding sensitive information)
|
|
217
|
+
protected safeFields = [
|
|
218
|
+
'created_at',
|
|
219
|
+
'email',
|
|
220
|
+
'email_confirmed_at',
|
|
221
|
+
'id',
|
|
222
|
+
'role',
|
|
223
|
+
'updated_at'
|
|
224
|
+
];
|
|
225
|
+
|
|
226
|
+
constructor(
|
|
227
|
+
@inject(I.DBBridgeInterface) protected dbBridge: DBBridgeInterface
|
|
228
|
+
) {}
|
|
229
|
+
|
|
230
|
+
async getUserByEmail(email: string): Promise<UserSchema | null> {
|
|
231
|
+
const result = await this.dbBridge.get({
|
|
232
|
+
table: this.name,
|
|
233
|
+
where: [['email', '=', email]]
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
return isEmpty(result.data) ? null : last(result.data as UserSchema[]);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async pagination<UserSchema>(
|
|
240
|
+
params: PaginationParams
|
|
241
|
+
): Promise<PaginationResult> {
|
|
242
|
+
const result = await this.dbBridge.pagination({
|
|
243
|
+
table: this.name,
|
|
244
|
+
page: params.page,
|
|
245
|
+
pageSize: params.pageSize,
|
|
246
|
+
fields: this.safeFields // Only return safe fields
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
list: result.data as UserSchema[],
|
|
251
|
+
total: result.count ?? 0,
|
|
252
|
+
page: params.page,
|
|
253
|
+
pageSize: params.pageSize
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## MongoDB Implementation Example
|
|
260
|
+
|
|
261
|
+
### 1. MongoDB Bridge
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
@injectable()
|
|
265
|
+
export class MongoDBBridge implements DBBridgeInterface {
|
|
266
|
+
protected client: MongoClient;
|
|
267
|
+
protected db: Db;
|
|
268
|
+
|
|
269
|
+
constructor(
|
|
270
|
+
@inject(I.AppConfig) appConfig: AppConfig,
|
|
271
|
+
@inject(I.Logger) protected logger: LoggerInterface
|
|
272
|
+
) {
|
|
273
|
+
this.client = new MongoClient(appConfig.mongodbUrl);
|
|
274
|
+
this.db = this.client.db(appConfig.mongodbDatabase);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async get(event: BridgeEvent): Promise<DBBridgeResponse<unknown>> {
|
|
278
|
+
const { table, where } = event;
|
|
279
|
+
const collection = this.db.collection(table);
|
|
280
|
+
|
|
281
|
+
const query = this.buildQuery(where ?? []);
|
|
282
|
+
const data = await collection.find(query).toArray();
|
|
283
|
+
|
|
284
|
+
return { data };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async pagination(event: BridgeEvent): Promise<DBBridgeResponse<unknown[]>> {
|
|
288
|
+
const { table, where, page = 1, pageSize = 10 } = event;
|
|
289
|
+
const collection = this.db.collection(table);
|
|
290
|
+
|
|
291
|
+
const query = this.buildQuery(where ?? []);
|
|
292
|
+
const total = await collection.countDocuments(query);
|
|
293
|
+
const data = await collection
|
|
294
|
+
.find(query)
|
|
295
|
+
.skip((page - 1) * pageSize)
|
|
296
|
+
.limit(pageSize)
|
|
297
|
+
.toArray();
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
data,
|
|
301
|
+
pagination: { total, page, pageSize }
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
protected buildQuery(where: Where[]): Document {
|
|
306
|
+
return where.reduce((query, [field, operation, value]) => {
|
|
307
|
+
switch (operation) {
|
|
308
|
+
case '=':
|
|
309
|
+
return { ...query, [field]: value };
|
|
310
|
+
case '!=':
|
|
311
|
+
return { ...query, [field]: { $ne: value } };
|
|
312
|
+
case '>':
|
|
313
|
+
return { ...query, [field]: { $gt: value } };
|
|
314
|
+
case '<':
|
|
315
|
+
return { ...query, [field]: { $lt: value } };
|
|
316
|
+
case '>=':
|
|
317
|
+
return { ...query, [field]: { $gte: value } };
|
|
318
|
+
case '<=':
|
|
319
|
+
return { ...query, [field]: { $lte: value } };
|
|
320
|
+
default:
|
|
321
|
+
return query;
|
|
322
|
+
}
|
|
323
|
+
}, {});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### 2. MongoDB Configuration
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
// Add MongoDB configuration in AppConfig
|
|
332
|
+
export class AppConfig implements EnvConfigInterface {
|
|
333
|
+
readonly mongodbUrl: string = process.env.MONGODB_URL!;
|
|
334
|
+
readonly mongodbDatabase: string = process.env.MONGODB_DATABASE!;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Register MongoDB bridge
|
|
338
|
+
export class ServerIOCRegister {
|
|
339
|
+
protected registerImplement(ioc: IOCContainerInterface): void {
|
|
340
|
+
// Use MongoDB implementation
|
|
341
|
+
ioc.bind(I.DBBridgeInterface, ioc.get(MongoDBBridge));
|
|
342
|
+
// Or use Supabase implementation
|
|
343
|
+
// ioc.bind(I.DBBridgeInterface, ioc.get(SupabaseBridge));
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Best Practices and Examples
|
|
349
|
+
|
|
350
|
+
### 1. Repository Pattern Usage
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
@injectable()
|
|
354
|
+
export class UserService {
|
|
355
|
+
constructor(
|
|
356
|
+
@inject(I.UserRepository)
|
|
357
|
+
private userRepository: UserRepositoryInterface
|
|
358
|
+
) {}
|
|
359
|
+
|
|
360
|
+
async getUserProfile(email: string): Promise<UserProfile | null> {
|
|
361
|
+
const user = await this.userRepository.getUserByEmail(email);
|
|
362
|
+
if (!user) return null;
|
|
363
|
+
|
|
364
|
+
// Convert to business model
|
|
365
|
+
return this.mapToUserProfile(user);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
async listUsers(
|
|
369
|
+
params: PaginationParams
|
|
370
|
+
): Promise<PaginationResult<UserProfile>> {
|
|
371
|
+
const result = await this.userRepository.pagination(params);
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
...result,
|
|
375
|
+
list: result.list.map(this.mapToUserProfile)
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### 2. Transaction Handling
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
@injectable()
|
|
385
|
+
export class TransactionManager {
|
|
386
|
+
async withTransaction<T>(
|
|
387
|
+
callback: (transaction: Transaction) => Promise<T>
|
|
388
|
+
): Promise<T> {
|
|
389
|
+
const transaction = await this.dbBridge.startTransaction();
|
|
390
|
+
|
|
391
|
+
try {
|
|
392
|
+
const result = await callback(transaction);
|
|
393
|
+
await transaction.commit();
|
|
394
|
+
return result;
|
|
395
|
+
} catch (error) {
|
|
396
|
+
await transaction.rollback();
|
|
397
|
+
throw error;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Using transactions
|
|
403
|
+
async createUserWithProfile(data: UserData): Promise<User> {
|
|
404
|
+
return this.transactionManager.withTransaction(async (transaction) => {
|
|
405
|
+
const user = await this.userRepository.add(data, transaction);
|
|
406
|
+
await this.profileRepository.add(data.profile, transaction);
|
|
407
|
+
return user;
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### 3. Query Optimization
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
@injectable()
|
|
416
|
+
export class OptimizedUserRepository extends UserRepository {
|
|
417
|
+
// Use field selection to optimize queries
|
|
418
|
+
async getUserProfile(id: number): Promise<UserProfile> {
|
|
419
|
+
const result = await this.dbBridge.get({
|
|
420
|
+
table: this.name,
|
|
421
|
+
fields: this.safeFields, // Only select needed fields
|
|
422
|
+
where: [['id', '=', id]]
|
|
423
|
+
});
|
|
424
|
+
return this.mapToProfile(result.data);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Use index to optimize queries
|
|
428
|
+
async searchUsers(query: string): Promise<User[]> {
|
|
429
|
+
const result = await this.dbBridge.get({
|
|
430
|
+
table: this.name,
|
|
431
|
+
where: [
|
|
432
|
+
['email', '=', query], // Assuming email field is indexed
|
|
433
|
+
['role', '=', 'user']
|
|
434
|
+
]
|
|
435
|
+
});
|
|
436
|
+
return result.data as User[];
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### 4. Caching Strategy
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
444
|
+
@injectable()
|
|
445
|
+
export class CachedUserRepository extends UserRepository {
|
|
446
|
+
constructor(
|
|
447
|
+
@inject(I.DBBridgeInterface) dbBridge: DBBridgeInterface,
|
|
448
|
+
@inject(I.CacheManager) private cache: CacheManager
|
|
449
|
+
) {
|
|
450
|
+
super(dbBridge);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
async getUserByEmail(email: string): Promise<UserSchema | null> {
|
|
454
|
+
// Check cache first
|
|
455
|
+
const cached = await this.cache.get(`user:${email}`);
|
|
456
|
+
if (cached) return cached;
|
|
457
|
+
|
|
458
|
+
// Cache miss, query database
|
|
459
|
+
const user = await super.getUserByEmail(email);
|
|
460
|
+
if (user) {
|
|
461
|
+
await this.cache.set(`user:${email}`, user, '1h');
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return user;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
## Summary
|
|
470
|
+
|
|
471
|
+
The project's database design follows these principles:
|
|
472
|
+
|
|
473
|
+
1. **Abstraction Layers**:
|
|
474
|
+
- Clear interface definitions
|
|
475
|
+
- Replaceable database implementations
|
|
476
|
+
- Repository pattern encapsulating business logic
|
|
477
|
+
|
|
478
|
+
2. **Type Safety**:
|
|
479
|
+
- Complete TypeScript type definitions
|
|
480
|
+
- Query condition type checking
|
|
481
|
+
- Response data type validation
|
|
482
|
+
|
|
483
|
+
3. **Security**:
|
|
484
|
+
- Field-level access control
|
|
485
|
+
- Parameterized queries preventing injection
|
|
486
|
+
- Sensitive data filtering
|
|
487
|
+
|
|
488
|
+
4. **Extensibility**:
|
|
489
|
+
- Support for multiple database implementations
|
|
490
|
+
- Plugin-based feature extension
|
|
491
|
+
- Unified query interface
|
|
492
|
+
|
|
493
|
+
5. **Performance Optimization**:
|
|
494
|
+
- Field selection optimization
|
|
495
|
+
- Query condition optimization
|
|
496
|
+
- Cache strategy support
|