This is a part of the Tutorial to build a blog platform with Elixir Phoenix and Next.js that help you to develop a web application from scratch using modern technologies such as Elixir Phoenix, Next.js, and more.
In the previous part, we enabled users to view their own posts in the My Page screen. Now, we will add the functionality to update and delete posts by implementing the APIs. To do this, we need to first fetch the posts that belong to the current user. Let’s start by updating lib/lani_blog/blog.ex
def get_post(%LaniBlog.Users.User{id: user_id}, id) do
q = from p in Post, where: p.user_id == ^user_id, preload: [:user]
case Repo.get(q, id) do
nil -> {:error, :not_found}
post -> {:ok, post}
end
end
Next, we add the update and delete functions to lib/lani_blog/blog.ex
.
def update_post(%Post{} = post, attrs) do
post
|> Post.changeset(attrs)
|> Repo.update()
end
def delete_post(%Post{} = post) do
Repo.delete(post)
end
Then, we use these functions in the MyPostController
. We create update and delete actions in lib/lani_blog_web/controllers/my_post_controller.ex
.
def update(conn, %{"id" => id, "data" => post_params}) do
with {:ok, post} <-
conn
|> Pow.Plug.current_user()
|> Blog.get_post(id),
{:ok, updated_post} <- Blog.update_post(post, post_params) do
conn
|> put_view(LaniBlogWeb.PostView)
|> render("show.json", post: updated_post)
end
end
def delete(conn, %{"id" => id}) do
with {:ok, post} <-
conn
|> Pow.Plug.current_user()
|> Blog.get_post(id),
{:ok, %Blog.Post{}} <- Blog.delete_post(post) do
send_resp(conn, :no_content, "")
end
end
We put these actions in MyPostController
instead of PostController
because they need user authentication.
For the update
action, we need the post id
and the new post data
. We fetch the post detail of the current user by the id
. We update this post with the new data from params. This way, the current user can only update their own posts. We do the same for the delete action.
Finally, we update the lib/lani_blog_web/router.ex
.
scope "/api", LaniBlogWeb do
pipe_through [:api, :api_protected]
resources "/my-posts", MyPostController, only: [:index, :create, :update, :delete]
end
Let’s test our update and delete functions:
First, we log in with this command:
curl --location --request POST 'http://localhost:4000/api/session' \
--header 'Content-Type: application/json' \
--data-raw '{
"user": {
"email": "john.doe@test.com",
"password": "letmein@123"
}
}'
We should get a successful response with the access_token
, expired_at
, renewal_token
, and user
information.
{
"data": {
"user": {
"email": "john.doe@test.com"
},
"access_token": "SFMyNTY.N2MyNjI1N2QtZjg0ZC00NzQ3LWJmNzktMjIxMjcwYjk3ZWM1.m5vDcxaC6Oz7l-HvbvyXxx161PlQA72_pHoD1ptGhyc",
"renewal_token": "SFMyNTY.ZjU4ODk0ZjAtMTE4Yi00YmZhLWI1MGUtNDhjYzc2Y2QwMDVj.YGykLnrNS9MAoKx_O_-umxWkdgpKDFNEGItJnFlKQCE",
"expired_at": "2024-02-18T04:44:53.673072Z"
}
}
Then, we use the access_token
to get our own posts with this command:
curl --location --request GET 'http://localhost:4000/api/my-posts' \
--header 'Authorization: SFMyNTY.ODljYWU1ZmMtNWY2Ny00NWJmLTkwNmUtZDk0NmE0MGEwZTg3.NqB-PQkWl0_UHpLr9VcV4DwEPS47y5OZ7M90kKXdb3g'
We should get a list of posts with their content
, description
, id
, and title
.
{
"data": [
{
"id": 5,
"description": "Ultrices sagittis orci a scelerisque purus semper eget",
"title": "Parturient montes nascetur ridiculus mus mauris vitae ultricies leo integer",
"content": "Luctus venenatis lectus magna fringilla urna. Metus vulputate eu scelerisque felis imperdiet proin fermentum..\n\n- Pharetra magna ac placerat vestibulum.\n- Convallis aenean et tortor at. Scelerisque purus semper eget duis at tellus at.\n- Mauris vitae ultricies leo integer malesuada nunc vel risus. Tristique nulla aliquet enim tortor at auctor urna.\n\n Phasellus faucibus scelerisque eleifend donec pretium vulputate. Varius duis at consectetur lorem donec massa sapien. Tellus molestie nunc non blandit massa enim.",
"created_at": "2023-11-13T10:18:58"
},
{
"id": 4,
"description": "Amet consectetur adipiscing elit duis tristique sollicitudin nibh sit",
"title": "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua",
"content": "Adipiscing enim eu turpis **pretium** aenean pharetra magna ac. Ornare suspendisse sed nisi lacus sed. Ultrices sagittis orci a scelerisque purus semper eget duis.\n\n- Orci sagittis eu volutpat odio facilisis. Ut tristique et egestas quis ipsum suspendisse ultrices gravida.\n- Nisl nisi scelerisque eu ultrices vitae auctor eu augue ut.\n- Consectetur a erat nam at lectus urna duis convallis.\n\n Tincidunt lobortis feugiat vivamus at augue eget arcu. Eleifend donec pretium vulputate sapien nec sagittis aliquam malesuada.",
"created_at": "2023-11-13T10:09:23"
}
]
}
Now, we try to update the first post in the list with id 5
with this command:
curl --location --request PUT 'http://localhost:4000/api/my-posts/5' \
--header 'Authorization: SFMyNTY.ODljYWU1ZmMtNWY2Ny00NWJmLTkwNmUtZDk0NmE0MGEwZTg3.NqB-PQkWl0_UHpLr9VcV4DwEPS47y5OZ7M90kKXdb3g' \
--header 'Content-Type: application/json' \
--data-raw '{
"data": {
"title": "Sed ullamcorper morbi tincidunt ornare massa eget egestas.",
"description": "Nec feugiat in fermentum posuere. Tellus molestie nunc non blandit massa enim nec. Sit amet consectetur adipiscing elit.",
"content": "Mollis aliquam ut porttitor leo a diam. Imperdiet massa tincidunt nunc pulvinar sapien et ligula **ullamcorper**. Est ullamcorper eget nulla facilisi etiam dignissim diam quis. Leo a diam sollicitudin tempor. Enim nec dui nunc mattis enim ut tellus..\n\n- Orci sagittis eu volutpat odio facilisis. Ut tristique et egestas quis ipsum suspendisse ultrices gravida.\n- Nisl nisi scelerisque eu ultrices vitae auctor eu augue ut.\n- Consectetur a erat nam at lectus urna duis convallis.\n\n Tincidunt lobortis feugiat vivamus at augue eget arcu. Eleifend donec pretium vulputate sapien nec sagittis aliquam malesuada."
}
}'
We should get a successful response with the updated post information.
{
"data": {
"id": 5,
"description": "Nec feugiat in fermentum posuere. Tellus molestie nunc non blandit massa enim nec. Sit amet consectetur adipiscing elit.",
"title": "Sed ullamcorper morbi tincidunt ornare massa eget egestas.",
"content": "Mollis aliquam ut porttitor leo a diam. Imperdiet massa tincidunt nunc pulvinar sapien et ligula **ullamcorper**. Est ullamcorper eget nulla facilisi etiam dignissim diam quis. Leo a diam sollicitudin tempor. Enim nec dui nunc mattis enim ut tellus..\n\n- Orci sagittis eu volutpat odio facilisis. Ut tristique et egestas quis ipsum suspendisse ultrices gravida.\n- Nisl nisi scelerisque eu ultrices vitae auctor eu augue ut.\n- Consectetur a erat nam at lectus urna duis convallis.\n\n Tincidunt lobortis feugiat vivamus at augue eget arcu. Eleifend donec pretium vulputate sapien nec sagittis aliquam malesuada.",
"created_at": "2023-11-13T10:18:58"
}
}
Now, we can get our post list again to verify the update with this command:
curl --location --request GET 'http://localhost:4000/api/my-posts' \
--header 'Authorization: SFMyNTY.ODljYWU1ZmMtNWY2Ny00NWJmLTkwNmUtZDk0NmE0MGEwZTg3.NqB-PQkWl0_UHpLr9VcV4DwEPS47y5OZ7M90kKXdb3g'
We should see that the first post with id 5 has been updated.
{
"data": [
{
"id": 5,
"description": "Nec feugiat in fermentum posuere. Tellus molestie nunc non blandit massa enim nec. Sit amet consectetur adipiscing elit.",
"title": "Sed ullamcorper morbi tincidunt ornare massa eget egestas.",
"content": "Mollis aliquam ut porttitor leo a diam. Imperdiet massa tincidunt nunc pulvinar sapien et ligula **ullamcorper**. Est ullamcorper eget nulla facilisi etiam dignissim diam quis. Leo a diam sollicitudin tempor. Enim nec dui nunc mattis enim ut tellus..\n\n- Orci sagittis eu volutpat odio facilisis. Ut tristique et egestas quis ipsum suspendisse ultrices gravida.\n- Nisl nisi scelerisque eu ultrices vitae auctor eu augue ut.\n- Consectetur a erat nam at lectus urna duis convallis.\n\n Tincidunt lobortis feugiat vivamus at augue eget arcu. Eleifend donec pretium vulputate sapien nec sagittis aliquam malesuada.",
"created_at": "2023-11-13T10:18:58"
},
{
"id": 4,
"description": "Amet consectetur adipiscing elit duis tristique sollicitudin nibh sit",
"title": "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua",
"content": "Adipiscing enim eu turpis **pretium** aenean pharetra magna ac. Ornare suspendisse sed nisi lacus sed. Ultrices sagittis orci a scelerisque purus semper eget duis.\n\n- Orci sagittis eu volutpat odio facilisis. Ut tristique et egestas quis ipsum suspendisse ultrices gravida.\n- Nisl nisi scelerisque eu ultrices vitae auctor eu augue ut.\n- Consectetur a erat nam at lectus urna duis convallis.\n\n Tincidunt lobortis feugiat vivamus at augue eget arcu. Eleifend donec pretium vulputate sapien nec sagittis aliquam malesuada.",
"created_at": "2023-11-13T10:09:23"
}
]
}
Now, we test the delete function for this post with this command:
curl --location --request DELETE 'http://localhost:4000/api/my-posts/5' \
--header 'Authorization: SFMyNTY.ODljYWU1ZmMtNWY2Ny00NWJmLTkwNmUtZDk0NmE0MGEwZTg3.NqB-PQkWl0_UHpLr9VcV4DwEPS47y5OZ7M90kKXdb3g'
We should get a response 204 — No Content
that indicates that our request was successful.
Now, we get our post list again with this command:
curl --location --request GET 'http://localhost:4000/api/my-posts' \
--header 'Authorization: SFMyNTY.ODljYWU1ZmMtNWY2Ny00NWJmLTkwNmUtZDk0NmE0MGEwZTg3.NqB-PQkWl0_UHpLr9VcV4DwEPS47y5OZ7M90kKXdb3g'
We should see that our post with id 12 has been deleted from the list.
{
"data": [
{
"id": 4,
"description": "Amet consectetur adipiscing elit duis tristique sollicitudin nibh sit",
"title": "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua",
"content": "Adipiscing enim eu turpis **pretium** aenean pharetra magna ac. Ornare suspendisse sed nisi lacus sed. Ultrices sagittis orci a scelerisque purus semper eget duis.\n\n- Orci sagittis eu volutpat odio facilisis. Ut tristique et egestas quis ipsum suspendisse ultrices gravida.\n- Nisl nisi scelerisque eu ultrices vitae auctor eu augue ut.\n- Consectetur a erat nam at lectus urna duis convallis.\n\n Tincidunt lobortis feugiat vivamus at augue eget arcu. Eleifend donec pretium vulputate sapien nec sagittis aliquam malesuada.",
"created_at": "2023-11-13T10:09:23"
}
]
}
Cool! We have done the update and delete API implementation. The next step is to implement the front-end that will utilize these APIs in the next part.