Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GraphQL query #5

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ require (
go.uber.org/fx v1.17.1
go.uber.org/zap v1.21.0
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e
google.golang.org/grpc v1.46.0
google.golang.org/protobuf v1.28.0
)
Expand All @@ -41,6 +42,8 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gofrs/uuid v3.2.0+incompatible // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/graphql-go/graphql v0.8.0 // indirect
github.com/graphql-go/handler v0.2.3 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/magiconair/properties v1.8.5 // indirect
Expand All @@ -63,7 +66,6 @@ require (
golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e // indirect
gopkg.in/ini.v1 v1.63.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/graphql-go/graphql v0.8.0 h1:JHRQMeQjofwqVvGwYnr8JnPTY0AxgVy1HpHSGPLdH0I=
github.com/graphql-go/graphql v0.8.0/go.mod h1:nKiHzRM0qopJEwCITUuIsxk9PlVlwIiiI8pnJEhordQ=
github.com/graphql-go/handler v0.2.3 h1:CANh8WPnl5M9uA25c2GBhPqJhE53Fg0Iue/fRNla71E=
github.com/graphql-go/handler v0.2.3/go.mod h1:leLF6RpV5uZMN1CdImAxuiayrYYhOk33bZciaUGaXeU=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
Expand Down
4 changes: 4 additions & 0 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package app

import (
"github.com/kevinmichaelchen/api-go-template/internal/app/config"
"github.com/kevinmichaelchen/api-go-template/internal/app/cors"
"github.com/kevinmichaelchen/api-go-template/internal/app/graphql"
"github.com/kevinmichaelchen/api-go-template/internal/app/grpc"
"github.com/kevinmichaelchen/api-go-template/internal/app/logging"
"github.com/kevinmichaelchen/api-go-template/internal/app/metrics"
Expand All @@ -13,6 +15,8 @@ import (

var Module = fx.Options(
config.Module,
cors.Module,
graphql.Module,
grpc.Module,
logging.Module,
metrics.Module,
Expand Down
47 changes: 47 additions & 0 deletions internal/app/cors/cors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package cors

import (
"github.com/rs/cors"
"go.uber.org/fx"
"net/http"
)

var Module = fx.Module("cors",
fx.Provide(
NewCORS,
),
)

func NewCORS() *cors.Cors {
// To let web developers play with the demo service from browsers, we need a
// very permissive CORS setup.
return cors.New(cors.Options{
AllowedMethods: []string{
http.MethodHead,
http.MethodGet,
http.MethodPost,
http.MethodPut,
http.MethodPatch,
http.MethodDelete,
},
AllowOriginFunc: func(origin string) bool {
// Allow all origins, which effectively disables CORS.
return true
},
AllowedHeaders: []string{"*"},
ExposedHeaders: []string{
// Content-Type is in the default safelist.
"Accept",
"Accept-Encoding",
"Accept-Post",
"Connect-Accept-Encoding",
"Connect-Content-Encoding",
"Content-Encoding",
"Grpc-Accept-Encoding",
"Grpc-Encoding",
"Grpc-Message",
"Grpc-Status",
"Grpc-Status-Details-Bin",
},
})
}
20 changes: 20 additions & 0 deletions internal/app/graphql/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package graphql

import (
"context"
"github.com/sethvargo/go-envconfig"
)

func NewConfig() (cfg Config, err error) {
err = envconfig.Process(context.Background(), &cfg)
return
}

type Config struct {
GraphQLConfig *NestedConfig `env:",prefix=GRAPHQL_"`
}

type NestedConfig struct {
Host string `env:"HOST,default=localhost"`
Port int `env:"PORT,default=8082"`
}
83 changes: 83 additions & 0 deletions internal/app/graphql/graphql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package graphql

import (
"context"
"errors"
"fmt"
"github.com/graphql-go/graphql"
"github.com/graphql-go/handler"
"github.com/rs/cors"
"go.uber.org/fx"
"go.uber.org/zap"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
"net/http"
)

var Module = fx.Module("graphql",
fx.Provide(
NewConfig,
NewGraphQL,
NewSchema,
),
fx.Invoke(
RegisterGraphQL,
),
)

type registerGraphQLInput struct {
fx.In

Schema *graphql.Schema
Logger *zap.Logger
Mux *http.ServeMux `name:"graphqlMux"`
}

type NewGraphQLOutput struct {
fx.Out

Mux *http.ServeMux `name:"graphqlMux"`
}

func NewGraphQL(lc fx.Lifecycle, logger *zap.Logger, cfg Config, crs *cors.Cors) NewGraphQLOutput {
mux := http.NewServeMux()
address := fmt.Sprintf("%s:%d", cfg.GraphQLConfig.Host, cfg.GraphQLConfig.Port)
srv := &http.Server{
Addr: address,
// Use h2c so we can serve HTTP/2 without TLS.
Handler: h2c.NewHandler(
crs.Handler(mux),
&http2.Server{},
),
}
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
// In production, we'd want to separate the Listen and Serve phases for
// better error-handling.
go func() {
err := srv.ListenAndServe()
if err != nil && !errors.Is(err, http.ErrServerClosed) {
logger.Error("connect-go ListenAndServe failed", zap.Error(err))
}
}()
logger.Sugar().Infof("Listening for connect-go on: %s", address)
return nil
},
OnStop: func(ctx context.Context) error {
return srv.Shutdown(ctx)
},
})
return NewGraphQLOutput{
Mux: mux,
}
}

func RegisterGraphQL(in registerGraphQLInput) {
h := handler.New(&handler.Config{
Schema: in.Schema,
Pretty: true,
GraphiQL: true,
Playground: true,
})
in.Mux.Handle("/graphql", h)
}
31 changes: 31 additions & 0 deletions internal/app/graphql/schema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package graphql

import "github.com/graphql-go/graphql"

func NewSchema() (*graphql.Schema, error) {
q := graphql.NewObject(graphql.ObjectConfig{
Name: "RootQuery",
Fields: graphql.Fields{
"name": &graphql.Field{
Name: "name",
Type: graphql.String,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return "Kevin", nil
},
},
},
})

s, err := graphql.NewSchema(graphql.SchemaConfig{
Query: q,
Mutation: nil,
Subscription: nil,
Types: nil,
Directives: nil,
Extensions: nil,
})
if err != nil {
return nil, err
}
return &s, nil
}
38 changes: 2 additions & 36 deletions internal/app/grpc/connect_go.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ type NewConnectGoServerOutput struct {
Mux *http.ServeMux `name:"connectGoMux"`
}

func NewConnectGoServer(lc fx.Lifecycle, logger *zap.Logger, cfg Config) NewConnectGoServerOutput {
func NewConnectGoServer(lc fx.Lifecycle, logger *zap.Logger, cfg Config, crs *cors.Cors) NewConnectGoServerOutput {
mux := http.NewServeMux()
address := fmt.Sprintf("%s:%d", cfg.ConnectConfig.Host, cfg.ConnectConfig.Port)
srv := &http.Server{
Addr: address,
// Use h2c so we can serve HTTP/2 without TLS.
Handler: h2c.NewHandler(
newCORS().Handler(mux),
crs.Handler(mux),
&http2.Server{},
),
}
Expand All @@ -74,37 +74,3 @@ func NewConnectGoServer(lc fx.Lifecycle, logger *zap.Logger, cfg Config) NewConn
Mux: mux,
}
}

func newCORS() *cors.Cors {
// To let web developers play with the demo service from browsers, we need a
// very permissive CORS setup.
return cors.New(cors.Options{
AllowedMethods: []string{
http.MethodHead,
http.MethodGet,
http.MethodPost,
http.MethodPut,
http.MethodPatch,
http.MethodDelete,
},
AllowOriginFunc: func(origin string) bool {
// Allow all origins, which effectively disables CORS.
return true
},
AllowedHeaders: []string{"*"},
ExposedHeaders: []string{
// Content-Type is in the default safelist.
"Accept",
"Accept-Encoding",
"Accept-Post",
"Connect-Accept-Encoding",
"Connect-Content-Encoding",
"Content-Encoding",
"Grpc-Accept-Encoding",
"Grpc-Encoding",
"Grpc-Message",
"Grpc-Status",
"Grpc-Status-Details-Bin",
},
})
}