Given I have a nested resource with articles having many comments, how do I create an API endpoint for posting comments.
I want my API for creating comments at POST /articles/:id/comments
. How do I create this nested API structure?
My resource for Articles and Comments are shown below:
Article resource:
defmodule Account.Blog.Article do
use Ash.Resource,
domain: Account.Blog,
data_layer: AshPostgres.DataLayer,
extensions: [AshJsonApi.Resource]
alias Account.Blog.Comment
postgres do
table "articles"
repo Account.Repo
end
json_api do
type "article"
end
actions do
defaults [:read]
create :article do
accept [:name]
end
end
attributes do
uuid_primary_key :id
attribute :name, :string
end
relationships do
has_many :comments, Comment
end
end
Comments resource
defmodule Account.Blog.Comment do
use Ash.Resource,
domain: Account.Blog,
data_layer: AshPostgres.DataLayer,
extensions: [AshJsonApi.Resource]
alias Account.Blog.Article
postgres do
table "comments"
repo Account.Repo
end
json_api do
type "comment"
end
actions do
defaults [:read]
create :comment do
accept [:comment, :article_id]
end
end
attributes do
uuid_primary_key :id
attribute :comment, :string
end
relationships do
belongs_to :article, Article
end
end
Domain:
defmodule Account.Blog do
use Ash.Domain,
extensions: [AshJsonApi.Domain]
alias Account.Blog.{Article, Comment}
resources do
resource Article
resource Comment
end
json_api do
routes do
base_route "/articles", Article do
get(:read)
post(:article)
end
base_route "/comments", Comment do
get(:read)
post(:comment)
end
end
end
end
I am currently giving the request at POST /comments
with the article_id to mention which article the comments belongs to.
Example:
{
"data": {
"type": "comment",
"attributes": {
"comment": "Good",
"article_id": "article_id"
}
}
}
You should be able to do that by including an article_id
in the route params for your post
.
# include at top level
post Post, :create, route: "/articles/:article_id/comments"
Your post pointed out to me that being able to nest base_routes
could clean this up greatly, so I’m pushing a release w/ that ability soon. Then it would look like this:
defmodule Account.Blog do
use Ash.Domain,
extensions: [AshJsonApi.Domain]
alias Account.Blog.{Article, Comment}
resources do
resource Article
resource Comment
end
json_api do
routes do
base_route "/articles", Article do
get(:read)
post(:article)
base_route "/:article_id", Comment do
post :comment # would call the `:comment` create action on comment
end
end
base_route "/comments", Comment do
get(:read)
post(:comment)
end
end
end
end
2 Likes
wow @zachdaniel that’s super fast! You comment here is 18hours ago and I see this commit implementing this feature17hrs ago! improvement: support nested `base_route`s · ash-project/ash_json_api@bf77d4d · GitHub
This is something I was also looking for as well. However, I would like to understand the difference between using related
or post_to_relationship
macro inside the routes
vs what you have proposed as a new solution here:
For eg., can the following do the same as what you have proposed as nested base_route
for creating comment?
json_api do
routes do
base_route "/articles", Article do
post_to_relationship :comments
end
end
1 Like
@zachdaniel Thank you so much for the quick reply . Updated to the newer version and applied the solution.
related
is a route for reading the records of a relationship, i.e
/posts/:id/comments
It can be thought of as an index route that only returns the related records.
post_to_relationship
is for “linking” a record or records to another via a relationship. It accepts a very specific input (something called “linkage”), and is typically only used to connect existing things together.
You can see more here: JSON:API — Latest Specification (v1.1)
2 Likes
Just to clarify, please check if my understanding is correct:
1. related
option on routes
Use this only for GET
requests for related records. Doesn’t work for any other methods.
If Article
resource has many Tag
relationship, this related
option is used to fetch only the associated tags of a specific article.
In this case, the Ash action
for the api is placed in the Article
resource.
2. post_to_relationship
option on routes
Use this option for any other methods other than GET
for related records. The related record must already exist. In the case of Article
and Tag
relationships, both the records must exist already. post_to_relationship
is used to create the linkage in ArticleTags
resource.
The same applies for patch_relationship
, delete_from_relationship
to update or delete the link ArticleTags
resource record.
In this case, the Ash action
for the api is placed in the Article
resource.
3. Nested base_route
as explained in this post by @zachdaniel
Use this option to do any of the above actions with the main differences as below:
- Ash
action
for the nested route is present in the nested resource rather than in the parent resource as in the above options. i.e., GET /articles/:id/tags
will be responded by an action in the Tag
resource.
- In the nested resource, one has to take care of creating/updating/deleting all the links manually.
Is my understanding correct?
1 Like