zenstack-graphql-builder 0.1.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.
Files changed (3) hide show
  1. package/README.md +246 -0
  2. package/package.json +34 -0
  3. package/src/index.js +1536 -0
package/README.md ADDED
@@ -0,0 +1,246 @@
1
+ ZenStack GraphQL Builder
2
+ ========================
3
+
4
+ Automatically generate a complete GraphQL CRUD API from your [ZenStack](https://zenstack.dev/) schema. This library builds a fully typed GraphQL schema and resolver map that mirrors Prisma's CRUD operations, including support for relations, filtering, sorting, pagination, aggregations, and custom directives.
5
+
6
+ Features
7
+ --------
8
+
9
+ - 🔄 Full CRUD -- Generates `Query` and `Mutation` fields for all models: `findUnique`, `findFirst`, `findMany`, `create`, `update`, `delete`, `upsert`, `aggregate`, `groupBy`, `exists`, and more.
10
+
11
+ - 🔗 Relations -- Automatically resolves nested relations and provides filter/order arguments for to-many fields.
12
+
13
+ - 🎛 Rich Filtering -- Creates `WhereInput` types with field‑specific filters (`equals`, `contains`, `gt`, `in`, `between`, ...) and relation filters (`every`/`some`/`none`).
14
+
15
+ - 📊 Aggregations -- Supports `count`, `avg`, `sum`, `min`, `max` aggregates and groupBy queries.
16
+
17
+ - 🧩 Custom Scalars -- Includes `DateTime`, `Json`, `BigInt`, `Bytes`, `Decimal`, and a safe `JSONInt` scalar that prevents 53‑bit precision loss.
18
+
19
+ - 🛡 Security Limits -- Enforce maximum `take`/`limit` values and query depth to protect your server.
20
+
21
+ - 🧪 Directives -- Define and apply custom directives (e.g., `@upperCase`) to transform field values after resolution.
22
+
23
+ Installation
24
+ ------------
25
+
26
+ ```bash
27
+ npm install zenstack-graphql-builder graphql
28
+ ```
29
+
30
+ Make sure `graphql` is installed as a peer dependency.
31
+
32
+ Quick Start
33
+ -----------
34
+
35
+ ### 1\. Define your ZenStack schema
36
+
37
+ ```zmodel
38
+ model User {
39
+ id String @id @default(cuid())
40
+ email String @unique
41
+ name String?
42
+ posts Post[]
43
+ }
44
+
45
+ model Post {
46
+ id String @id @default(cuid())
47
+ title String
48
+ content String?
49
+ published Boolean @default(false)
50
+ author User @relation(fields: [authorId], references: [id])
51
+ authorId String
52
+ }
53
+ ```
54
+
55
+ ### 2\. Build the GraphQL schema and resolvers
56
+
57
+ ```typescript
58
+ import { ZenStackGraphQLBuilder } from 'zenstack-graphql-builder';
59
+ import { schema as zenSchema } from './path-to-your-zenstack-schema';
60
+
61
+ const builder = new ZenStackGraphQLBuilder({
62
+ schema: zenSchema,
63
+ options: {
64
+ maxTake: 100,
65
+ maxDepth: 10
66
+ },
67
+ directives,
68
+ directiveDefinitions,
69
+ // optionally filter which CRUD operations to generate
70
+ operations: ['findMany', 'create', 'update', 'delete'],
71
+ });
72
+
73
+ const schema = builder.getSchema();
74
+ const rootValue = builder.getRootResolver();
75
+
76
+ // Now you can use `schema` and `rootValue` with any GraphQL server (e.g., express-graphql, Apollo Server)
77
+ ```
78
+
79
+ ### 3\. Use in your GraphQL server
80
+
81
+ Example with `express-graphql`:
82
+
83
+ ```typescript
84
+ import express from 'express';
85
+ import { createHandler } from 'graphql-http/lib/use/express';
86
+ import { ZenStackClient } from '@zenstackhq/orm';
87
+ import { SqliteDialect } from '@zenstackhq/orm/dialects/sqlite';
88
+
89
+ const db = new ZenStackClient(schema, {
90
+ dialect: new SqliteDialect({
91
+ database: new SQLite('./test.db'),
92
+ }),
93
+ });
94
+
95
+ const app = express();
96
+
97
+ app.use(
98
+ '/graphql',
99
+ createHandler({
100
+ schema,
101
+ rootValue,
102
+ context: {
103
+ client: db, // the ZenStack Client
104
+ options: { maxTake: 50 }, // per‑request security overrides
105
+ }
106
+ })
107
+ );
108
+
109
+ app.listen(4000);
110
+ ```
111
+
112
+ API Reference
113
+ -------------
114
+
115
+ ### `ZenStackGraphQLBuilder` constructor
116
+
117
+ ```typescript
118
+ new ZenStackGraphQLBuilder({
119
+ schema: ZenSchemaDef;
120
+ options?: BuilderOptions;
121
+ directives?: Record<string, DirectiveHandler>;
122
+ directiveDefinitions?: GraphQLDirective[];
123
+ operations?: string[];
124
+ scalars?: Record<string, GraphQLScalarType>;
125
+ });
126
+ ```
127
+ #### Parameters
128
+
129
+ | Param | Type | Description |
130
+ | --- | --- | --- |
131
+ | `schema` | `ZenSchemaDef` | The ZenStack schema definition object (usually exported from your `.zmodel` compilation). |
132
+ | `options` | `BuilderOptions` | Configuration for security and scalar handling (see below). |
133
+ | `directives` | `Record<string, DirectiveHandler>` | A map of directive names to resolver functions. Each function receives `(value, args, variableValues, fieldName)` and should return the transformed value (can be async). |
134
+ | `directiveDefinitions` | `GraphQLDirective[]` | Array of `GraphQLDirective` instances for schema introspection (e.g., for GraphQL playground to show the directives). |
135
+ | `operations` | `string[]` | List of CRUD operations to include. Defaults to all operations (see `AllCrudOperations` in the code). |
136
+ | `scalars` | `Record<string, GraphQLScalarType>` | Override or add custom scalar implementations. |
137
+
138
+ #### `BuilderOptions`
139
+
140
+ | Option | Type | Default | Description |
141
+ | --- | --- | --- | --- |
142
+ | `maxTake` | `number` | `100` | Maximum allowed value for `take`/`limit` arguments. |
143
+ | `maxDepth` | `number` | `9` | Maximum allowed depth of nested selections. |
144
+ | `throwOnError` | `boolean` | `false` | If `true`, throws an error when security limits are exceeded; otherwise silently caps the value. |
145
+ | `useJSONIntScalar` | `boolean` | `false` | If `true`, uses a custom `JSONInt` scalar for `Int` fields, which safely serialises BigInt values that exceed 53 bits as strings. |
146
+
147
+ ### Methods
148
+
149
+ #### `getSchema(): GraphQLSchema`
150
+
151
+ Returns the generated GraphQL schema.
152
+
153
+ #### `getRootResolver(): Record<string, Function>`
154
+
155
+ Returns an object containing resolver functions for all generated Query and Mutation fields. Each resolver accepts `(args, context, info)` and:
156
+
157
+ - Validates arguments against security limits.
158
+
159
+ - Parses the GraphQL selection set into a Prisma `select` object and a transformation plan.
160
+
161
+ - Calls the corresponding Prisma client method (using `context.client[model][operation]`).
162
+
163
+ - Applies any directives to the result before returning.
164
+
165
+ Custom Directives
166
+ -----------------
167
+
168
+ To add a custom directive, you need to:
169
+
170
+ 1. Define the directive in your schema (if you want it to appear in introspection) and pass it via `directiveDefinitions`.
171
+
172
+ 2. Provide an implementation in the `directives` map.
173
+
174
+ Example:
175
+
176
+ ```typescript
177
+ // directive definition
178
+ import { GraphQLDirective, DirectiveLocation } from 'graphql';
179
+
180
+ const maskDirective = new GraphQLDirective({
181
+ name: 'mask',
182
+ locations: [DirectiveLocation.FIELD],
183
+ args: {
184
+ start: { type: GraphQLInt },
185
+ end: { type: GraphQLInt },
186
+ },
187
+ });
188
+
189
+ // handler
190
+ const directives = {
191
+ mask: async (value, args) => {
192
+ if (typeof value !== 'string') return value;
193
+ const start = args.start ?? 0;
194
+ const end = args.end ?? value.length;
195
+ return '*'.repeat(start) + value.slice(start, end);
196
+ },
197
+ };
198
+
199
+ // pass to builder
200
+ new ZenStackGraphQLBuilder({
201
+ schema,
202
+ directives,
203
+ directiveDefinitions: [maskDirective],
204
+ });
205
+ ```
206
+ Now you can use `@mask` in your GraphQL queries:
207
+
208
+ ```graphql
209
+
210
+ query {
211
+ user_findMany {
212
+ email @mask(start: 2, end: 5)
213
+ }
214
+ }
215
+ ```
216
+ Security Limits
217
+ ---------------
218
+
219
+ The builder automatically enforces:
220
+
221
+ - Maximum `take`/`limit` -- Prevents clients from requesting too many records at once.
222
+
223
+ - Maximum query depth -- Protects against deeply nested queries that could overload the database.
224
+
225
+ You can configure these globally via `options` and override them per request via `context.options`.
226
+
227
+ Custom Scalars
228
+ --------------
229
+
230
+ The builder includes several common scalars out of the box:
231
+
232
+ | Scalar | Description |
233
+ | --- | --- |
234
+ | `DateTime` | ISO‑8601 date/time strings. |
235
+ | `Json` | Arbitrary JSON values. |
236
+ | `BigInt` | BigInt values (serialized as strings). |
237
+ | `Bytes` | Base64‑encoded binary data. |
238
+ | `Decimal` | High‑precision decimal numbers (using `decimal.js`). |
239
+ | `JSONInt` | A 53‑bit safe integer scalar (falls back to string for larger values). |
240
+
241
+ You can override any scalar by passing a custom `GraphQLScalarType` in the `scalars` option.
242
+
243
+ License
244
+ -------
245
+
246
+ MIT
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "zenstack-graphql-builder",
3
+ "version": "0.1.0",
4
+ "description": "Automatically generate a complete GraphQL CRUD API from ZenStack schemas, including input types, filters, relation resolvers, custom directive support, security limits, and query projection parsing.",
5
+ "keywords": [
6
+ "zenstack",
7
+ "graphql",
8
+ "schema-builder",
9
+ "crud-generator",
10
+ "prisma",
11
+ "orm",
12
+ "api",
13
+ "graphql-schema",
14
+ "directive",
15
+ "security",
16
+ "projection",
17
+ "resolver"
18
+ ],
19
+ "license": "MIT",
20
+ "author": "17",
21
+ "type": "commonjs",
22
+ "main": "src/index.js",
23
+ "scripts": {
24
+ "test": "echo \"Error: no test specified\" && exit 1"
25
+ },
26
+ "dependencies": {
27
+ "decimal.js": "^10.6.0",
28
+ "graphql": "^16.12.0"
29
+ },
30
+ "files": [
31
+ "src/index.js",
32
+ "README.md"
33
+ ]
34
+ }