graphql

logo


Motivation

public record Customer(
        String name,
        String email,
        Address address,
        List<Order> orders,
        String avatarUrl) { }
/home
name, avatarUrl
/orders
orders
/profile
alles

Lösungen

/customers/{id} lädt alles
Extratraffic (mobile!)
3 Endpoints
Skalierung 😕
Backend restricted Frontend

GraphQL

{
    customer(id: 42) {
        name
        avatarUrl
    }
}
  • Client spezifiziert zu liefernde Daten
  • Server schickt nur Benötigtes
  • wird zu POST-Request
  • 2015 von Meta veröffentlicht

magic--

{
  students {
    id
    name
  }
}
POST http://localhost:8080/graphql
Content-Type: application/json

{
    "query": "{students {id name} }"
}

Schema

type Student {
    id: String
    name : String!
    schoolClass: SchoolClass!
}

type SchoolClass {
    title: String!
    students: [Student!]!
}
type Query {
  students: [Student!]!
  withClass(schoolClass: String!): [Student!]!
  student(id: String!): Student
}

Queries

query {
    students {
        id
        name
    }
}
{
    withClass(schoolClass: "5AHIF") {
        id
        name
        schoolClass{
            title
        }
    }
}

Mutations

type Mutation {
    createStudent(student: StudentInput!): Student
}

input StudentInput {
    name : String!
    classTitle: String!
}
mutation {
    createStudent(student: { name: "new", classTitle: "5AHIF" }) {
        id
    }
}

Controller

type Query {
    students: [Student!]!
    student(id: String!): Student
}

type Mutation {
    createStudent(student: StudentInput!): Student
}
@Controller
public class GraphQLController {

    @QueryMapping
    public List<Student> students() { ... }

    @QueryMapping
    public Student student(@Argument String id) { ... }

    @MutationMapping
    public Student createStudent(
            @Valid @Argument("student") StudentInput studentInput) { ... }
}

Error Handling

@Component
public class ExceptionResolver extends DataFetcherExceptionResolverAdapter {

    @Override
    protected List<GraphQLError> resolveToMultipleErrors(Throwable ex, DataFetchingEnvironment env) {
        var errors = new ArrayList<GraphQLError>();
        if (ex instanceof NoSuchElementException)
            errors.add(buildGraphQLError(ErrorType.NOT_FOUND, ex, env));
        if (ex instanceof BindException)
            errors.add(buildGraphQLError(ErrorType.BAD_REQUEST, ex, env));
        return errors;
    }

    private static GraphQLError buildGraphQLError(ErrorType type, Throwable ex, DataFetchingEnvironment env) {
        return GraphqlErrorBuilder.newError()
                .errorType(type)
                .message(ex.getMessage())
                .path(env.getExecutionStepInfo().getPath())
                .location(env.getField().getSourceLocation())
                .build();
    }
}