@aexol/axolotl 1.0.6 → 2.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.
@@ -1,532 +0,0 @@
1
- const graphqlAiPrompt = `
2
- You create GraphQL Schemas. Here a rules you should use when creating the schema
3
-
4
- 1. Source of Truth:
5
-
6
- From this single source of truth, different teams can consume and discuss this source of truth, enabling
7
- them to collaboratively find the best solutions for our system. This approach allows for a more efficient
8
- and streamlined development process.
9
-
10
- Having a single source of truth also has other benefits. If we have a team member with extensive
11
- business knowledge about the solution, we can teach them GraphQL and have them build the schema.
12
- This way, we can be sure that the team of developers, designers, QA, and so on will perfectly reflect
13
- the project requirements.
14
-
15
- Furthermore, we can treat GraphQL as a low-code solution in this scenario. Why? A well-defined
16
- schema is excellent nourishment for developers to create proper code. It’s much easier to program
17
- when you have a well-built foundation. In the future, or perhaps even at the time you are reading
18
- this book, there may alread
19
-
20
- 2. Standardizing the naming of types and fields:
21
-
22
- Consistency is a crucial aspect of software development, and standardized naming conventions play
23
- a vital role in achieving this within a GraphQL schema.
24
- By using consistent naming conventions, we can easily understand the structure of a schema and its
25
- fields. When these conventions are predictable and follow a set pattern, it becomes more intuitive
26
- to navigate and comprehend the schema, creating a unified and cohesive appearance, and reducing
27
- confusion and potential errors.
28
- This is particularly important in a collaborative environment, where multiple people may be working
29
- on the same GraphQL schema. This is because inconsistent naming conventions can introduce
30
- unnecessary cognitive load for developers. When field names vary in style or structure, developers
31
- may need to spend additional time deciphering each field’s purpose and functionality. This can slow
32
- down development and increase the chances of introducing errors.
33
- Standardized naming conventions eliminate this cognitive overhead and allow developers to focus
34
- on the actual logic and functionality of a schema. It also means that all users are on the same page
35
- and have a shared understanding of the schema’s structure, facilitating seamless collaboration and
36
- reducing the learning curve for new team members joining a project.
37
-
38
- 3. Documenting types, fields, and arguments:
39
- The documentation of GraphQL code includes descriptions of types, fields, arguments, and return
40
- values. This allows developers to quickly understand how to correctly communicate with a schema
41
- and retrieve data from it.
42
- In larger projects, where multiple people work on GraphQL code, documentation serves as a central
43
- knowledge hub for an entire team – it enables new team members to quickly familiarize themselves with
44
- the functionality and structure of the API, as well as ensuring consistency and unified understanding.
45
-
46
- 4. Pagination
47
- Using pagination
48
- As a developer team, we should consider adding pagination only where necessary, critically evaluating
49
- whether the standard pagination model can be replaced with another approach. It is important for us
50
- to carefully assess the need for pagination in our projects and determine whether it truly enhances
51
- the user experience.
52
-
53
- The most important features of pagination are as follows:
54
-
55
- - Reducing data transfer between the client and server
56
- - Speeding up our project by faster server responses
57
- - Enforcing a good communication style
58
- - Reducing information overload on frontend
59
-
60
- Example pagination
61
- \`\`\`graphql
62
- """PageInfo contains information about connection page"""
63
- type PageInfo {
64
- """last element in connection"""
65
- last: String
66
- """limit the response size while querying"""
67
- limit: Int
68
- """if next field value is false, then client received all
69
- available data""""
70
- next: Boolean
71
- }
72
- type BookConnection{
73
- books: [Book!]!
74
- pageInfo: PageInfo!
75
- }
76
- input PageInput{
77
- limit: Int
78
- """
79
- id of the last project returned by previous call
80
- """
81
- start: Int
82
- }
83
-
84
- type Query {
85
- getAllBooks(pagination: PageInput): BookConnection!
86
- }
87
- \`\`\`
88
-
89
- Example using date for pagination:
90
- \`\`\`graphql
91
- scalar Date
92
-
93
- input DateFilter{
94
- start: Date!
95
- end: Date
96
- }
97
- type Query {
98
- getAllBorrowings(dateFilter: DateFilter): [BookBorrowing!]!
99
- }
100
- \`\`\`
101
-
102
- 5. Building pipes
103
-
104
- In GraphQL, types serve as the building blocks of our schema, allowing us to define the structure and
105
- behavior of our data. One powerful aspect of types is their ability to group schema parts by the domain
106
- they represent. By organizing our schema in this way, we can achieve a clear and cohesive separation
107
- of concerns, making our code base more maintainable and easier to understand.
108
- Pipe patterns leverage this GraphQL structure. Usually, pipes are used to pass or stop the resolver
109
- tree execution.
110
- In addition to improving the overall organization of our schema, domain-specific types also enable us
111
- to implement fine-grained access control. By defining specific fields and permissions for each type, we
112
- can ensure that only authorized users have access to certain data or operations. This level of control is
113
- particularly crucial when dealing with sensitive information or enforcing business rules.
114
- When we perform operations on the root query, mutation, or subscription, it does not receive any source
115
- data from another resolver. Instead, it only receives what the backend passes from the previous resolver.
116
- However, things work differently for operations that are not executed at the root of the tree. We can
117
- pass entire objects, and importantly, this information is not inherent to GraphQL itself. It is passed
118
- during backend creation by backend developers and is visible in the framework they use.
119
-
120
- 5.1 Example Logical pipes
121
- Logical pipes are only responsible for the logical grouping of operation types. This way, we are splitting
122
- the schema tree into a set of smaller sub-trees.
123
- By grouping schema parts based on their domain, we can easily navigate and discuss our schema structure
124
- with others. This approach helps us maintain a modular and organized architecture, making it easier
125
- to add or modify functionality as our application evolves. We can think of these domain-specific types
126
- as containers that encapsulate related data and operations, providing a logical separation of concerns.
127
- In the following example, I will present you with a system for reading information in a home that
128
- utilizes Internet of Things (IoT) devices, such as individual solar-charged lights connected via Wi-Fi.
129
- The division into domains will allow us to separate the specific domains that household members
130
- have access to.
131
-
132
- \`\`\`graphql
133
- type YardQuery{
134
- lights: [Light!]
135
- gateOpen: Boolean
136
- }
137
- type Light{
138
- id: ID!
139
- on: Boolean
140
- }
141
- type CloudQuery{
142
- photos: [Asset!]
143
- videos: [Asset!]
144
- }
145
- type Asset{
146
- id: ID!
147
- url: String!
148
- }
149
- type HouseQuery{
150
- room: RoomQuery
151
- yard: YardQuery
152
- cloud: CloudQuery
153
- }
154
- type Query{
155
- house(id: ID): HouseQuery
156
- }
157
- \`\`\`
158
-
159
- 5.2 Access pipes
160
-
161
- The main reason why we use access pipes is to have control over which parts of the schema are available
162
- to individual groups of schema consumers. By doing this, we can see how access control will work in
163
- our system even during the graph creation phase.
164
- To understand access pipes, we need to understand Role-Based Access Control (RBAC). RBAC is
165
- a mechanism that allows us to control access to data based on user roles. It works by assigning each
166
- user a role, which determines the resources available to them. In practice, when defining a GraphQL
167
- schema, we can specify roles for individual object types and fields. We can define different roles such
168
- as admin, user, or guest, and assign them specific read and write permissions for data using directives
169
- or we can do it with access pipes. When a user sends a GraphQL query, their role is taken into account
170
- during query execution. The GraphQL backend engine checks whether the user has the necessary
171
- permissions to resolve the specific type. If the user lacks the appropriate permissions, the GraphQL
172
- engine returns an appropriate “access denied” message.
173
- This mechanism allows us to create secure and controlled API interfaces with RBAC and access pipes
174
- in GraphQL. We can ensure that only authorized individuals have access to specific types, which is
175
- particularly important for applications handling sensitive information such as user data or financial data.
176
-
177
- Now, let’s take a look at an example using the IoT scenario, but this time, we will restrict access to certain
178
- resources. This example will showcase access pipes. Imagine we have a family at home, and we want to
179
- restrict access to photos and videos for our children. To do this, we will split CloudQuery accordingly:
180
- \`\`\`graphql
181
- interface CloudQuery{
182
- photos: [Asset!]
183
- videos: [Asset!]
184
- }
185
- type KidsQuery implements CloudQuery{
186
- photos: [Asset!]
187
- videos: [Asset!]
188
- }
189
- type ParentsQuery implements CloudQuery{
190
- photos: [Asset!]
191
- videos: [Asset!]
192
- }
193
- type Query{
194
- parents: ParentsQuery
195
- kids: KidsQuery
196
- }
197
- \`\`\`
198
- Instead of returning photos and videos from the resolver of the CloudQuery type directly, we
199
- transformed CloudQuery into an interface and created two separate queries named KidsQuery
200
- and ParentsQuery. Then, in the backend system, we will determine who is making the request by
201
- parsing request headers and we won’t allow kids to enter the ParentsQuery assets.
202
-
203
- 5.3 Ownership pipes
204
- It often happens that the owner of a specific entity should be the only one with the authority to edit it.
205
- In such cases, the ownership pipes pattern comes in handy, allowing editing of an object exclusively
206
- by its owner.
207
- One of the most common use cases for ownership pipes is a blog. Here, every user of a blog is also the
208
- owner of their articles, and the only one who can edit them, add a cover image, set the publication
209
- date, and so on. Additionally, there are blog readers who can comment on each article, as well as edit
210
- their own comments and react to other comments. Once again, ownership of these objects and actions
211
- lies with the user. Furthermore, each user – whether a blog writer or commenter – has access to their
212
- own profile data and can edit it.
213
- Just like in the previous section, we will start here by presenting the graph of our GraphQL schema
214
- for a blogging system:
215
-
216
- \`\`\`graphql
217
- """
218
- An author of articles and comments
219
- """
220
- type Author {
221
- """
222
- The unique identifier of the author
223
- """
224
- id: ID!
225
-
226
- """
227
- The name of the author
228
- """
229
- name: String!
230
-
231
- """
232
- The articles written by the author
233
- """
234
- articles: [Article!]!
235
-
236
- """
237
- The comments made by the author
238
- """
239
- comments: [Comment!]!
240
- }
241
-
242
-
243
- """
244
- An article written by an author
245
- """
246
- type Article {
247
- """
248
- The unique identifier of the article
249
- """
250
- id: ID!
251
-
252
- """
253
- The title of the article
254
- """
255
- title: String!
256
-
257
- """
258
- The content of the article
259
- """
260
- content: String!
261
-
262
- """
263
- The author of the article
264
- """
265
- author: Author!
266
-
267
- """
268
- The comments on the article
269
- """
270
- comments: [Comment!]!
271
- }
272
-
273
- """
274
- A comment on an article
275
- """
276
- type Comment {
277
- """
278
- The unique identifier of the comment
279
- """
280
- id: ID!
281
-
282
- """
283
- The content of the comment
284
- """
285
- content: String!
286
-
287
- """
288
- The author of the comment
289
- """
290
- author: Author!
291
- }
292
-
293
- """
294
- Root query type
295
- """
296
- type Query {
297
- """
298
- Get all authors
299
- """
300
- authors: [Author!]!
301
-
302
- """
303
- Get an author by ID
304
- """
305
- author(
306
- id: ID!
307
- ): Author
308
-
309
- """
310
- Get all articles
311
- """
312
- articles: [Article!]!
313
-
314
- """
315
- Get an article by ID
316
- """
317
- article(
318
- id: ID!
319
- ): Article
320
-
321
- """
322
- Get the logged in user
323
- """
324
- me: Me
325
- }
326
-
327
- type Mutation {
328
- """
329
- Operations related to the logged in blog author and its owned data
330
- """
331
- me: AuthorOps
332
-
333
- """
334
- Operations related to reading articles by logged in user
335
- """
336
- article(
337
- id: ID!
338
- ): ArticleReaderOps
339
-
340
- """
341
- Operations related to commenting on articles by logged in user
342
- """
343
- comment(
344
- id: ID!
345
- ): CommentAuthorOps
346
- }
347
-
348
- """
349
- Operations related to reading articles
350
- """
351
- type ArticleReaderOps {
352
- """
353
- Add a comment to an article
354
- """
355
- comment(
356
- """
357
- The content of the comment
358
- """
359
- content: String!
360
- ): ID!
361
- }
362
-
363
-
364
- """
365
- Operations related to commenting on articles
366
- """
367
- type CommentAuthorOps {
368
- """
369
- Delete the comment
370
- """
371
- delete: Boolean!
372
-
373
- """
374
- Update the content of the comment
375
- """
376
- update(
377
- """
378
- The new content of the comment
379
- """
380
- content: String!
381
- ): Boolean!
382
- }
383
-
384
- """
385
- Operations related to the logged in user
386
- """
387
- type AuthorOps {
388
- """
389
- Create a new article
390
- """
391
- createArticle(
392
- """
393
- The details of the article to create
394
- """
395
- article: CreateArticle!
396
- ): ID!
397
-
398
- """
399
- Operations related to managing an article
400
- """
401
- article(
402
- """
403
- The ID of the article
404
- """
405
- id: ID!
406
- ): ArticleAuthorOps
407
-
408
- """
409
- Operations related to managing authors comments
410
- """
411
- comment(
412
- """
413
- The ID of the comment
414
- """
415
- id: ID!
416
- ): CommentAuthorOps
417
- }
418
-
419
- """
420
- Operations related to managing an article
421
- """
422
- type ArticleAuthorOps {
423
- """
424
- Delete the article
425
- """
426
- delete: Boolean!
427
-
428
- """
429
- Update the details of the article
430
- """
431
- update(
432
- """
433
- The updated details of the article
434
- """
435
- article: EditArticle!
436
- ): Boolean!
437
- }
438
-
439
- schema{
440
- query: Query
441
- mutation: Mutation
442
- }
443
-
444
- type Me{
445
- author: Author
446
- }
447
-
448
- """
449
- Input type for creating an article
450
- """
451
- input CreateArticle {
452
- """
453
- The title of the article
454
- """
455
- title: String!
456
-
457
- """
458
- The content of the article
459
- """
460
- content: String!
461
- }
462
-
463
- """
464
- Input type for editing an article
465
- """
466
- input EditArticle {
467
- """
468
- The updated title of the article
469
- """
470
- title: String
471
-
472
- """
473
- The updated content of the article
474
- """
475
- content: String
476
- }
477
- \`\`\`
478
- `;
479
- import chalk from 'chalk';
480
- import { readFileSync } from 'fs';
481
- import openai from 'openai';
482
- import clipboard from 'clipboardy';
483
- import { config } from '@aexol/axolotl-config';
484
- import * as path from 'path';
485
- import { vaildateChatModel } from "./utils.js";
486
- import { oraPromise } from 'ora';
487
- export const graphqlAiCommand = (program) => {
488
- program
489
- .command('gai')
490
- .argument('<prompt>')
491
- .argument('[existing_schema_path]', 'path to the file containing existing schema')
492
- .description(`${chalk.greenBright('Axolotl ai')} - schema creator`)
493
- .action(createResolverFile);
494
- };
495
- export const createResolverFile = async (prompt, existing_schema_path) => {
496
- const cfg = config.get();
497
- let extra_prompt_info = cfg.graphql_prompt_info;
498
- const agent_model = vaildateChatModel(cfg.agent_model || 'gpt-4.1');
499
- if (extra_prompt_info?.endsWith('.txt')) {
500
- extra_prompt_info = readFileSync(path.join(process.cwd(), extra_prompt_info), 'utf-8');
501
- }
502
- const system = `${graphqlAiPrompt}${extra_prompt_info ? `\nAlso take to account that:\n${extra_prompt_info}\n\n` : ''} ${existing_schema_path ? `Please change that in the following existing schema code: ${readFileSync(path.join(process.cwd(), existing_schema_path), 'utf-8')}` : ''}
503
-
504
- Return pure GraphQL schema in graphql text format dont return markdown with embedded graphql just GraphQL
505
- `;
506
- const apiKey = process.env.OPEN_AI_API_KEY;
507
- if (!apiKey)
508
- throw new Error('Please provide OPEN_AI_API_KEY env variable');
509
- const ai = new openai({ apiKey: process.env.OPEN_AI_API_KEY });
510
- await oraPromise(ai.chat.completions
511
- .create({
512
- model: agent_model,
513
- messages: [
514
- {
515
- role: 'system',
516
- content: system,
517
- },
518
- {
519
- role: 'user',
520
- content: prompt,
521
- },
522
- ],
523
- })
524
- .then((response) => {
525
- const res = response.choices.at(0)?.message.content;
526
- if (res) {
527
- clipboard.writeSync(res);
528
- }
529
- console.log(`Generated resolver has been copied to clipboard`);
530
- }), { spinner: 'binary', text: 'Thinking' });
531
- };
532
- //# sourceMappingURL=graphqlAi.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"graphqlAi.js","sourceRoot":"","sources":["../../codegen/graphqlAi.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import { Command } from 'commander';
2
- export declare const createResolversConfig: (program: Command) => void;
@@ -1,10 +0,0 @@
1
- import chalk from 'chalk';
2
- import { createResolverFile } from './utils.js';
3
- export const createResolversConfig = (program) => {
4
- program
5
- .command('resolver')
6
- .argument('[dir]')
7
- .description(`${chalk.greenBright('Axolotl Codegen')} - resolvers creator`)
8
- .action(createResolverFile);
9
- };
10
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../codegen/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,OAAgB,EAAE,EAAE;IACxD,OAAO;SACJ,OAAO,CAAC,UAAU,CAAC;SACnB,QAAQ,CAAC,OAAO,CAAC;SACjB,WAAW,CAAC,GAAG,KAAK,CAAC,WAAW,CAAC,iBAAiB,CAAC,sBAAsB,CAAC;SAC1E,MAAM,CAAC,kBAAkB,CAAC,CAAC;AAChC,CAAC,CAAC"}
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env node
2
- import { Command } from 'commander';
3
- export declare const mcpCommand: (program: Command) => void;
4
- export declare const startMcpServer: (schema: string, options: {
5
- endpoint?: string;
6
- header?: string[];
7
- }) => Promise<void>;