How to properly implement GraphQL resolvers

96 views Asked by At

I am creating a basic Graphql project. In this project I am only fetching user details. The data is stored in database. Here is the GQL schema:

type User {
  id: ID!
  username: String!
  email: String!
  age: Int
  address : String
  firstname: String
  lastname :String
}

type Query {
  getUserById(id: ID!): User
}

When client queries my endpoint there will be 2 connection involved.

  • Connection 1: From/to user to my resolver.

  • Connection 2 : From/to my resolver to my database.

Graphql will help me save bandwidth of Connection 1 if I am not requesting some fields.

But, in Connection 2 same amount data will be requested and fetched every time because the same sql query is running for ever request.

For example,

This query

query {
  getUserById(id: "your_user_id_here") {
    id
    username
    email
    age
  }
}

And this query

query {
  getUserById(id: "your_user_id_here") {
    id
    username
    email
    age
    address
    firstname
    lastname
  }
}

Both trigger the same resolver function in my backend which will run the same sql query( select id, email, username.....) for both the cases. So am I not saving any bandwidth in Connection 2 when data requested is less. Right now my schema is small but as it will increase this becomes a much bigger problem.

The underlying libraries of my backend code will ensure only requested fields will be returned to the user. I ams using gqlgen and researched on TypeGraphQL. Both seems work the same in this regards.

My question is: am I missing some graphql concept or is this really a problem? If yes, any suggestions on how to improve my resolvers.

I researched but was not able to find any stuff.

2

There are 2 answers

4
Michel Floyd On

The important bandwidth to conserve is in connection 1 as you've described it. Your GraphQL server should be near your database and connected via a high speed connection. This is typical for a 3-tier design.

As your schema gets more complex you'll end up with computed fields and related types which will get pruned (not executed) from the query plan if the client doesn't request those fields.

For example let's say that instead of storing age directly in the db (invariably a bad idea) you stored birth date instead (or at least birth year). Then age would be a computed field with its own resolver. If the client did not request age that resolver would not run.

0
Cesarvspr On

Your question is really good.

I don't know what framework you are using. But in Go + gqlgen, we can avoid the select * sometimes. Sometimes is not worth implementing a resolver for each column.

In node, I used to use Nexus and Prisma. It's a big problem because the framework always fetches the whole table sometimes.

Probably it's fine to always select multiple columns for most cases. If this becomes a problem you should have a workaround available for your preferred framework using custom fields or a resolver for each column.