@simtlix/simfinity-js 1.6.0 → 1.8.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/README.md CHANGED
@@ -1232,6 +1232,11 @@ const newBook = await simfinity.saveObject('Book', {
1232
1232
  const BookModel = simfinity.getModel(BookType);
1233
1233
  const books = await BookModel.find({ author: 'Douglas Adams' });
1234
1234
 
1235
+ // Get the GraphQL type definition by name
1236
+ const UserType = simfinity.getType('User');
1237
+ console.log(UserType.name); // 'User'
1238
+ console.log(UserType.getFields()); // Access GraphQL fields
1239
+
1235
1240
  // Get the input type for a GraphQL type
1236
1241
  const BookInput = simfinity.getInputType(BookType);
1237
1242
  ```
@@ -2122,4 +2127,270 @@ When the plugin is correctly set up, your GraphQL response will include the coun
2122
2127
 
2123
2128
  This setup allows you to efficiently manage and display pagination information in your GraphQL applications.
2124
2129
 
2130
+ ## 📖 API Reference
2131
+
2132
+ Simfinity.js provides several utility methods for programmatic access to your GraphQL types and data:
2133
+
2134
+ ### `getType(typeName)`
2135
+
2136
+ Retrieves a GraphQL type definition from the internal types registry.
2137
+
2138
+ **Parameters:**
2139
+ - `typeName` (string | GraphQLObjectType): The name of the type or a GraphQL type object
2140
+
2141
+ **Returns:**
2142
+ - `GraphQLObjectType | null`: The GraphQL type definition, or null if not found
2143
+
2144
+ **Examples:**
2145
+
2146
+ ```javascript
2147
+ import { getType } from '@simtlix/simfinity-js';
2148
+
2149
+ // Get type by string name
2150
+ const UserType = getType('User');
2151
+ if (UserType) {
2152
+ console.log(UserType.name); // 'User'
2153
+
2154
+ // Access field definitions
2155
+ const fields = UserType.getFields();
2156
+ console.log(Object.keys(fields)); // ['id', 'name', 'email', ...]
2157
+
2158
+ // Check specific field
2159
+ const nameField = fields.name;
2160
+ console.log(nameField.type); // GraphQLString
2161
+ }
2162
+
2163
+ // Get type by GraphQL type object
2164
+ const BookType = getType(SomeBookType);
2165
+
2166
+ // Safe access - returns null if not found
2167
+ const nonExistentType = getType('NonExistent');
2168
+ console.log(nonExistentType); // null
2169
+ ```
2170
+
2171
+ **Use Cases:**
2172
+ - **Type introspection**: Examine type definitions programmatically
2173
+ - **Dynamic schema analysis**: Build tools that analyze your GraphQL schema
2174
+ - **Runtime type checking**: Validate types exist before operations
2175
+ - **Admin interfaces**: Build dynamic forms based on type definitions
2176
+ - **Circular reference resolution**: Prevent import cycles when types reference each other
2177
+
2178
+ ### Preventing Circular References with `getType`
2179
+
2180
+ When you have types that reference each other (like User and Group), using `getType` prevents circular import issues:
2181
+
2182
+ ```javascript
2183
+ import { GraphQLObjectType, GraphQLID, GraphQLString, GraphQLList } from 'graphql';
2184
+ import { getType } from '@simtlix/simfinity-js';
2185
+
2186
+ // User type that references Group
2187
+ const UserType = new GraphQLObjectType({
2188
+ name: 'User',
2189
+ fields: () => ({
2190
+ id: { type: GraphQLID },
2191
+ name: { type: GraphQLString },
2192
+ email: { type: GraphQLString },
2193
+
2194
+ // Reference Group type by name to avoid circular imports
2195
+ groups: {
2196
+ type: new GraphQLList(() => getType('Group')), // Use getType instead of direct import
2197
+ extensions: {
2198
+ relation: {
2199
+ connectionField: 'members',
2200
+ displayField: 'name'
2201
+ }
2202
+ }
2203
+ },
2204
+
2205
+ // Single group reference
2206
+ primaryGroup: {
2207
+ type: () => getType('Group'), // Lazy evaluation with getType
2208
+ extensions: {
2209
+ relation: {
2210
+ connectionField: 'primaryGroupId',
2211
+ displayField: 'name'
2212
+ }
2213
+ }
2214
+ }
2215
+ })
2216
+ });
2217
+
2218
+ // Group type that references User
2219
+ const GroupType = new GraphQLObjectType({
2220
+ name: 'Group',
2221
+ fields: () => ({
2222
+ id: { type: GraphQLID },
2223
+ name: { type: GraphQLString },
2224
+ description: { type: GraphQLString },
2225
+
2226
+ // Reference User type by name to avoid circular imports
2227
+ members: {
2228
+ type: new GraphQLList(() => getType('User')), // Use getType instead of direct import
2229
+ extensions: {
2230
+ relation: {
2231
+ connectionField: 'groups',
2232
+ displayField: 'name'
2233
+ }
2234
+ }
2235
+ },
2236
+
2237
+ // Single user reference (admin)
2238
+ admin: {
2239
+ type: () => getType('User'), // Lazy evaluation with getType
2240
+ extensions: {
2241
+ relation: {
2242
+ connectionField: 'adminId',
2243
+ displayField: 'name'
2244
+ }
2245
+ }
2246
+ }
2247
+ })
2248
+ });
2249
+
2250
+ // Register types with simfinity
2251
+ simfinity.connect(null, UserType, 'user', 'users');
2252
+ simfinity.connect(null, GroupType, 'group', 'groups');
2253
+
2254
+ // Create schema - resolvers will be auto-generated for all relationships
2255
+ const schema = simfinity.createSchema();
2256
+ ```
2257
+
2258
+ **Benefits of this approach:**
2259
+
2260
+ 1. **🔄 No Circular Imports**: Each file can import `getType` without importing other type definitions
2261
+ 2. **⚡ Lazy Resolution**: Types are resolved at schema creation time when all types are registered
2262
+ 3. **🛡️ Type Safety**: Still maintains GraphQL type checking and validation
2263
+ 4. **🧹 Clean Architecture**: Separates type definitions from type relationships
2264
+ 5. **📦 Better Modularity**: Each type can be in its own file without import dependencies
2265
+
2266
+ **File Structure Example:**
2267
+
2268
+ ```
2269
+ types/
2270
+ ├── User.js // Defines UserType using getType('Group')
2271
+ ├── Group.js // Defines GroupType using getType('User')
2272
+ └── index.js // Registers all types and creates schema
2273
+ ```
2274
+
2275
+ ```javascript
2276
+ // types/User.js
2277
+ import { GraphQLObjectType, GraphQLID, GraphQLString, GraphQLList } from 'graphql';
2278
+ import { getType } from '@simtlix/simfinity-js';
2279
+
2280
+ export const UserType = new GraphQLObjectType({
2281
+ name: 'User',
2282
+ fields: () => ({
2283
+ id: { type: GraphQLID },
2284
+ name: { type: GraphQLString },
2285
+ groups: {
2286
+ type: new GraphQLList(() => getType('Group')),
2287
+ extensions: { relation: { connectionField: 'members' } }
2288
+ }
2289
+ })
2290
+ });
2291
+
2292
+ // types/Group.js
2293
+ import { GraphQLObjectType, GraphQLID, GraphQLString, GraphQLList } from 'graphql';
2294
+ import { getType } from '@simtlix/simfinity-js';
2295
+
2296
+ export const GroupType = new GraphQLObjectType({
2297
+ name: 'Group',
2298
+ fields: () => ({
2299
+ id: { type: GraphQLID },
2300
+ name: { type: GraphQLString },
2301
+ members: {
2302
+ type: new GraphQLList(() => getType('User')),
2303
+ extensions: { relation: { connectionField: 'groups' } }
2304
+ }
2305
+ })
2306
+ });
2307
+
2308
+ // types/index.js
2309
+ import { UserType } from './User.js';
2310
+ import { GroupType } from './Group.js';
2311
+ import simfinity from '@simtlix/simfinity-js';
2312
+
2313
+ // Register all types
2314
+ simfinity.connect(null, UserType, 'user', 'users');
2315
+ simfinity.connect(null, GroupType, 'group', 'groups');
2316
+
2317
+ // Create schema with auto-generated resolvers
2318
+ export const schema = simfinity.createSchema();
2319
+ ```
2320
+
2321
+ ### `getModel(gqltype)`
2322
+
2323
+ Retrieves the Mongoose model associated with a GraphQL type.
2324
+
2325
+ **Parameters:**
2326
+ - `gqltype` (GraphQLObjectType): The GraphQL type object
2327
+
2328
+ **Returns:**
2329
+ - `MongooseModel`: The associated Mongoose model
2330
+
2331
+ **Example:**
2332
+
2333
+ ```javascript
2334
+ const BookModel = simfinity.getModel(BookType);
2335
+ const books = await BookModel.find({ author: 'Douglas Adams' });
2336
+ ```
2337
+
2338
+ ### `getInputType(type)`
2339
+
2340
+ Retrieves the input type for mutations associated with a GraphQL type.
2341
+
2342
+ **Parameters:**
2343
+ - `type` (GraphQLObjectType): The GraphQL type object
2344
+
2345
+ **Returns:**
2346
+ - `GraphQLInputObjectType`: The input type for mutations
2347
+
2348
+ **Example:**
2349
+
2350
+ ```javascript
2351
+ const BookInput = simfinity.getInputType(BookType);
2352
+ console.log(BookInput.getFields()); // Input fields for mutations
2353
+ ```
2354
+
2355
+ ### `saveObject(typeName, args, session?)`
2356
+
2357
+ Programmatically save an object outside of GraphQL mutations.
2358
+
2359
+ **Parameters:**
2360
+ - `typeName` (string): The name of the GraphQL type
2361
+ - `args` (object): The data to save
2362
+ - `session` (MongooseSession, optional): Database session for transactions
2363
+
2364
+ **Returns:**
2365
+ - `Promise<object>`: The saved object
2366
+
2367
+ **Example:**
2368
+
2369
+ ```javascript
2370
+ const newBook = await simfinity.saveObject('Book', {
2371
+ title: 'New Book',
2372
+ author: 'Author Name'
2373
+ }, session);
2374
+ ```
2375
+
2376
+ ### `createSchema(includedQueryTypes?, includedMutationTypes?, includedCustomMutations?)`
2377
+
2378
+ Creates the final GraphQL schema with all connected types.
2379
+
2380
+ **Parameters:**
2381
+ - `includedQueryTypes` (array, optional): Limit query types to include
2382
+ - `includedMutationTypes` (array, optional): Limit mutation types to include
2383
+ - `includedCustomMutations` (array, optional): Limit custom mutations to include
2384
+
2385
+ **Returns:**
2386
+ - `GraphQLSchema`: The complete GraphQL schema
2387
+
2388
+ **Example:**
2389
+
2390
+ ```javascript
2391
+ const schema = simfinity.createSchema();
2392
+ ```
2393
+
2394
+ *Built with ❤️ by [Simtlix](https://github.com/simtlix)*
2395
+
2125
2396
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simtlix/simfinity-js",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/index.js CHANGED
@@ -1535,13 +1535,54 @@ const buildRootQuery = (name, includedTypes) => {
1535
1535
  /* Creating a new GraphQL Schema, with options query which defines query
1536
1536
  we will allow users to use when they are making request. */
1537
1537
  export const createSchema = (includedQueryTypes,
1538
- includedMutationTypes, includedCustomMutations) => new GraphQLSchema({
1539
- query: buildRootQuery('RootQueryType', includedQueryTypes),
1540
- mutation: buildMutation('Mutation', includedMutationTypes, includedCustomMutations),
1541
- });
1538
+ includedMutationTypes, includedCustomMutations) => {
1539
+
1540
+ // Generate models for all registered types now that all types are available
1541
+ Object.values(typesDict.types).forEach(typeInfo => {
1542
+ if (typeInfo.gqltype && !typeInfo.model) {
1543
+ if (typeInfo.endpoint) {
1544
+ // Generate model with collection for endpoint types (types registered with connect)
1545
+ typeInfo.model = generateModel(typeInfo.gqltype, typeInfo.onModelCreated);
1546
+ } else if (typeInfo.needsModel) {
1547
+ // Generate model without collection for no-endpoint types that need models (addNoEndpointType)
1548
+ typeInfo.model = generateModelWithoutCollection(typeInfo.gqltype, null);
1549
+ }
1550
+ }
1551
+ });
1552
+
1553
+ // Also update the typesDictForUpdate with the generated models
1554
+ Object.keys(typesDict.types).forEach(typeName => {
1555
+ if (typesDictForUpdate.types[typeName]) {
1556
+ typesDictForUpdate.types[typeName].model = typesDict.types[typeName].model;
1557
+ }
1558
+ });
1559
+
1560
+ // Auto-generate resolvers for all registered types now that all types are available
1561
+ Object.values(typesDict.types).forEach(typeInfo => {
1562
+ if (typeInfo.gqltype) {
1563
+ autoGenerateResolvers(typeInfo.gqltype);
1564
+ }
1565
+ });
1566
+
1567
+ return new GraphQLSchema({
1568
+ query: buildRootQuery('RootQueryType', includedQueryTypes),
1569
+ mutation: buildMutation('Mutation', includedMutationTypes, includedCustomMutations),
1570
+ });
1571
+ };
1542
1572
 
1543
1573
  export const getModel = (gqltype) => typesDict.types[gqltype.name].model;
1544
1574
 
1575
+ export const getType = (typeName) => {
1576
+ if (typeof typeName === 'string') {
1577
+ return typesDict.types[typeName]?.gqltype;
1578
+ }
1579
+ // If it's already a GraphQL type object, get by its name
1580
+ if (typeName && typeName.name) {
1581
+ return typesDict.types[typeName.name]?.gqltype;
1582
+ }
1583
+ return null;
1584
+ };
1585
+
1545
1586
  export const registerMutation = (name, description, inputModel, outputModel, callback) => {
1546
1587
  registeredMutations[name] = {
1547
1588
  description,
@@ -1607,19 +1648,17 @@ export const connect = (model, gqltype, simpleEntityEndpointName,
1607
1648
  gqltype,
1608
1649
  };
1609
1650
  typesDict.types[gqltype.name] = {
1610
- model: model || generateModel(gqltype, onModelCreated),
1651
+ model: model, // Will be generated later in createSchema if not provided
1611
1652
  gqltype,
1612
1653
  simpleEntityEndpointName,
1613
1654
  listEntitiesEndpointName,
1614
1655
  endpoint: true,
1615
1656
  controller,
1616
1657
  stateMachine,
1658
+ onModelCreated, // Store the callback for later use
1617
1659
  };
1618
1660
 
1619
1661
  typesDictForUpdate.types[gqltype.name] = { ...typesDict.types[gqltype.name] };
1620
-
1621
- // Auto-generate resolve methods for relationship fields if not already defined
1622
- autoGenerateResolvers(gqltype);
1623
1662
  };
1624
1663
 
1625
1664
  export const addNoEndpointType = (gqltype) => {
@@ -1643,14 +1682,12 @@ export const addNoEndpointType = (gqltype) => {
1643
1682
  typesDict.types[gqltype.name] = {
1644
1683
  gqltype,
1645
1684
  endpoint: false,
1646
- // Generate model if needed for relationships, but don't create collection
1647
- model: needsModel ? generateModelWithoutCollection(gqltype, null) : null,
1685
+ // Model will be generated later in createSchema if needed
1686
+ model: null,
1687
+ needsModel, // Store whether this type needs a model
1648
1688
  };
1649
1689
 
1650
1690
  typesDictForUpdate.types[gqltype.name] = { ...typesDict.types[gqltype.name] };
1651
-
1652
- // Auto-generate resolve methods for relationship fields if not already defined
1653
- autoGenerateResolvers(gqltype);
1654
1691
  };
1655
1692
 
1656
1693
  export { createValidatedScalar };
@@ -28,6 +28,7 @@ describe('preventCreatingCollection option', () => {
28
28
  });
29
29
 
30
30
  simfinity.connect(null, TestType, 'testTypeDefault', 'testTypesDefault');
31
+ simfinity.createSchema(); // Models are now generated during schema creation
31
32
  expect(createCollectionSpy).toHaveBeenCalledTimes(1);
32
33
  });
33
34
 
@@ -43,6 +44,7 @@ describe('preventCreatingCollection option', () => {
43
44
  });
44
45
 
45
46
  simfinity.connect(null, TestType, 'testTypePrevent', 'testTypesPrevent');
47
+ simfinity.createSchema(); // Models are now generated during schema creation
46
48
  expect(createCollectionSpy).not.toHaveBeenCalled();
47
49
  });
48
50
 
@@ -59,6 +61,7 @@ describe('preventCreatingCollection option', () => {
59
61
  });
60
62
 
61
63
  simfinity.connect(null, TestType, 'testTypeAllow', 'testTypesAllow');
64
+ simfinity.createSchema(); // Models are now generated during schema creation
62
65
  expect(createCollectionSpy).toHaveBeenCalledTimes(1);
63
66
  });
64
67
  });
@@ -117,6 +117,7 @@ describe('Custom Validated Scalar Types', () => {
117
117
 
118
118
  beforeAll(() => {
119
119
  simfinity.connect(null, UserType, 'user', 'users');
120
+ simfinity.createSchema(); // Models are now generated during schema creation
120
121
  UserModel = simfinity.getModel(UserType);
121
122
  });
122
123
 
@@ -150,6 +151,7 @@ describe('Custom Validated Scalar Types', () => {
150
151
  }),
151
152
  });
152
153
  simfinity.connect(null, UserWithUniqueType, 'userWithUnique', 'usersWithUnique');
154
+ simfinity.createSchema(); // Models are now generated during schema creation
153
155
  const UserWithUniqueModel = simfinity.getModel(UserWithUniqueType);
154
156
  const schema = UserWithUniqueModel.schema.obj;
155
157