mmport80

mmport80

The Absinthe book is thought provoking,

and has helped me navigate the library.

I was wondering about a few things.

Putting Authorisation in Schema middleware is on the one hand convenient, e.g.

#schema
field :user_profiles, list_of :user_profile do
      middleware Schema.Middleware.Authorise, :some_permission
      (&UserProfileResolver.all/2) |> Utils.handle_errors |> resolve
    end

on the other repetitive (because I have have so many useful ‘edges’ to protect as well as ‘nodes’), e.g.

# types
object :user do
    field :id, non_null :positive_int
    ...
    field :user_profile, :user_profile do
      resolve &UserResolver.user_profile_association/3
    end
    ...
  end

Taking FB’s advice I will push my authorisation into business logic and away from the schema, where I have one source of truth, which seems sensible.

Perhaps it would be good to emphasise this trade-off more in the book?

The book does in fact show a way of pushing authentication context into the business logic & resolvers, e.g.

# schema
field :user_profiles, list_of :user_profile do
      (fn a, b, %{context: context} -> UserProfileResolver.all a, b, context end) 
      |> Utils.handle_errors 
      |> resolve
end

It seems like a good, and potentially popular way to implement things.

Is there a neater way of doing this? I suspect there is, but I cannot figure it out.

It seems v verbose, but perhaps that’s OK.

Here I found a nice way of treating partially errored results, which is really smart, especially when it comes to authorisation.

Is there anywhere I can find some good examples for this?

I suspect it’s the nature of the GraphQL beast that many edges / fields need such a Union type. Perhaps providing something like a ‘maybe’ or ‘option’ type out of the box from the library would be helpful…

Showing Posts 1 to 8

idi527

idi527

I move all of authorisation into what’s currently called “contexts” around here. Not sure if it’s any better though.

mmport80

mmport80 OP

Do you do something like this?

type fields ↔ permissions
schema fields ↔ permissions

I think I am better off doing it close to the ecto db queries, and as I said, have one source of truth

idi527

idi527

Do you do something like this?

type fields ↔ permissions
schema fields ↔ permissions

Not sure … Web/graphql layer doesn’t actually know anything about authorization in my case.

All of the protected functions in a context accept additional parameters that help authorize the action.

@spec create_resource(attrs, by: User.t) :: {:ok, Resource.t} | {:error, Ecto.Changeset.t} | no_return
def create_resource(attrs, by: %User{id: user_id} = user) do
  Resource.Policy.authorize!(:create, user)

  %Resource{owner_id: user_id}
  |> Resource.changeset(attrs)
  |> Resource.insert()
end

Same for get, update, …

If the action is not authorized, currently I raise an UnauthorizedError with a plug status set. I can catch this exception and return nil or empty list for absinthe to render partially erred results (haven’t tried it yet though).

mmport80

mmport80 OP

Yep, this looks similar to my thinking (without the exceptions - I try to avoid those whenever poss, but maybe I should revisit after seeing this!)

Also, I will have a union type, so in MLish pseudocode…

Users = (…, MaybeGroups)
MaybeGroups = Groups | UnauthorisedError
Groups = [Group]

The only thing is, the FB spec recommends returning a null or [], as you suggest, and then include a list of errors in the returned JSON…

This way, I suppose the error will be under groups, i.e.

users = {…, groups: {"error", "unauthorised"}}

which isn’t bad per se, just perhaps not idiomatic.

Hmm.

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

I’m a bit curious where you saw the Utils.handle_errors pattern. The book would have been promoting a middleware based approach, which isn’t what is happening here. The approach here will fail if any asynchronous execution is used by the resolver.

mmport80

mmport80 OP

Good pt! I must’ve picked it up somewhere, I will amend it : ) thanks : )

sheharyarn

sheharyarn

Is it possible to completely skip sending a response on trigger as opposed to sending back an error? In our use case, we want to send data over subscriptions for only the posts the user is a member of, and not send anything at all in other cases.

Is there a straight-forward way of doing that?

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Hey @sheharyarn, this sort of thing should be handled by how you organize your topic so that you aren’t trying to run documents that shouldn’t be sent. For example in your use case, when each user sends a subscription in they could do something like:

subscription {
  postsForMe { title body }
}

And this would generate a topic: "#{user.id}"

Then when a forum post is updated or posted you should:

# roughly
user_ids = post |> Ecto.assoc(:members) |> Repo.all |> Enum.map(& &1.user_id)
Absinthe.Subscription.publish(MyApp.Web, post, posts_for_me: user_ids)

Publishing to a topic that has no documents is very cheap, it’s just an :ets lookup. Running a document that you don’t want to send out is much more expensive.

— All posts loaded —

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
water
I’m aware of 2 such projects, are there others? git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
itzmidinesh
Hi all, I’m working on a Phoenix project where I need to make external HTTP requests and I’m trying to choose between Req and HTTPoison....
New
pragdave
How do I tell Hologram not to escape HTML when substituting into templates? {Textile.render(@product.long_description)} escapes the HTM...
New
rlipscombe
I’d like to create a file with 600 permissions (rw-------), but there’s a race condition if I create it and then change the permissions. ...
New
leoschweizer
Hi, we run different LLM models across a cluster, with each node loading a configurable subset. Model-specific queues let nodes consume ...
New
rlipscombe
When I use Mix.install with a git dependency, using git@github.com:... how do I change the SSH identity used? I’ve got two Github accoun...
New

Other Trending Topics Top

dominicletz
Hey there, I haven’t seen a list of useful Elixir specific skills for the popular AI tools yet so I’m starting one here with my first. ...
New
raa-ziq
Hi everyone :waving_hand: I wanted to share something I’ve been building — NowBlind, a random chat app designed to help people discover ...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
OndrejValenta
(I just needed to vent somewhere and LinkedIn is full of hope, or hype, I’m not sure which exactly) AI dream has many faces, but general...
New
kip
Localize is the next generation localisation library for Elixir. Think of it as ex_cldr version 3.0. The first version will be released ...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews