What is the proper way to define fields that may not be accessible to all users.
For example, a general user can query the users and find out another users handle, but only admin users can find out their email address. The user type defines it as a field but it may not be accessible. Should there be a separate type for what a general user can see? How would you define it?
Sorry if that isn't that clear I just don't possess the vocabulary.
Edit: Caution: Graphql documentation disagrees with this approach. Use with caution. Wherever you need a private field you must include the appropriate middlewares.
Use absinthe middleware.
Here is some code how to do it. In this example the authenticated user can see the email addresses. The anonymous user can't. You can adjust the logic to require whatever permissions you want.
defmodule MySite.Middleware.RequireAuthenticated do
@behaviour Absinthe.Middleware
@moduledoc """
Middleware to require authenticated user
"""
def call(resolution, config) do
case resolution.context do
%{current_user: _} ->
resolution
_ ->
Absinthe.Resolution.put_result(resolution, {:error, "unauthenticated"})
end
end
end
and then you define your object:
object :user do
field :id, :id
field :username, :string
field :email, :string do
middleware MySite.Middleware.RequireAuthenticated
middleware Absinthe.Middleware.MapGet, :email
end
end
So our field email is protected by the RequireAuthenticated middleware. But according to the link above
One use of middleware/3 is setting the default middleware on a field, replacing the default_resolver macro.
This happens also by using the middleware/2 macro on the field. This is why we need to also add
middleware Absinthe.Middleware.MapGet, :email
to the list of middlewares on the field.
Finally when we perform a query
query {
user(id: 1){
username
email
id
}
}
We get the response with the open fields filled and the protected fields nullified
{
"errors": [
{
"message": "In field \"email\": unauthenticated",
"locations": [
{
"line": 4,
"column": 0
}
]
}
],
"data": {
"user": {
"username": "MyAwesomeUsername",
"id": "1",
"email": null
}
}
}
You can also use the middleware/3 callback so your object don't get too verbose
def middleware(middleware, %{identifier: :email} = field, _object) do
[MySite.Middleware.RequireAuthenticated] ++
[{Absinthe.Middleware.MapGet, :email}] ++
middleware
end
With a bit of creative use of the __using__/1 callback you can get a bunch of such functions out of your main schema file.