Error Handling di GraphQL
GraphQL selalu return HTTP 200. Errors ada di response body field errors. Ini berbeda dari REST yang pakai status codes.
Default Error Format
{
"data": { "user": null },
"errors": [{
"message": "User not found",
"locations": [{ "line": 2, "column": 3 }],
"path": ["user"],
"extensions": {
"code": "NOT_FOUND",
"http": { "status": 404 }
}
}]
}
Throwing Errors in Resolvers
import { GraphQLError } from "graphql";
const resolvers = {
Query: {
user: async (_, { id }, context) => {
if (!context.user) {
throw new GraphQLError("Authentication required", {
extensions: { code: "UNAUTHENTICATED" },
});
}
const user = await context.db.users.findById(id);
if (!user) {
throw new GraphQLError("User not found", {
extensions: { code: "NOT_FOUND" },
});
}
return user;
},
},
};
Union-based Errors (Recommended)
# Schema — error as data
union CreateUserResult = User | ValidationError | DuplicateEmailError
type ValidationError {
field: String!
message: String!
}
type DuplicateEmailError {
email: String!
message: String!
}
# Resolver
createUser: async (_, { input }) => {
if (!input.email.includes("@")) {
return { __typename: "ValidationError", field: "email", message: "Email tidak valid" };
}
const existing = await db.users.findByEmail(input.email);
if (existing) {
return { __typename: "DuplicateEmailError", email: input.email, message: "Email sudah terdaftar" };
}
const user = await db.users.create({ data: input });
return { __typename: "User", ...user };
}
# Client query
mutation {
createUser(input: { name: "Budi", email: "[email protected]" }) {
... on User { id name }
... on ValidationError { field message }
... on DuplicateEmailError { email message }
}
}
Partial Errors
GraphQL bisa return data DAN errors sekaligus — partial success. Field yang error jadi null, sisanya tetap ada.