ash-project / ash_json_api

The JSON:API extension for the Ash Framework
https://hexdocs.pm/ash_json_api
MIT License
55 stars 40 forks source link

When trying to access my resource through JsonApi, I get an unexpected http 404 error. #154

Closed willemodendaal closed 2 months ago

willemodendaal commented 3 months ago

Describe the bug I have a minimal set-up, following the documentation. I have a simple resource, that I would like to "GET" through the json api, but I get a 404 error, even though everything seems ok from my side.

To Reproduce Create a domain:

defmodule MyApi2.FlashCards do
  use Ash.Domain,
    extensions: [AshJsonApi.Domain]

  resources do
    resource MyApi2.FlashCards.Card
  end
end

Create a resource:

defmodule MyApi2.FlashCards.Card do
  use Ash.Resource,
    domain: MyApi2.FlashCards,
    data_layer: AshPostgres.DataLayer,
    extensions: [AshJsonApi.Resource]

  actions do
    defaults [:read]

    create :create do
      accept [:side_a, :side_b]
    end
  end

  attributes do
    uuid_primary_key :id

    attribute :side_a, :string do
      allow_nil? false
    end

    attribute :side_b, :string do
      allow_nil? false
    end
  end

  postgres do
    table "flash_cards"
    repo MyApi2.Repo
  end

  json_api do
    type "card"

    routes do
      base("/flashcards")

      get(:read)
      index :read
      post(:create)
    end
  end
end

Add JsonApiRouter:

defmodule MyApi2Web.JsonApiRouter do
  use AshJsonApi.Router,
    domains: [Module.concat(["MyApi2.FlashCards"])],
    json_schema: "/json_schema",
    open_api: "/open_api"
end

Add json_api configuration in router.ex, with "forward":

scope "/api/json" do
    pipe_through :api

    forward "/flashcards", MyApi2Web.JsonApiRouter
  end

  # Note! I also have this, my non-ash routes, on existing code:
  scope "/api" do
    pipe_through :api

    # Do some basic auth:
    unless Mix.env() in [:dev, :test] do
      pipe_through :with_auth
    end

    resources "/tasks", MyApi2Web.TaskController, except: [:new, :edit]
  end

Configure as per the documentation in config.ex:

config :mime, :types, %{
  "application/vnd.api+json" => ["json"]
}

config :mime, :extensions, %{
  "json" => "application/vnd.api+json"
}

List routes in iex -S mix:

iex(1) > AshJsonApi.Resource.Info.routes(MyApi2.FlashCards.Card)
[
  %AshJsonApi.Resource.Route{
    route: "/flashcards/:id",
    action: :read,
    action_type: :read,
    default_fields: nil,
    method: :get,
    controller: AshJsonApi.Controllers.Get,
    relationship: nil,
    resource: nil,
    type: :get,
    primary?: false,
    upsert?: nil,
    upsert_identity: nil,
    read_action: nil,
    relationship_arguments: []
  },
  %AshJsonApi.Resource.Route{
    route: "/flashcards",
    action: :read,
    action_type: :read,
    default_fields: nil,
    method: :get,
    controller: AshJsonApi.Controllers.Index,
    relationship: nil,
    resource: nil,
    type: :index,
    primary?: false,
    upsert?: nil,
    upsert_identity: nil,
    read_action: nil,
    relationship_arguments: []
  },
  %AshJsonApi.Resource.Route{
    route: "/flashcards",
    action: :create,
    action_type: :create,
    default_fields: nil,
    method: :post,
    controller: AshJsonApi.Controllers.Post,
    relationship: nil,
    resource: nil,
    type: :post,
    primary?: false,
    upsert?: false,
    upsert_identity: false,
    read_action: nil,
    relationship_arguments: []
  }
]

^ looks good to me.

Try calling the "get" with curl localhost:4000/api/json/flashcards and see response:

{"errors":[{"code":"no_route_found","id":"a8d27b3f-a90f-4bb3-8b9e-350384fa7d76","meta":{},"status":"404","title":"NoRouteFound","detail":"no route found"}],"jsonapi":{"version":"1.0"}}%

Is there something obvious I'm doing wrong? When I interact with my resource via iex, it all works fine. So I think my Ash configuration is ok, and that there's something wrong with how I'm setting up the JsonApi bits.

Expected behavior When I call curl localhost:4000/api/json/flashcards, I expect a list of the 'flashcards' I've created already.

** Runtime

willemodendaal commented 3 months ago

Perhaps there's something wrong with my "base" route? Or with my "api/json" route and how that forwards to "/flashcards"? I'm not sure how to configure these exactly.

This bit in the error also seemed strange: "jsonapi":{"version":"1.0"}, since I'm using ash_json_api v1.1. I tried a mix deps.compile mime --force, but that didn't make a difference.

willemodendaal commented 3 months ago

Here's what the iex -S mix output looks like, when the curl happens:

[info] GET /api/json/flashcards
[debug] Processing with MyApi2Web.JsonApiRouter
  Parameters: %{}
  Pipelines: [:api]
[info] Sent 404 in 182µs
willemodendaal commented 3 months ago

I added some debug statements in my local ash_json_api dependency, and can confirm that the code gets here, if that helps: https://github.com/ash-project/ash_json_api/blob/main/lib/ash_json_api/controllers/router.ex#L63

willemodendaal commented 3 months ago

I figured it out! Because my router's forward configuration was set to "/flashcards", and my resource's base was set to "/flashcards", the actual path to the resource ended up being: "/api/json/flashcards/flashcards"

Very obvious in hindsight. I created a PR to clarify in the documentation: https://github.com/ash-project/ash_json_api/pull/155