Ontwerp een Geavanceerd GraphQL Schema met Aangepaste Scalar Types en Complexe Inputvalidatie
description
Deze prompt helpt gebruikers bij het maken van GraphQL schema's die verder gaan dan standaard types door aangepaste scalars te implementeren die zijn afgestemd op hun domein. Het lost veelvoorkomende problemen rond invoerdata kwaliteit en validatie op en zorgt voor robuuste API's die fouten verminderen en het vertrouwen van ontwikkelaars vergroten. In tegenstelling tot generieke schema ontwerp prompts, richt deze zich specifiek op aangepaste scalars en complexe validatiestrategieën, met praktische voorbeelden en uitleg die de onderhoudbaarheid en bruikbaarheid van API's verbeteren.
prompt
Help me een geavanceerd GraphQL schema te ontwerpen dat aangepaste scalar types en complexe inputvalidatieregels bevat. Mijn domein is: <beschrijf je domein of toepassingsgebied>. Ik wil ondersteuning voor: <specificeer de benodigde aangepaste scalar types, valid ...
try_prompt
generate_helper
disclaimerOnPageApi
tags
ai_answers
provider: openai
model: gpt-4.1-nano
Zeker! Hieronder vind je een gedetailleerd voorbeeld van een geavanceerd GraphQL-schema voor een social media platform gericht op het delen van foto's, inclusief aangepaste scalars en complexe validatieregels. Ik leg ook uit hoe deze bijdragen aan dataintegriteit, ontwikkelaarservaring en API-robustheid.
---
### 1. Aangepaste scalars definiëren
**a. DateTime Scalar (ISO8601)**
Deze scalar zorgt dat datums en tijden in ISO8601-formaat worden afgedwongen en gevalideerd.
**b. Email Scalar (RFC 5322)**
Deze scalar valideert dat e-mailadressen voldoen aan RFC 5322 standaard.
---
### 2. Voorbeeld schema met scalars en validatie
```graphql
scalar DateTime @specifiedBy(url: "https://datatracker.ietf.org/doc/html/rfc3339") # Voor ISO8601
scalar Email @specifiedBy(url: "https://tools.ietf.org/html/rfc5322") # Voor RFC 5322
type User {
id: ID!
username: String!
email: Email!
joinedAt: DateTime!
photos: [Photo!]!
}
type Photo {
id: ID!
url: String!
caption: String
uploadedAt: DateTime!
owner: User!
tags: [String!]
}
# Input voor het uploaden van foto's met validatie
input UploadPhotoInput {
url: String! @constraint(pattern: "^https?://.+\\.(jpg|jpeg|png)$") # Alleen afbeeldingen
caption: String
}
type Mutation {
registerUser(username: String!, email: Email!): User!
uploadPhoto(input: UploadPhotoInput!): Photo!
}
```
---
### 3. Validatiedirectieven en middleware
**a. Validatie via custom directives**
Je kunt validatiedirectieven gebruiken zoals `@constraint` (van bijvoorbeeld [GraphQL-Tools](https://github.com/confuser/graphql-constraint-directive)) om veld-specifieke validatie toe te passen.
**b. Implementatievoorbeeld (bijvoorbeeld in JavaScript/TypeScript)**
```js
const { GraphQLScalarType, Kind } = require('graphql');
const { constraintDirective } = require('graphql-constraint-directive');
// Custom DateTime scalar
const DateTimeScalar = new GraphQLScalarType({
name: 'DateTime',
description: 'ISO8601 Datum en tijd',
serialize(value) { return value; },
parseValue(value) {
if (isValidISO8601(value)) {
return value;
}
throw new Error('Ongeldig ISO8601 datetime');
},
parseLiteral(ast) {
if (ast.kind === Kind.STRING && isValidISO8601(ast.value)) {
return ast.value;
}
throw new Error('Ongeldig ISO8601 datetime');
},
});
// Custom Email scalar
const EmailScalar = new GraphQLScalarType({
name: 'Email',
description: 'RFC 5322 valide e-mailadres',
serialize(value) { return value; },
parseValue(value) {
if (isValidEmail(value)) {
return value;
}
throw new Error('Ongeldig emailadres');
},
parseLiteral(ast) {
if (ast.kind === Kind.STRING && isValidEmail(ast.value)) {
return ast.value;
}
throw new Error('Ongeldig emailadres');
},
});
// Validatiefuncties
function isValidISO8601(value) {
// Implementatie met regex of date parsing library
const date = new Date(value);
return date.toISOString() === value;
}
function isValidEmail(value) {
// Regex of library zoals validator.js
const emailRegex = /^(?:[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}|\\[(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\\])$/;
return emailRegex.test(value);
}
```
---
### 4. Verbeterde dataintegriteit en robuustheid
- **Datavalidatie op schema-niveau:** Door gebruik te maken van aangepaste scalars en validatiedirectieven voorkom je dat ongeldige data de API binnenkomt. Bijvoorbeeld, alleen juiste datums en e-mails worden geaccepteerd.
- **Consistente data:** ISO8601 datums zorgen voor uniforme tijdsnotatie, wat essentieel is voor tijdgevoelige functies.
- **Ontwikkelaarservaring:** Documentatie en foutmeldingen zijn duidelijker doordat validatie expliciet is gedefinieerd, wat debugging en integratie vergemakkelijkt.
- **API-robustheid:** Strikte validatie vermindert de kans op buggy of corrupte data, wat de stabiliteit en betrouwbaarheid van het platform verbetert.
---
### 5. Samenvatting
Door aangepaste scalars zoals `DateTime` en `Email` te gebruiken, gekoppeld aan validatiedirectieven en/of middleware, zorg je voor:
- **Strikte datavalidatie** die voorkomen dat ongeldige data wordt opgeslagen.
- **Verbeterde ontwikkelaarservaring** door duidelijke documentatie en consistente datatypes.
- **Toegenomen robuustheid** van de API, minder bugs, en hogere datakwaliteit.
---
Als je verdere hulp nodig hebt bij het implementeren of uitbreiden van dit schema, laat het gerust weten!
provider: deepseek
model: deepseek-chat
Zeker! Hier is een geavanceerd GraphQL schema voor een foto-deel platform met uitgebreide validatie:
## Schema Definitie
```graphql
# Aangepaste Scalar Types
scalar DateTimeISO
scalar EmailRFC5322
scalar URL
scalar NonEmptyString
# Validatie Directieven
directive @validateLength(min: Int, max: Int) on INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION
directive @validateRange(min: Float, max: Float) on INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION
directive @validatePattern(regex: String) on INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION
directive @validateFileSize(maxSizeMB: Float) on INPUT_FIELD_DEFINITION
# Types
type User {
id: ID!
email: EmailRFC5322!
username: String!
profile: UserProfile
posts: [Post!]!
createdAt: DateTimeISO!
updatedAt: DateTimeISO!
}
type UserProfile {
bio: String
avatar: URL
website: URL
location: String
}
type Post {
id: ID!
title: String!
description: String
imageUrl: URL!
imageMetadata: ImageMetadata!
author: User!
tags: [Tag!]!
likes: Int!
comments: [Comment!]!
createdAt: DateTimeISO!
updatedAt: DateTimeISO!
}
type ImageMetadata {
width: Int!
height: Int!
fileSize: Int!
format: String!
dominantColor: String
}
type Comment {
id: ID!
content: NonEmptyString!
author: User!
post: Post!
createdAt: DateTimeISO!
updatedAt: DateTimeISO!
}
type Tag {
id: ID!
name: NonEmptyString!
posts: [Post!]!
}
# Input Types met Validatie
input CreateUserInput {
email: EmailRFC5322!
username: String! @validateLength(min: 3, max: 30)
password: String! @validateLength(min: 8, max: 100)
@validatePattern(regex: "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]")
profile: CreateUserProfileInput
}
input CreateUserProfileInput {
bio: String @validateLength(max: 500)
avatar: URL
website: URL
location: String @validateLength(max: 100)
}
input CreatePostInput {
title: String! @validateLength(min: 1, max: 100)
description: String @validateLength(max: 1000)
imageUrl: URL!
tags: [String!]! @validateLength(min: 1, max: 10)
}
input UpdatePostInput {
title: String @validateLength(min: 1, max: 100)
description: String @validateLength(max: 1000)
tags: [String!] @validateLength(min: 1, max: 10)
}
input CreateCommentInput {
postId: ID!
content: NonEmptyString! @validateLength(min: 1, max: 500)
}
input FileUploadInput {
fileName: String!
fileSize: Int! @validateFileSize(maxSizeMB: 10)
mimeType: String! @validatePattern(regex: "^image/(jpeg|png|gif|webp)$")
}
# Queries en Mutations
type Query {
# Gebruikers
me: User
user(id: ID!): User
users(search: String @validateLength(min: 1, max: 50)): [User!]!
# Posts
posts(
limit: Int @validateRange(min: 1, max: 50)
offset: Int @validateRange(min: 0)
tags: [String!] @validateLength(max: 10)
): [Post!]!
post(id: ID!): Post
postsByUser(userId: ID!): [Post!]!
# Zoeken
searchPosts(
query: String! @validateLength(min: 1, max: 100)
limit: Int @validateRange(min: 1, max: 50)
): [Post!]!
}
type Mutation {
# Gebruiker Management
register(input: CreateUserInput!): User!
updateProfile(input: CreateUserProfileInput!): UserProfile!
# Post Management
createPost(input: CreatePostInput!): Post!
updatePost(id: ID!, input: UpdatePostInput!): Post!
deletePost(id: ID!): Boolean!
# Interacties
likePost(postId: ID!): Post!
unlikePost(postId: ID!): Post!
addComment(input: CreateCommentInput!): Comment!
deleteComment(id: ID!): Boolean!
# Bestand Upload
generateUploadUrl(input: FileUploadInput!): URL!
}
```
## Implementatie Voorbeelden
### Custom Scalar Implementatie (Apollo Server)
```javascript
// scalar-resolvers.js
const { GraphQLScalarType, Kind } = require('graphql');
const DateTimeISO = new GraphQLScalarType({
name: 'DateTimeISO',
description: 'ISO 8601 date-time string',
serialize(value) {
// Valideer en formatteer output
if (!(value instanceof Date)) {
throw new Error('DateTimeISO kan alleen Date objecten serialiseren');
}
return value.toISOString();
},
parseValue(value) {
// Valideer input van variabelen
if (typeof value !== 'string') {
throw new Error('DateTimeISO moet een string zijn');
}
const date = new Date(value);
if (isNaN(date.getTime())) {
throw new Error('Ongeldig ISO 8601 formaat');
}
return date;
},
parseLiteral(ast) {
// Valideer inline arguments
if (ast.kind !== Kind.STRING) {
throw new Error('DateTimeISO moet een string zijn');
}
const date = new Date(ast.value);
if (isNaN(date.getTime())) {
throw new Error('Ongeldig ISO 8601 formaat');
}
return date;
}
});
const EmailRFC5322 = new GraphQLScalarType({
name: 'EmailRFC5322',
description: 'RFC 5322 email adres',
serialize(value) {
return String(value).toLowerCase();
},
parseValue(value) {
const email = String(value).toLowerCase();
const emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
if (!emailRegex.test(email)) {
throw new Error('Ongeldig email formaat volgens RFC 5322');
}
return email;
},
parseLiteral(ast) {
if (ast.kind !== Kind.STRING) {
throw new Error('Email moet een string zijn');
}
return this.parseValue(ast.value);
}
});
const NonEmptyString = new GraphQLScalarType({
name: 'NonEmptyString',
description: 'String die niet leeg mag zijn',
serialize(value) {
return value;
},
parseValue(value) {
if (typeof value !== 'string' || value.trim().length === 0) {
throw new Error('String mag niet leeg zijn');
}
return value.trim();
},
parseLiteral(ast) {
if (ast.kind !== Kind.STRING || ast.value.trim().length === 0) {
throw new Error('String mag niet leeg zijn');
}
return ast.value.trim();
}
});
```
### Validatie Middleware
```javascript
// validation-middleware.js
const validationMiddleware = async (resolve, root, args, context, info) => {
const { fieldName, parentType } = info;
// Valideer input arguments
const inputArgs = info.fieldNodes[0].arguments || [];
for (const arg of inputArgs) {
const argName = arg.name.value;
const argValue = args[argName];
// Valideer op basis van schema directives
await validateArgument(argName, argValue, info);
}
return resolve(root, args, context, info);
};
async function validateArgument(argName, argValue, info) {
const field = info.parentType.getFields()[info.fieldName];
const argDef = field.args.find(arg => arg.name === argName);
if (!argDef) return;
// Valideer length directives
const lengthDirective = argDef.astNode.directives.find(d => d.name.value === 'validateLength');
if (lengthDirective) {
const min = getDirectiveArg(lengthDirective, 'min');
const max = getDirectiveArg(lengthDirective, 'max');
if (typeof argValue === 'string') {
if (min !== undefined && argValue.length < min) {
throw new Error(`"${argName}" moet minimaal ${min} karakters bevatten`);
}
if (max !== undefined && argValue.length > max) {
throw new Error(`"${argName}" mag maximaal ${max} karakters bevatten`);
}
} else if (Array.isArray(argValue)) {
if (min !== undefined && argValue.length < min) {
throw new Error(`"${argName}" moet minimaal ${min} items bevatten`);
}
if (max !== undefined && argValue.length > max) {
throw new Error(`"${argName}" mag maximaal ${max} items bevatten`);
}
}
}
// Valideer pattern directives
const patternDirective = argDef.astNode.directives.find(d => d.name.value === 'validatePattern');
if (patternDirective && typeof argValue === 'string') {
const regex = new RegExp(getDirectiveArg(patternDirective, 'regex'));
if (!regex.test(argValue)) {
throw new Error(`"${argName}" voldoet niet aan het vereiste formaat`);
}
}
}
```
## Voordelen van deze Aanpak
### 1. **Dataintegriteit**
- **Type Safety**: Custom scalars zorgen voor consistente dataformaten
- **Validatie aan de Grens**: Fouten worden vroeg gevangen, voordat ze de business logic bereiken
- **Consistente Formaten**: ISO 8601 voor datums, RFC 5322 voor emails
### 2. **Ontwikkelaarservaring**
- **Self-Documenting**: Schema beschrijft precies wat verwacht wordt
- **Duidelijke Foutmeldingen**: Specifieke validatiefouten in plaats van generieke errors
- **IDE Support**: Type informatie voor betere autocompletion
### 3. **API Robustheid**
- **Defensief Ontwerp**: Veilige standaardwaarden en limits
- **Performance**: Validatie gebeurt voordat complexe operaties starten
- **Schaalbaarheid**: Middelware kan uitgebreid worden zonder schema wijzigingen
### 4. **Security**
- **Input Sanitization**: Automatische trimming en normalisatie
- **Size Limits**: Voorkomt DoS door grote bestanden of strings
- **Format Enforcement**: Voorkomt injection en malformed data
Dit schema biedt een solide foundation voor een veilig, schaalbaar en ontwikkelaarsvriendelijk foto-deel platform met enterprise-grade validatie!
error: Generation failed [deepseek] prompt_id=5680 locale=pt err=DeepSeek connection/timeout | Generation failed [deepseek] prompt_id=5680 locale=es err=DeepSeek connection/timeout

