Ontwerp een Geavanceerd GraphQL-schema met Geautomatiseerde Data Normalisatie en Consistentiebeheer
description
Met deze prompt kun je een GraphQL-schema ontwerpen dat automatisch data normaliseert en consistentie bewaakt, waardoor je API betrouwbaarder, schaalbaarder en onderhoudsvriendelijker wordt. Dit lost problemen op rondom data-inconsistenties en maakt het eenvoudiger om complexe datasets te beheren zonder duplicatie of conflicten. De prompt biedt concrete voorbeelden en best practices, beter dan generieke schema-ontwerpen die deze automatisering missen.
prompt
Help me een geavanceerd GraphQL-schema te ontwerpen dat geautomatiseerde data normalisatie en consistentiebeheer ondersteunt binnen mijn domein: <beschrijf je domein of toepassingsgebied>. Ik wil functies zoals: <specificeer gewens ...
try_prompt
generate_helper
disclaimerOnPageApi
tags
ai_answers
provider: openai
model: gpt-4.1-nano
Hier is een geavanceerd GraphQL-schema dat automatische normalisatie en consistentiebeheer in jouw social media platform ondersteunt. Het schema bevat custom scalars en resolvers die zorgen voor datakwaliteit, schaalbaarheid en fouttolerantie.
### 1. Belangrijkste ontwerpprincipes
- **Normalisatie bij invoer:** Gebruik custom scalars en resolvers om telefoonnummers te normaliseren naar internationaal formaat en e-mails naar kleine letters.
- **Schema-uitbreiding:** Voeg input types en mutaties toe om datakwaliteit te waarborgen bij creatie en update.
- **Flexibiliteit en schaalbaarheid:** Schematiseer entiteiten en scheid validatie van businesslogica via resolvers.
- **Foutafhandeling:** Gebruik uitgebreide foutmeldingen en fallback-mechanismen.
---
### 2. GraphQL-schema ontwerp
```graphql
# Custom scalar voor telefoonnummers
scalar PhoneNumber
# Custom scalar voor e-mailadressen
scalar Email
# User type
type User {
id: ID!
username: String!
email: Email!
phone: PhoneNumber
createdAt: String!
updatedAt: String!
}
# Bericht type
type Post {
id: ID!
author: User!
content: String!
createdAt: String!
comments: [Comment!]!
}
# Reactie type
type Comment {
id: ID!
author: User!
content: String!
createdAt: String!
}
# Query type
type Query {
getUser(id: ID!): User
getPost(id: ID!): Post
listUsers: [User!]!
listPosts: [Post!]!
}
# Input types voor mutaties
input CreateUserInput {
username: String!
email: Email!
phone: PhoneNumber
}
input UpdateUserInput {
email: Email
phone: PhoneNumber
}
# Mutatie type
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
createPost(authorId: ID!, content: String!): Post!
addComment(postId: ID!, authorId: ID!, content: String!): Comment!
}
```
---
### 3. Normalisatie en validatie in resolvers
- **Email:** Convert naar kleine letters voordat opgeslagen.
- **Telefoonnummer:** Normaliseer naar internationaal formaat (bijvoorbeeld +31..., +49..., etc.) met behulp van een bibliotheek zoals `libphonenumber`.
**Voorbeeld resolver voor createUser:**
```javascript
const { parsePhoneNumberFromString } = require('libphonenumber-js');
const resolvers = {
Email: {
serialize(value) {
return value.toLowerCase(); // Normaliseer naar kleine letters
},
parseValue(value) {
// Validatie kan hier worden toegevoegd
return value.toLowerCase();
}
},
PhoneNumber: {
serialize(value) {
return value; // Al genormaliseerd
},
parseValue(value) {
const phoneNumber = parsePhoneNumberFromString(value);
if (!phoneNumber || !phoneNumber.isValid()) {
throw new Error('Ongeldig telefoonnummer');
}
return phoneNumber.formatInternational(); // Normaliseer
}
},
Mutation: {
createUser: (_, { input }) => {
const email = input.email.toLowerCase();
let phone = null;
if (input.phone) {
const phoneNumber = parsePhoneNumberFromString(input.phone);
if (!phoneNumber || !phoneNumber.isValid()) {
throw new Error('Ongeldig telefoonnummer');
}
phone = phoneNumber.formatInternational();
}
// Sla op in database met genormaliseerde waarden
const newUser = {
id: generateID(),
username: input.username,
email,
phone,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};
// ... database opslaan
return newUser;
},
updateUser: (_, { id, input }) => {
// Fetch bestaande user
const user = getUserById(id);
if (!user) throw new Error('Gebruiker niet gevonden');
if (input.email) {
user.email = input.email.toLowerCase();
}
if (input.phone) {
const phoneNumber = parsePhoneNumberFromString(input.phone);
if (!phoneNumber || !phoneNumber.isValid()) {
throw new Error('Ongeldig telefoonnummer');
}
user.phone = phoneNumber.formatInternational();
}
user.updatedAt = new Date().toISOString();
// ... database update
return user;
}
}
};
```
---
### 4. Toelichting op ontwerpkeuzes
- **Data kwaliteit:** Door gebruik te maken van custom scalars en preprocessing in resolvers, wordt de datakwaliteit gewaarborgd. E-mails worden altijd in kleine letters opgeslagen, telefoonnummers worden genormaliseerd, wat consistentie en correcte data-invoer garandeert.
- **Schaalbaarheid:** Scheiding van validatie (resolvers) en data opslag maakt het mogelijk om eenvoudig te schalen en te integreren met verschillende databronnen. De schematische scheiding zorgt voor beheersbaarheid in complexe API-omgevingen.
- **Fouttolerantie:** Door uitgebreide validatie en foutmeldingen wordt het systeem robuust; ongeldige data wordt afgewezen met duidelijke foutmeldingen, wat debugging en datakwaliteit verbetert.
- **Flexibiliteit:** Het gebruik van input types en mutaties maakt het mogelijk om complexe workflows te ondersteunen zonder dat de API onnodig ingewikkeld wordt.
---
### Samenvatting
Dit schema zorgt voor automatische datanormalisatie via custom scalars en resolvers, ondersteunt schaalbare en fouttolerante data-invoer, en bevordert datakwaliteit binnen je social media platform. Door deze aanpak te volgen, blijft je API robuust en consistent, zelfs in complexe omgevingen.
provider: deepseek
model: deepseek-chat
Zeker, ik help je met een geavanceerd GraphQL-schema voor een sociaal media platform. Hieronder vind je een uitgebreid ontwerp met automatische data-normalisatie en consistentiebeheer.
---
### **GraphQL Schema Ontwerp**
```graphql
# Basis types
scalar PhoneNumber
scalar EmailAddress
scalar DateTime
type User {
id: ID!
username: String!
email: EmailAddress!
phone: PhoneNumber
profile: UserProfile
posts: [Post!]!
createdAt: DateTime!
updatedAt: DateTime!
}
type UserProfile {
displayName: String!
avatar: String
bio: String
}
type Post {
id: ID!
author: User!
content: String!
media: [String!]
reactions: [Reaction!]!
comments: [Comment!]!
createdAt: DateTime!
updatedAt: DateTime!
}
type Comment {
id: ID!
post: Post!
author: User!
content: String!
reactions: [Reaction!]!
createdAt: DateTime!
}
type Reaction {
id: ID!
type: ReactionType!
user: User!
createdAt: DateTime!
}
enum ReactionType {
LIKE
LOVE
HAHA
WOW
SAD
ANGRY
}
# Input types met validatie
input CreateUserInput {
username: String!
email: String!
phone: String
profile: UserProfileInput
}
input UpdateUserInput {
username: String
email: String
phone: String
profile: UserProfileInput
}
input UserProfileInput {
displayName: String!
avatar: String
bio: String
}
input CreatePostInput {
content: String!
media: [String!]
}
# Queries & Mutations
type Query {
getUser(id: ID!): User
getPost(id: ID!): Post
getUserPosts(userId: ID!): [Post!]!
searchUsers(query: String!): [User!]!
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
createPost(userId: ID!, input: CreatePostInput!): Post!
addReaction(postId: ID!, type: ReactionType!): Reaction!
addComment(postId: ID!, content: String!): Comment!
}
```
---
### **Custom Scalars & Normalisatie**
```javascript
// Voorbeeld implementatie (Node.js/Apollo)
const { GraphQLScalarType } = require('graphql');
const { parsePhoneNumberFromString } = require('libphonenumber-js');
const PhoneNumber = new GraphQLScalarType({
name: 'PhoneNumber',
description: 'Telefoonnummer in E.164 formaat',
serialize(value) {
// Validatie bij uitgaande data
if (!isValidPhoneNumber(value)) {
throw new Error('Ongeldig telefoonnummer formaat');
}
return value;
},
parseValue(value) {
// Normalisatie bij inkomende data
const phoneNumber = parsePhoneNumberFromString(value, 'NL');
if (!phoneNumber || !phoneNumber.isValid()) {
throw new Error('Ongeldig telefoonnummer');
}
return phoneNumber.format('E.164');
}
});
const EmailAddress = new GraphQLScalarType({
name: 'EmailAddress',
description: 'Geverifieerd e-mailadres in lowercase',
serialize(value) {
return value;
},
parseValue(value) {
const normalizedEmail = value.toLowerCase().trim();
if (!isValidEmail(normalizedEmail)) {
throw new Error('Ongeldig e-mailadres');
}
return normalizedEmail;
}
});
```
---
### **Data Consistentie Laag**
```javascript
// Data Consistency Manager
class DataConsistencyManager {
constructor() {
this.normalizers = {
phone: this.normalizePhone,
email: this.normalizeEmail
};
}
normalizePhone(phone) {
const parsed = parsePhoneNumberFromString(phone, 'NL');
return parsed?.isValid() ? parsed.format('E.164') : null;
}
normalizeEmail(email) {
return email.toLowerCase().trim();
}
async ensureConsistency(input, context) {
const normalized = { ...input };
// Automatische normalisatie
if (normalized.phone) {
normalized.phone = this.normalizePhone(normalized.phone);
}
if (normalized.email) {
normalized.email = this.normalizeEmail(normalized.email);
}
// Duplicate prevention
await this.checkUniqueness(normalized, context);
return normalized;
}
async checkUniqueness(data, context) {
if (data.email) {
const existing = await context.db.user.findUnique({
where: { email: data.email }
});
if (existing) throw new Error('E-mailadres already in use');
}
}
}
```
---
### **Resolvers met Foutafhandeling**
```javascript
const resolvers = {
Mutation: {
createUser: async (_, { input }, context) => {
try {
// Data normalisatie & consistentie check
const normalizedInput = await context.consistencyManager.ensureConsistency(input, context);
// Transactionele operatie
return await context.db.$transaction(async (tx) => {
const user = await tx.user.create({
data: {
...normalizedInput,
profile: input.profile ? {
create: input.profile
} : undefined
},
include: { profile: true }
});
// Audit log
await tx.auditLog.create({
data: {
action: 'USER_CREATED',
userId: user.id,
metadata: { input: normalizedInput }
}
});
return user;
});
} catch (error) {
// Gestructureerde foutafhandeling
context.logger.error('User creation failed', { error, input });
throw new Error(`Gebruikersaanmaak mislukt: ${error.message}`);
}
}
}
};
```
---
### **Ontwerp Keuzes & Voordelen**
#### **1. Datakwaliteit**
- **Automatische Normalisatie**: Telefoonnummers en e-mails worden gestandaardiseerd bij invoer
- **Validatie op Schema-niveau**: Custom scalars garanderen geldige dataformaten
- **Unieke Constraints**: Database-level constraints voorkomen duplicates
#### **2. Schaalbaarheid**
- **Efficiente Queries**: Geneste relaties minimaliseren N+1 query problemen
- **Cursor-based Pagination**: Kan eenvoudig worden toegevoegd voor lijsten
- **Caching-strategie**: GraphQL responses zijn ideaal voor CDN-caching
#### **3. Fouttolerantie**
- **Transactionele Operaties**: Gehele operaties of geen (atomiciteit)
- **Gestructureerde Foutafhandeling**: Consistente error responses
- **Circuit Breaker Pattern**: Kan worden geïmplementeerd voor externe services
#### **4. Onderhoudbaarheid**
- **Schema First Design**: Duidelijke contracten tussen frontend en backend
- **Modular Design**: Normalisatie-logica is centrale verantwoordelijkheid
- **Uitbreidbaarheid**: Nieuwe velden kunnen eenvoudig normalisatie-regels krijgen
---
### **Aanbevolen Patronen**
1. **DataLoader voor N+1 Problemen**:
```javascript
const userLoader = new DataLoader(async (userIds) => {
const users = await context.db.user.findMany({
where: { id: { in: userIds } }
});
return userIds.map(id => users.find(user => user.id === id));
});
```
2. **Audit Logging voor Traceerbaarheid**:
```graphql
type AuditLog {
id: ID!
action: String!
userId: ID
metadata: JSON
createdAt: DateTime!
}
```
3. **Rate Limiting**:
```javascript
// Implementatie aan de resolver-level voor abuse prevention
```
Dit ontwerp biedt een robuuste foundation die datakwaliteit garandeert terwijl het schaalbaar en onderhoudbaar blijft in complexe API-omgevingen. De automatische normalisatie zorgt voor consistente data zonder extra client-logica, terwijl de foutafhandeling betrouwbare operaties garandeert.