Graphql-engine: Current user subscription

Created on 14 Mar 2020  路  6Comments  路  Source: hasura/graphql-engine

We have these tables:

users (id, user_name)
user_stats (user_id, rating, matches_count, etc.)

We want to make a subscription to the current user (with some relations, like user_stats) with this query:

SELECT *
FROM users
WHERE id = X-Hasura-User-Id

So, we have two ways to do that:

1. Make a custom function, using X-Hasura-User-Id session variable.

Pros:

  • We get right typename (users)
  • We don't have to recreate all permissions for all roles
  • We don't have to recreate all relations for other tables

Cons:

  • This way works only for queries. In subscriptions, this way doesn't support batching/multiplexing

So we can't use this way due to performance reasons.

2. Make a view, using custom check ({"id":{"_eq":"X-Hasura-User-Id"}})

Pros:

  • Subscriptions are multiplexed

Cons:

  • We get different typename (users_current instead of users), so we can't reuse cache and the same fragments on our react/apollo frontend.
  • We have to recreate all permissions
  • We have to recreate all relations and maintain them.

What we can do?

question

Most helpful comment

@Maxpain177

Both the solutions you suggested work (with small changes)

Using functions:

Subscriptions on functions which accept session variables as their arguments are multiplexed. The function should look something like this:

create function get_current_user(hasura_session json) returns setof users as $$
  select * from users where id = (hasura_session ->> 'x-hasura-user-id')::Int
$$ language sql stable;

Console doesn't yet support tracking functions which require session variables so you'll need to manually hit /v1/query endpoint with this query:

{
  "type": "track_function",
  "version": 2,
  "args": {
    "function": {
      "schema": "public",
      "name": "get_current_user"
    },
    "configuration": {
      "session_argument": "hasura_session"
    }
  }
}

or it can also be made part of metadata as follows:

{
  "version": 2,
  "tables": [
    {
      "table": {
        "schema": "public",
        "name": "users"
      }
    }
  ],
  "functions": [
    {
      "function": {
        "schema": "public",
        "name": "get_current_user"
      },
      "configuration": {
        "session_argument": "hasura_session"
      }
    }
  ]
}

Using views

To avoid the cons that you listed, you just need to create an object relationship to the users table. So this will be the workflow:

  1. create a view called current_user as follows:
    ```sql
    create view current_user as select * from users
    ````
  2. Create permission on current_user for user roles as {"user_id": {"_eq": "x-hasura-user-id"}}

  3. Create a manual object relationship from current_user view to user table so that you can access all the relationships defined on the user table. Your subscription would look something like this:
    ```graphql
    subscription current_user {
    current_user {
    user {
    id
    stats {

     }
    

    }
    }
    }

You can use any of the above approaches.

All 6 comments

@Maxpain177 Any reason you aren't setting the {"id":{"_eq":"X-Hasura-User-Id"}} check as a permission rule and then using subscriptions on the users table directly?

@0x777 any insights on the subscriptions bit?

@rikinsk Because we want to get not only current user from users table

Can you get the user id from your auth server, and query users using that and 2 different subscriptions / queries?

subscription currentUser {
  currentUser:user(where: {id: {_eq: 1}}) {
    id
    email
  }

}

subscription otherUsers {
    otherUsers:user(where: {id: {_neq: 1}}) {
    id
    email
  }
}

I agree with @kelly-ry4n: why not just look up the current user鈥檚 id and create a subscription on users filtered by that id?

@Maxpain177

Both the solutions you suggested work (with small changes)

Using functions:

Subscriptions on functions which accept session variables as their arguments are multiplexed. The function should look something like this:

create function get_current_user(hasura_session json) returns setof users as $$
  select * from users where id = (hasura_session ->> 'x-hasura-user-id')::Int
$$ language sql stable;

Console doesn't yet support tracking functions which require session variables so you'll need to manually hit /v1/query endpoint with this query:

{
  "type": "track_function",
  "version": 2,
  "args": {
    "function": {
      "schema": "public",
      "name": "get_current_user"
    },
    "configuration": {
      "session_argument": "hasura_session"
    }
  }
}

or it can also be made part of metadata as follows:

{
  "version": 2,
  "tables": [
    {
      "table": {
        "schema": "public",
        "name": "users"
      }
    }
  ],
  "functions": [
    {
      "function": {
        "schema": "public",
        "name": "get_current_user"
      },
      "configuration": {
        "session_argument": "hasura_session"
      }
    }
  ]
}

Using views

To avoid the cons that you listed, you just need to create an object relationship to the users table. So this will be the workflow:

  1. create a view called current_user as follows:
    ```sql
    create view current_user as select * from users
    ````
  2. Create permission on current_user for user roles as {"user_id": {"_eq": "x-hasura-user-id"}}

  3. Create a manual object relationship from current_user view to user table so that you can access all the relationships defined on the user table. Your subscription would look something like this:
    ```graphql
    subscription current_user {
    current_user {
    user {
    id
    stats {

     }
    

    }
    }
    }

You can use any of the above approaches.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Fortidude picture Fortidude  路  3Comments

egislook picture egislook  路  3Comments

leoalves picture leoalves  路  3Comments

tirumaraiselvan picture tirumaraiselvan  路  3Comments

macalinao picture macalinao  路  3Comments