Skip to content

Instantly share code, notes, and snippets.

@steebchen
Created June 23, 2019 21:31
Show Gist options
  • Save steebchen/4ca43998ebda3c00363f7b4470aaec5c to your computer and use it in GitHub Desktop.
Save steebchen/4ca43998ebda3c00363f7b4470aaec5c to your computer and use it in GitHub Desktop.
Photon Go client: query for multiple things in a single query

Proposal for selecting multiple things as alternative for the initial proposal

Generated Prisma client types

The prisma types could be generated with all relations a type can have. For example, when a User can have Posts and a Post has an Author, the respective types define the relation in the struct.

// generated code by prisma

type User struct {
	Name string
	Posts []Post
}

type Post struct {
	Title string
	Likes []User
}

// ...

Client usage

Now, we can easily query for a user with their posts.

user, err := client.User(
	prisma.user.WithPosts()
)

log.Printf("user: %+v", user) // { Name: John }
// user.Posts is filled:
log.Printf("user posts: %+v", user.Posts) // []{{ Title: First post, Likes: <nil> }}

We could even go deeper:

user, err := client.User(
	prisma.user.WithPosts(
		prisma.post.WithLikes()
	)
)

log.Printf("user: %+v", user) // { Name: John }
// user.Posts is filled with likes:
log.Printf("user posts: %+v", user.Posts) // []{{ Title: First post, Likes: []Likes{{ Name: Luca }} }}

However, this means that you would have to make sure you're not using the fields if you didn't query for them:

user, err := client.User()

log.Printf("user: %+v", user) // { Name: John }
log.Printf("user posts: %+v", user.Posts) // <nil>

// user.posts is now nil because we didn't ask for it, so we are not allowed to access it.
user.Posts[0].Title // ERROR: runtime error: index out of range

Achieving 100% type safety

In theory, it could be possible to generate types for it when chaining function calls, instead of using functional options:

user1, err := client.User().Exec()
// user1 is User{ Name: "John" }

user2, err := client.User().UserWithPosts().PostWithLikes().Exec()
// user2 is User{ Name: "John", Posts: []Post{}{ Title: "First Post", Likes: []User{ Name: "Jane" } } }

// assuming a user has more relations than posts, i.e. posts and emails
user3, err := client.User().UserWithEmailsAndPosts().PostWithLikes().Exec()
// user3 is User{ Name: "John", Emails: []Email, Posts: []Post{}{ Title: "First Post", Likes: []User{ Name: "Jane" } } }

Each of those methods could return another struct to generate all possible chainings.

However, when using more and more relations, this would generate an abnormal amount of structs, growing exponentially with more relations. It's probably possible, but I'm not sure if it's a good idea.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment