Skip to content

Commit a91e196

Browse files
author
Claudio Gonzalez
committed
feat: Add declarative validation helpers and pre-built scalars
- Add validators module with string, number, array, and date validators - Add scalars module with pre-built validated scalars (Email, URL, PositiveInt, PositiveFloat) - Add factory functions for creating custom bounded and pattern-based scalars - Update README with comprehensive documentation - Add comprehensive test coverage for validators and scalars
1 parent 4d61be0 commit a91e196

7 files changed

Lines changed: 1067 additions & 4 deletions

File tree

README.md

Lines changed: 182 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,9 +1148,117 @@ mutation {
11481148
11491149
## ✅ Validations
11501150
1151-
### Field-Level Validations
1151+
### Declarative Validation Helpers
11521152
1153-
Add validation logic directly to fields:
1153+
Simfinity.js provides built-in validation helpers to simplify common validation patterns, eliminating verbose boilerplate code.
1154+
1155+
#### Using Validators
1156+
1157+
```javascript
1158+
const { validators } = require('@simtlix/simfinity-js');
1159+
1160+
const PersonType = new GraphQLObjectType({
1161+
name: 'Person',
1162+
fields: () => ({
1163+
id: { type: GraphQLID },
1164+
name: {
1165+
type: GraphQLString,
1166+
extensions: {
1167+
validations: validators.stringLength('Name', 2, 100)
1168+
}
1169+
},
1170+
email: {
1171+
type: GraphQLString,
1172+
extensions: {
1173+
validations: validators.email()
1174+
}
1175+
},
1176+
website: {
1177+
type: GraphQLString,
1178+
extensions: {
1179+
validations: validators.url()
1180+
}
1181+
},
1182+
age: {
1183+
type: GraphQLInt,
1184+
extensions: {
1185+
validations: validators.numberRange('Age', 0, 120)
1186+
}
1187+
},
1188+
price: {
1189+
type: GraphQLFloat,
1190+
extensions: {
1191+
validations: validators.positive('Price')
1192+
}
1193+
}
1194+
})
1195+
});
1196+
```
1197+
1198+
#### Available Validators
1199+
1200+
**String Validators:**
1201+
- `validators.stringLength(name, min, max)` - Validates string length with min/max bounds (required for CREATE)
1202+
- `validators.maxLength(name, max)` - Validates maximum string length
1203+
- `validators.pattern(name, regex, message)` - Validates against a regex pattern
1204+
- `validators.email()` - Validates email format
1205+
- `validators.url()` - Validates URL format
1206+
1207+
**Number Validators:**
1208+
- `validators.numberRange(name, min, max)` - Validates number range
1209+
- `validators.positive(name)` - Ensures number is positive
1210+
1211+
**Array Validators:**
1212+
- `validators.arrayLength(name, maxItems, itemValidator)` - Validates array length and optionally each item
1213+
1214+
**Date Validators:**
1215+
- `validators.dateFormat(name, format)` - Validates date format
1216+
- `validators.futureDate(name)` - Ensures date is in the future
1217+
1218+
#### Validator Features
1219+
1220+
- **Automatic Operation Handling**: Validators work for both `CREATE` (save) and `UPDATE` operations
1221+
- **Smart Validation**: For CREATE operations, values are required. For UPDATE operations, undefined/null values are allowed (field might not be updated)
1222+
- **Consistent Error Messages**: All validators throw `SimfinityError` with appropriate messages
1223+
1224+
#### Example: Multiple Validators
1225+
1226+
```javascript
1227+
const ProductType = new GraphQLObjectType({
1228+
name: 'Product',
1229+
fields: () => ({
1230+
id: { type: GraphQLID },
1231+
name: {
1232+
type: GraphQLString,
1233+
extensions: {
1234+
validations: validators.stringLength('Product Name', 3, 200)
1235+
}
1236+
},
1237+
sku: {
1238+
type: GraphQLString,
1239+
extensions: {
1240+
validations: validators.pattern('SKU', /^[A-Z0-9-]+$/, 'SKU must be uppercase alphanumeric with hyphens')
1241+
}
1242+
},
1243+
price: {
1244+
type: GraphQLFloat,
1245+
extensions: {
1246+
validations: validators.positive('Price')
1247+
}
1248+
},
1249+
tags: {
1250+
type: new GraphQLList(GraphQLString),
1251+
extensions: {
1252+
validations: validators.arrayLength('Tags', 10)
1253+
}
1254+
}
1255+
})
1256+
});
1257+
```
1258+
1259+
### Field-Level Validations (Manual)
1260+
1261+
For custom validation logic, you can still write manual validators:
11541262
11551263
```javascript
11561264
const { SimfinityError } = require('@simtlix/simfinity-js');
@@ -1236,7 +1344,78 @@ const OrderType = new GraphQLObjectType({
12361344
12371345
### Custom Validated Scalar Types
12381346
1239-
Create custom scalar types with built-in validation. The generated type names follow the pattern `{name}_{baseScalarTypeName}`:
1347+
Create custom scalar types with built-in validation. The generated type names follow the pattern `{name}_{baseScalarTypeName}`.
1348+
1349+
#### Pre-built Scalars
1350+
1351+
Simfinity.js provides ready-to-use validated scalars for common patterns:
1352+
1353+
```javascript
1354+
const { scalars } = require('@simtlix/simfinity-js');
1355+
1356+
const UserType = new GraphQLObjectType({
1357+
name: 'User',
1358+
fields: () => ({
1359+
id: { type: GraphQLID },
1360+
email: { type: scalars.EmailScalar }, // Type name: Email_String
1361+
website: { type: scalars.URLScalar }, // Type name: URL_String
1362+
age: { type: scalars.PositiveIntScalar }, // Type name: PositiveInt_Int
1363+
price: { type: scalars.PositiveFloatScalar } // Type name: PositiveFloat_Float
1364+
}),
1365+
});
1366+
```
1367+
1368+
**Available Pre-built Scalars:**
1369+
- `scalars.EmailScalar` - Validates email format (`Email_String`)
1370+
- `scalars.URLScalar` - Validates URL format (`URL_String`)
1371+
- `scalars.PositiveIntScalar` - Validates positive integers (`PositiveInt_Int`)
1372+
- `scalars.PositiveFloatScalar` - Validates positive floats (`PositiveFloat_Float`)
1373+
1374+
#### Factory Functions for Custom Scalars
1375+
1376+
Create custom validated scalars with parameters:
1377+
1378+
```javascript
1379+
const { scalars } = require('@simtlix/simfinity-js');
1380+
1381+
// Create a bounded string scalar (name length between 2-100 characters)
1382+
const NameScalar = scalars.createBoundedStringScalar('Name', 2, 100);
1383+
1384+
// Create a bounded integer scalar (age between 0-120)
1385+
const AgeScalar = scalars.createBoundedIntScalar('Age', 0, 120);
1386+
1387+
// Create a bounded float scalar (rating between 0-10)
1388+
const RatingScalar = scalars.createBoundedFloatScalar('Rating', 0, 10);
1389+
1390+
// Create a pattern-based string scalar (phone number format)
1391+
const PhoneScalar = scalars.createPatternStringScalar(
1392+
'Phone',
1393+
/^\+?[\d\s\-()]+$/,
1394+
'Invalid phone number format'
1395+
);
1396+
1397+
// Use in your types
1398+
const PersonType = new GraphQLObjectType({
1399+
name: 'Person',
1400+
fields: () => ({
1401+
id: { type: GraphQLID },
1402+
name: { type: NameScalar }, // Type name: Name_String
1403+
age: { type: AgeScalar }, // Type name: Age_Int
1404+
rating: { type: RatingScalar }, // Type name: Rating_Float
1405+
phone: { type: PhoneScalar } // Type name: Phone_String
1406+
}),
1407+
});
1408+
```
1409+
1410+
**Available Factory Functions:**
1411+
- `scalars.createBoundedStringScalar(name, min, max)` - String with length bounds
1412+
- `scalars.createBoundedIntScalar(name, min, max)` - Integer with range validation
1413+
- `scalars.createBoundedFloatScalar(name, min, max)` - Float with range validation
1414+
- `scalars.createPatternStringScalar(name, pattern, message)` - String with regex pattern validation
1415+
1416+
#### Creating Custom Scalars Manually
1417+
1418+
You can also create custom scalars using `createValidatedScalar` directly:
12401419
12411420
```javascript
12421421
const { GraphQLString, GraphQLInt } = require('graphql');

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@simtlix/simfinity-js",
3-
"version": "2.1.0",
3+
"version": "2.2.0",
44
"description": "",
55
"main": "src/index.js",
66
"type": "module",

src/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2075,6 +2075,9 @@ export const addNoEndpointType = (gqltype) => {
20752075

20762076
export { createValidatedScalar };
20772077

2078+
export { default as validators } from './validators.js';
2079+
export { default as scalars } from './scalars.js';
2080+
20782081
const createArgsForQuery = (argTypes) => {
20792082
const argsObject = {};
20802083

0 commit comments

Comments
 (0)