Coder Social home page Coder Social logo

Documentation about graphql-go HOT 11 CLOSED

graph-gophers avatar graph-gophers commented on August 23, 2024 19
Documentation

from graphql-go.

Comments (11)

EmperorEarth avatar EmperorEarth commented on August 23, 2024 14

I'm figuring it out as I go but I'll contribute 1-2 examples to this repo in the next 2 monthseventually, with persistence and authentication+authorization. (I have those things working right now, but want some time to check if my implementations have gaps)

For now, this is what I've been doing:

schema.graphql

type Schema {
	query: Query
	mutation: Mutation
}
type Query {
	foo(id: ID!): Foo
}
type Mutation {
	createFoo(bar: String!, baz: String): Foo
}
type Foo {
	id: ID!
	bar: String!
	baz: String
}

main.go

func main() {
	db, _ db.Open("/DSN")
	rawSchema, _ := ioutil.ReadFile("/path/to/schema.graphql")
	parsedSchema, _ := graphql.ParseSchema(string(rawSchema), NewResolver(db))
	if os.Getenv("production") != "true" {
		http.HandleFunc("/", graphiqlHandler)
	}
	http.Handle("/graphql", Middleware(injectViewerToContext(&relay.Handler{Schema: parsedSchema})))
	http.ListenAndServe(":8080", nil)
}

func injectViewerToContext(next http.Handler) http.Handler {
	return func(w http.ResponseWriter, r *http.Request) {
		tokenString := r.Header.Get("Authorization")
		viewerID, authenticated := authenticate(tokenString) // custom 401 function
		if !authenticated {
			w.Write(http.StatusUnauthorized)
			return
		}

		next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), "Authorization", viewerID))) // could pass a custom viewer type with roles, etc
	}
}

resolver.go

type Resolver { db: *sql.DB }
func NewResolver(db *sql.DB) *Resolver {
	return &Resolver{db: db}
}

func (r *Resolver) Foo(ctx context.Context, args *struct{ ID string }) (*FooResolver, error) {
	viewerID, _ := context.Value("auth", ctx).(uuid.UUID)
	return NewFooResolver(viewerID, args.ID, r.db)
}

func (r *Resolver) CreateFoo(ctx context.Context, args *struct{
	Bar	string
	Baz	*string
}) (*FooResolver, error) {
	viewerID, _ := context.Value("auth", ctx).(uuid.UUID)
	id, _ := NewFoo(viewerID, args.Bar, args.Baz, r.db)
	return NewFooResolver(viewerID, id, r.db)
}

foo.go

func NewFoo(viewerID uuid.UUID, bar string, bazPtr *string, db *sql.DB) (*FooResolver, error) {
	// do some 403
	if !mayCreateFoo(viewerID) {
		return nil, errors.New("403")
	}

	id := uuid.NewV1()
	tx, _ := db.Begin()
	_, _ = tx.Exec("INSERT INTO foo_bar (id, bar) VALUES (?, ?)", id.Bytes(), args.Bar)
	if bazPtr != nil {
		_, _ = tx.Exec("INSERT INTO foo_baz (id, baz) VALUES (?, ?)", id.Bytes(), *args.Baz)
	}
	_ = tx.Commit()
	return NewFooResolver(viewerID, id, db), nil
}

type FooResolver struct {
	viewerID	uuid.UUID
	id		uuid.UUID
	db		*sql.DB
}

func NewFooResolver(viewerID, id uuid.UUID, db *sql.DB) (*FooResolver, error) {
	return &FooResolver{
		viewerID: viewerID,
		id: id,
		db: db,
	}, nil
}

func (r *FooResolver) ID() graphql.ID {
	return graphql.ID(r.id.String())
}

func (r *FooResolver) Bar() (string, error) {
	// do some 403
	if !mayViewBar(r.viewerID, id) {
		return "", errors.New("403")
	}

	var bar string
	_ := r.db.QueryRow("SELECT bar FROM foo_bar WHERE id=?", r.id.Bytes()).Scan(&bar)
	return bar, nil
}

func (r *FooResolver) Baz) (*string, error) {
	// do some 403
	if !mayViewBaz(r.viewerID, id) {
		return "", errors.New("403")
	}

	var baz string
	_ := r.db.QueryRow("SELECT baz FROM foo_baz WHERE id=?", r.id.Bytes()).Scan(&baz)
	return &baz, nil
}

Notes:

  • It looks like type-asserting the viewer from context is the "best" available way right now
  • If your type in your graphql schema is nullable, then your resolver must return a pointer, with a nil pointer resolving to null
  • The above is a fairly naive implementation. Still want to add caching, for one. I've exhausted my database's connections quite quickly nesting 5 layers in. I'm really interested in some type of middleware layer that adds onto dataloader by also parsing the SQL queries and combining columns. Hopefully, some of that goes into Vitess' query optimizer too
  • One practice I think is good that I go partway towards is to handle your 401 before enterring graphql and putting your 403 logic in your custom type's constructor (not the resolver, but your domain types). Make your struct's fields unexported and have only one exported constructor. Now, on your exported methods on your custom type, handle 403 within that so that anytime you have data, you know that it's already been 403'd. That would mean moving the SELECT queries out of Bar() and Baz(), and moving them into the methods on that custom struct.

from graphql-go.

seeruk avatar seeruk commented on August 23, 2024 12

One other thing I've noticed while poking around the code is that there aren't many comments in general. It'd be really useful in general to at least have godoc comments. I'd like to be able to contribute to the code if possible, but it makes it that much more difficult to start without any docs or comments.

from graphql-go.

tonyghita avatar tonyghita commented on August 23, 2024 3

I've been working on a closer-to-real-life example that you may find helpful: https://github.com/tonyghita/graphql-go-example

It just wraps the API at https://swapi.co and implements some patterns I've found useful in my year of experience working with GraphQL+Go.

I haven't had time to go through and document all the concepts, but hopefully it helps someone out as it is.

from graphql-go.

tsauvajon avatar tsauvajon commented on August 23, 2024 2

@tonyghita well that's the repo I linked in my previous message!
Thanks it indeed helped a lot, I greatly inspired from your work!

from graphql-go.

SCKelemen avatar SCKelemen commented on August 23, 2024 1

I just need to see how mutations work. How to expose the mutation over the graphql api and then how to make a function, like with anonymous functions/lambda expressions, or calling a method.

from graphql-go.

tendolukwago avatar tendolukwago commented on August 23, 2024 1

@abradley2 i think you're getting that error because fields in your schema (files with the .graphql extension) aren't required or are left optional (Field! is required, Field is optional).

@SCKelemen interesting...haven't had a chance to check it out yet, but this ended up working for me (via POST request body):

{ "query": "mutation{create(name:\"Name\"){name, created}}"}

from graphql-go.

tonyghita avatar tonyghita commented on August 23, 2024

Can you be a little more specific on what is confusing to you about the example given? I could take a shot at writing an example based off of an approach I've taken, but it would help to know which points are most unclear.

from graphql-go.

tsauvajon avatar tsauvajon commented on August 23, 2024

I'd be happy to contribute to a documentation, especially if there were clear governance and guidelines !

A tutorial would also be helpful, as obviously the hard part is getting started, even though the starwars example aswell as this repo help a lot.

from graphql-go.

tendolukwago avatar tendolukwago commented on August 23, 2024

@SCKelemen Have you found out how mutations work? Any idea what that query looks like?

from graphql-go.

abradley2 avatar abradley2 commented on August 23, 2024

I've been looking at all the examples trying to implement whatever I can but I'm getting errors saying all fields in args need to be pointers. Has this changed?

from graphql-go.

SCKelemen avatar SCKelemen commented on August 23, 2024

@tendolukwago I actually haven't. I haven't been working with GraphQL lately. I've mostly switched to gRPC. I've had less issues, and the documentation is better. (It's slightly different, though) I may be revisiting this in a few months, depending on how my priorities are set, by my team.

from graphql-go.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.