GraphQL
Write the schema in .graphqls files and run gqlgen generate (schema first). The root resolver is a provider, so it injects services:
func NewResolver(users *users.Service) *graph.Resolver { return &graph.Resolver{Users: users} }
var GraphModule = &dygo.Module{ Name: "graph", Imports: []*dygo.Module{users.Module}, Providers: []dygo.Provider{ dygo.Provide(NewResolver), dygo.Provide(func(r *graph.Resolver) graphql.ExecutableSchema { return graph.NewExecutableSchema(graph.Config{Resolvers: r}) }), }, Controllers: []dygo.Provider{graphql.Endpoint(graphql.Options{ Playground: true, Global: []dygo.RouteOption{dygo.Guards(dygo.Ref[*AuthGuard]())}, Fields: map[string][]dygo.RouteOption{"Mutation.deleteUser": {roles.Set([]string{"admin"})}}, })},}The endpoint is /graphql on the app’s HTTP server. Guards, interceptors and filters run around each root field with ec.Kind() == dygo.KindGraphQL; ec.GraphQL() gives Field() ("Mutation.deleteUser"), Args() and the HTTP Request(), and ec.HTTP() is nil, so a guard written for HTTP routes needs a GraphQL branch. A misspelt Fields key fails at boot. Resolvers return errs.NotFound(...) and friends as usual; clients get {"message": "user not found", "extensions": {"code": "NOT_FOUND", "statusCode": 404}}. Other errors and panics show “Internal server error” and go to Options.Logger. Add transport.Websocket{} to Options.Transports for subscriptions, and use Options.Configure for complexity limits. Parsed queries are cached (Options.QueryCacheSize, 1000 by default) and automatic persisted queries are on.