GraphQL Fundamentals
💡 Vad är GraphQL?
GraphQL är som en smart kypare som låter dig beställa exakt vad du vill ha!
GraphQL skapades av Facebook 2012 och blev öppet standardiserat 2015. Det är ett API-språk som gör att du kan fråga efter exakt den data du behöver, utan att få överflödig information. Tänk på det som en meny där du kan välja exakt vilka rätter du vill ha, istället för en fast meny med allt på.
- Det är som att beställa en pizza - med REST skulle du få hela pizzan med alla toppings, medan GraphQL låter dig beställa bara ost och skinka, plus en läsk utan is. Du får exakt det du bad om, inget mer, inget mindre. Det är som SQL för API:er - du skriver en query och får precis den data du bad om.
Med vanlig REST säger du “ge mig en pizza” och får en hel pizza med allt. Med GraphQL säger du “jag vill ha bara ost och skinka från pizzan, plus en läsk men bara märket”. Du får exakt det - inget mer, inget mindre!
GraphQL är ett API-språk som gör att du kan fråga efter exakt den data du behöver, utan att få överflödig information. Det är som SQL för API:er - du skriver en query och får precis den data du bad om.
🎯 TL;DR
GraphQL = “SQL för API:er”. Du skriver en query och får exakt den data du bad om. Mindre nätverkstrafik, färre requests, utvecklaren bestämmer vad som hämtas.
📋 Efter att ha läst detta kommer du att kunna
- Förstå skillnaden mellan REST och GraphQL
- Skriva grundläggande GraphQL queries
- Implementera GraphQL i ASP.NET Core
- Hantera mutations (skapa/uppdatera data)
- Förstå när GraphQL är bättre än REST
🔧 Grundprinciper
Princip 1: En endpoint, många möjligheter
Istället för /users, /posts, /comments har GraphQL EN endpoint: /graphql
Princip 2: Klienten bestämmer
Du skickar en query och anger exakt vilka fält du vill ha.
Princip 3: Strongly typed
Allt har en tydlig typ - precis som C-Sharp.
💭 Tänk så här (REST vs GraphQL)
💬 Skillnaden mellan REST och GraphQL:
REST (traditionellt):
GET /users/1 → { id, name, email, address, phone, ... }
GET /users/1/posts → [{ id, title, content, author, date, ... }]
GET /posts/1/comments → [{ id, text, author, date, ... }]
GraphQL (smart):
POST /graphql med query:
{
user(id: 1) {
name
posts {
title
comments {
text
}
}
}
}
Resultat: Exakt det du frågade efter!
🚀 GraphQL i praktiken
🟢 Grundläggande GraphQL query-syntax
# Hämta användarnamn och email
{
user(id: 1) {
name
email
}
}
# Resultat:
{
"data": {
"user": {
"name": "Marcus",
"email": "marcus@example.com"
}
}
}
# Mer komplex query - användare med deras posts
{
user(id: 1) {
name
email
posts {
title
content
createdAt
comments {
text
author {
name
}
}
}
}
}
🟡 GraphQL i ASP.NET Core med HotChocolate
// Install: dotnet add package HotChocolate.AspNetCore
// 1. Definiera dina data-typer
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public List<Post> Posts { get; set; } = new();
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
// 2. Skapa Query-klass
public class Query
{
// Hämta alla användare
public List<User> GetUsers([Service] IUserService userService)
{
return userService.GetAllUsers();
}
// Hämta specifik användare
public User? GetUser(int id, [Service] IUserService userService)
{
return userService.GetUserById(id);
}
// Hämta alla posts
public List<Post> GetPosts([Service] IPostService postService)
{
return postService.GetAllPosts();
}
}
// 3. Konfigurera i Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<IPostService, PostService>();
builder.Services
.AddGraphQLServer()
.AddQueryType<Query>();
var app = builder.Build();
app.MapGraphQL();
app.Run();
🔴 Avancerat: Mutations och Subscriptions
// Mutation för att skapa/uppdatera data
public class Mutation
{
public User CreateUser(string name, string email, [Service] IUserService userService)
{
var user = new User { Name = name, Email = email };
return userService.CreateUser(user);
}
public User UpdateUser(int id, string? name, string? email, [Service] IUserService userService)
{
var user = userService.GetUserById(id);
if (user == null) throw new Exception("User not found");
if (name != null) user.Name = name;
if (email != null) user.Email = email;
return userService.UpdateUser(user);
}
public bool DeleteUser(int id, [Service] IUserService userService)
{
return userService.DeleteUser(id);
}
}
// Subscription för real-time updates
public class Subscription
{
[Subscribe]
public User OnUserCreated([EventMessage] User user) => user;
[Subscribe]
public Post OnPostCreated([EventMessage] Post post) => post;
}
// Uppdatera Program.cs
builder.Services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddMutationType<Mutation>()
.AddSubscriptionType<Subscription>()
.AddInMemorySubscriptions(); // För real-time
GraphQL Mutation exempel:
mutation {
createUser(name: "Anna", email: "anna@example.com") {
id
name
email
}
}
GraphQL Subscription exempel:
subscription {
onUserCreated {
id
name
email
}
}
🔍 GraphQL vs REST - När ska du använda vad?
✅ Använd GraphQL när:
- Mobil-appar (begränsat med data/batteri)
- Komplexa relationer mellan data
- Många olika klienter med olika behov
- Du vill undvika over-fetching/under-fetching
- Real-time updates är viktiga
✅ Använd REST när:
- Enkla CRUD-operationer
- Caching är super-viktigt
- File uploads/downloads
- Teamet är mer bekant med REST
- Befintliga verktyg och infrastruktur
🛠️ GraphQL verktyg och testing
GraphQL Playground
När du kör din app går du till /graphql och får en interaktiv editor:
# Testa dina queries direkt i browsern
{
users {
name
email
posts(limit: 5) {
title
}
}
}
C-Sharp GraphQL Client
// Install: dotnet add package StrawberryShake
// eller använd HttpClient direkt:
public class GraphQLClient
{
private readonly HttpClient _httpClient;
public GraphQLClient(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<T> QueryAsync<T>(string query, object? variables = null)
{
var request = new
{
query = query,
variables = variables
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync("/graphql", content);
var responseJson = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<GraphQLResponse<T>>(responseJson);
return result.Data;
}
}
public class GraphQLResponse<T>
{
public T Data { get; set; }
public List<GraphQLError>? Errors { get; set; }
}
public class GraphQLError
{
public string Message { get; set; }
}
⚠️ Vanliga GraphQL-misstag
Misstag 1: N+1 queries problem
- Varje användare triggar en separat databas-query för posts
- Lösning: Använd DataLoader pattern
Misstag 2: Ingen djup-begränsning
- Klienten kan skriva oändligt djupa queries
- Lösning: Sätt max depth limits
Misstag 3: Glömma error handling
- GraphQL returnerar alltid 200 OK, även för fel
- Lösning: Kolla
errorsi response
// DataLoader för att lösa N+1 problem
public class PostByUserIdDataLoader : BatchDataLoader<int, Post[]>
{
private readonly IPostService _postService;
public PostByUserIdDataLoader(IPostService postService)
{
_postService = postService;
}
protected override async Task<IReadOnlyDictionary<int, Post[]>> LoadBatchAsync(
IReadOnlyList<int> userIds,
CancellationToken cancellationToken)
{
var posts = await _postService.GetPostsByUserIds(userIds);
return posts.GroupBy(p => p.UserId)
.ToDictionary(g => g.Key, g => g.ToArray());
}
}
📚 Sammanfattning
GraphQL är kraftfullt men komplexare än REST:
- En endpoint för allt
- Klienten bestämmer vilka fält som hämtas
- Mindre nätverkstrafik och färre requests
- Strongly typed schema
- Real-time subscriptions möjliga
- Brantare inlärningskurva än REST
Faror
Då GraphQL ger klienten mycket makt, kan det leda till:
- Över-fetching: Klienten hämtar mer data än nödvändigt
- Under-fetching: Klienten måste göra flera requests för att få all data
- Komplexa queries som kan bli svåra att optimera
- Säkerhetsrisker: Klienten kan fråga efter känslig data
- Prestandaproblem: Om inte rätt begränsningar sätts kan queries bli för tunga
- N+1 queries problem: Varje relation kan leda till extra databas-anrop
- Ingen standardisering: Olika API:er kan ha olika konventioner
…och i värsta fall kan gode vän Boddy Tables hälsa på
Källa: XKCD 327
- SQL-Injection: Om inte queries är korrekt sanitiserade kan det leda till allvarliga säkerhetsproblem.
🎯 Övningar
- 🟢 Grundläggande: Skapa ett enkelt GraphQL API för Books med queries
- 🟡 Utmanande: Lägg till mutations för att skapa/uppdatera böcker
- 🔴 Expert: Implementera DataLoader för att undvika N+1 queries
😄 Obligatorisk dad joke
Varför är GraphQL-utvecklare så specifika? För att de alltid vill ha exakt vad de frågar efter - inget mer, inget mindre! 🎯