GraphQL Grundlagen Einführung
GraphQL: Die flexible API-Query-Sprache
GraphQL lässt Clients genau die Daten abfragen, die sie brauchen. Lernen Sie die Grundlagen dieser modernen API-Technologie.
GraphQL vs. REST
| Aspekt | REST | GraphQL |
|---|---|---|
| Endpoints | Viele (/users, /posts...) | Einer (/graphql) |
| Daten | Server bestimmt | Client bestimmt |
| Over-fetching | Häufig | Nie |
| Under-fetching | Häufig (N+1) | Selten |
| Versionierung | /v1, /v2 | Schema-Evolution |
Query-Beispiele
# REST: Mehrere Requests nötig
GET /users/1
GET /users/1/posts
GET /posts/5/comments
# GraphQL: Ein Request
query {
user(id: 1) {
name
email
posts {
title
comments {
text
author {
name
}
}
}
}
}
Schema Definition
# schema.graphql
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
createdAt: DateTime!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
comments: [Comment!]!
tags: [Tag!]!
published: Boolean!
}
type Comment {
id: ID!
text: String!
author: User!
post: Post!
}
type Tag {
id: ID!
name: String!
posts: [Post!]!
}
type Query {
user(id: ID!): User
users(limit: Int, offset: Int): [User!]!
post(id: ID!): Post
posts(published: Boolean): [Post!]!
}
type Mutation {
createUser(input: CreateUserInput!): User!
createPost(input: CreatePostInput!): Post!
updatePost(id: ID!, input: UpdatePostInput!): Post
deletePost(id: ID!): Boolean!
}
input CreateUserInput {
name: String!
email: String!
password: String!
}
input CreatePostInput {
title: String!
content: String!
tags: [ID!]
}
Node.js Server (Apollo)
npm install @apollo/server graphql
const { ApolloServer } = require('@apollo/server');
const { startStandaloneServer } = require('@apollo/server/standalone');
const typeDefs = `#graphql
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
}
type Query {
users: [User!]!
user(id: ID!): User
posts: [Post!]!
}
type Mutation {
createPost(title: String!, content: String!, authorId: ID!): Post!
}
`;
const resolvers = {
Query: {
users: () => db.users.findAll(),
user: (_, { id }) => db.users.findById(id),
posts: () => db.posts.findAll(),
},
Mutation: {
createPost: async (_, { title, content, authorId }) => {
return db.posts.create({ title, content, authorId });
},
},
// Field Resolver
User: {
posts: (user) => db.posts.findByAuthor(user.id),
},
Post: {
author: (post) => db.users.findById(post.authorId),
},
};
const server = new ApolloServer({ typeDefs, resolvers });
startStandaloneServer(server, { listen: { port: 4000 } })
.then(({ url }) => console.log(`Server ready at ${url}`));
Client-Anfragen
// Query
const GET_USER = `
query GetUser($id: ID!) {
user(id: $id) {
name
email
posts {
title
}
}
}
`;
// Fetch
const response = await fetch('/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: GET_USER,
variables: { id: '1' }
})
});
const { data, errors } = await response.json();
// Mutation
const CREATE_POST = `
mutation CreatePost($title: String!, $content: String!, $authorId: ID!) {
createPost(title: $title, content: $content, authorId: $authorId) {
id
title
}
}
`;
Fragments (Wiederverwendbare Felder)
fragment UserFields on User {
id
name
email
}
query {
user(id: 1) {
...UserFields
posts {
title
author {
...UserFields
}
}
}
}
Subscriptions (Echtzeit)
# Schema
type Subscription {
postCreated: Post!
commentAdded(postId: ID!): Comment!
}
# Client
subscription {
postCreated {
id
title
author {
name
}
}
}
N+1 Problem lösen (DataLoader)
const DataLoader = require('dataloader');
// Batched Loading
const userLoader = new DataLoader(async (userIds) => {
const users = await db.users.findByIds(userIds);
return userIds.map(id => users.find(u => u.id === id));
});
const resolvers = {
Post: {
// Ohne DataLoader: N Queries
// author: (post) => db.users.findById(post.authorId)
// Mit DataLoader: 1 Query für alle
author: (post) => userLoader.load(post.authorId)
}
};
Fehlerbehandlung
// Response Format
{
"data": {
"user": null
},
"errors": [
{
"message": "User nicht gefunden",
"locations": [{ "line": 2, "column": 3 }],
"path": ["user"],
"extensions": {
"code": "NOT_FOUND"
}
}
]
}
// Im Resolver
const resolvers = {
Query: {
user: (_, { id }) => {
const user = db.users.findById(id);
if (!user) {
throw new GraphQLError('User nicht gefunden', {
extensions: { code: 'NOT_FOUND' }
});
}
return user;
}
}
};
💡 Tipp:
GraphQL bietet eine eingebaute API-Dokumentation. Nutzen Sie GraphQL Playground oder Apollo Studio zum Testen.