github.com/mstephano/gqlgen-schemagen@v0.0.0-20230113041936-dd2cd4ea46aa/docs/content/reference/errors.md (about) 1 --- 2 linkTitle: Handling Errors 3 title: Sending custom error data in the graphql response 4 description: Customising graphql error types to send custom error data back to the client using gqlgen. 5 menu: { main: { parent: "reference", weight: 10 } } 6 --- 7 8 ## Returning errors 9 10 All resolvers simply return an error to be sent to the user. The assumption is that any error message returned 11 here is appropriate for end users. If certain messages aren't safe, customise the error presenter. 12 13 ### Multiple errors 14 15 To return multiple errors you can call the `graphql.Error` functions like so: 16 17 ```go 18 package foo 19 20 import ( 21 "context" 22 "errors" 23 24 "github.com/vektah/gqlparser/v2/gqlerror" 25 "github.com/mstephano/gqlgen-schemagen/graphql" 26 ) 27 28 // DoThings add errors to the stack. 29 func (r Query) DoThings(ctx context.Context) (bool, error) { 30 // Print a formatted string 31 graphql.AddErrorf(ctx, "Error %d", 1) 32 33 // Pass an existing error out 34 graphql.AddError(ctx, gqlerror.Errorf("zzzzzt")) 35 36 // Or fully customize the error 37 graphql.AddError(ctx, &gqlerror.Error{ 38 Path: graphql.GetPath(ctx), 39 Message: "A descriptive error message", 40 Extensions: map[string]interface{}{ 41 "code": "10-4", 42 }, 43 }) 44 45 // And you can still return an error if you need 46 return false, gqlerror.Errorf("BOOM! Headshot") 47 } 48 ``` 49 50 They will be returned in the same order in the response, eg: 51 52 ```json 53 { 54 "data": { 55 "todo": null 56 }, 57 "errors": [ 58 { "message": "Error 1", "path": ["todo"] }, 59 { "message": "zzzzzt", "path": ["todo"] }, 60 { 61 "message": "A descriptive error message", 62 "path": ["todo"], 63 "extensions": { "code": "10-4" } 64 }, 65 { "message": "BOOM! Headshot", "path": ["todo"] } 66 ] 67 } 68 ``` 69 70 or you can simply return multiple errors 71 72 ```go 73 package foo 74 75 import ( 76 "context" 77 "errors" 78 79 "github.com/vektah/gqlparser/v2/gqlerror" 80 "github.com/mstephano/gqlgen-schemagen/graphql" 81 ) 82 83 var errSomethingWrong = errors.New("some validation failed") 84 85 // DoThingsReturnMultipleErrors collect errors and returns it if any. 86 func (r Query) DoThingsReturnMultipleErrors(ctx context.Context) (bool, error) { 87 errList := gqlerror.List{} 88 89 // Add existing error 90 errList = append(errList, gqlerror.Wrap(errSomethingWrong)) 91 92 // Create new formatted and append 93 errList = append(errList, gqlerror.Errorf("invalid value: %s", "invalid")) 94 95 // Or fully customize the error and append 96 errList = append(errList, &gqlerror.Error{ 97 Path: graphql.GetPath(ctx), 98 Message: "A descriptive error message", 99 Extensions: map[string]interface{}{ 100 "code": "10-4", 101 }, 102 }) 103 104 return false, errList 105 } 106 ``` 107 108 They will be returned in the same order in the response, eg: 109 110 ```json 111 { 112 "data": { 113 "todo": null 114 }, 115 "errors": [ 116 { "message": "some validation failed", "path": ["todo"] }, 117 { "message": "invalid value: invalid", "path": ["todo"] }, 118 { "message": "A descriptive error message", "path": ["todo"], "extensions": { "code": "10-4" } } 119 ] 120 } 121 ``` 122 123 ## Hooks 124 125 ### The error presenter 126 127 All `errors` returned by resolvers, or from validation, pass through a hook before being displayed to the user. 128 This hook gives you the ability to customise errors however makes sense in your app. 129 130 The default error presenter will capture the resolver path and use the Error() message in the response. 131 132 You change this when creating the server: 133 134 ```go 135 package bar 136 137 import ( 138 "context" 139 "errors" 140 141 "github.com/vektah/gqlparser/v2/gqlerror" 142 "github.com/mstephano/gqlgen-schemagen/graphql" 143 "github.com/mstephano/gqlgen-schemagen/graphql/handler" 144 ) 145 146 func main() { 147 server := handler.NewDefaultServer(MakeExecutableSchema(resolvers)) 148 server.SetErrorPresenter(func(ctx context.Context, e error) *gqlerror.Error { 149 err := graphql.DefaultErrorPresenter(ctx, e) 150 151 var myErr *MyError 152 if errors.As(e, &myErr) { 153 err.Message = "Eeek!" 154 } 155 156 return err 157 }) 158 } 159 160 ``` 161 162 This function will be called with the same resolver context that generated it, so you can extract the 163 current resolver path and whatever other state you might want to notify the client about. 164 165 ### The panic handler 166 167 There is also a panic handler, called whenever a panic happens to gracefully return a message to the user before 168 stopping parsing. This is a good spot to notify your bug tracker and send a custom message to the user. Any errors 169 returned from here will also go through the error presenter. 170 171 You change this when creating the server: 172 173 ```go 174 server := handler.NewDefaultServer(MakeExecutableSchema(resolvers) 175 server.SetRecoverFunc(func(ctx context.Context, err interface{}) error { 176 // notify bug tracker... 177 178 return gqlerror.Errorf("Internal server error!") 179 }) 180 ```