How to make CRUD REST API/Service with Yesod Web Framework
the last time that i read something here was just the instalations and uping the server, now we gonna start a "blog" to do our frist CRUD \O/ (FINNALY)
but before we start to code we need to understend something about the framework and his structure, like how the routes is build.
in our routes file (path: "/config/routes") we have some routes there but lets use create our own routes that we will use in the project
/posts PostR POST GET
ok now we have our routes lets undertend how then work:
"/posts" this is the URL of the route, if you want to access "localhost:3000/posts"
"PostR" is the function that we gonna use
"POST GET" this is the HTTP methods that the route gonna suport
lets add the table in the migrations
Post json
name Text
content Text
likes Int
(as in the last article when we run the aplication it will make the migration)
the first steps we gonna need to do its adaptade somethings because we gonna make just the crud without the Auth service, to do that go to the file "src/Foundation" and add in the patterning matching this line
isAuthorized _ _ = return Authorized
like in this image
imagem
because without this line if we try to acces the route unAutorized, the server gonna return 500 patterning matching exausted
and we need to declare that we have a new file in handler, to do that add this line in the file Application.hs (Path: "src/Application.hs")
import Handler.Post
put this under the other files on line 47
now we will create a file "Post.hs" in the Handlers folder (path: "src/Handler/Post.hs")
and paste this code
{-# LANGUAGE OverloadedStrings #-}
module Handler.Post where
import Import
import Database.Persist.Postgresql
now we finnaly can start our Crud (づ ̄ ³ ̄)づ
lets first make a register with the POST method, to do that is very easy but we need to understend how the route work (what I tried to explain some lines ago), just paste the code in Posts.hs and that it
postPostR :: Handler Value
postPostR = do
post <- requireJsonBody :: Handler Post
pid <- runDB $ insert post
sendStatusJSON created201 (object ["id" .= fromSqlKey pid])
ok, i'm kirring lets explay what is happing here line by line
"postPostR :: Handler Value" ok, remember that in the router file we had the function "PostR", to link the function with the method of the route we just need to concat the method in lower case with the function name like that "postPostR" and the "Handler Value" is just what the function return
"postPostR = do" lets use the monnad "Do" (anyone uses bind's come on)
"post <- requireJsonBody :: Handler Post" whats happen here, we are getting the content of the body request, and turn in into a Haskell type
"requireJsonBody :: Handler Post" the Haskell type will have the same fields of the table because we are typing the input with ":: Handler Post" and the function requireJsonBody make the magic for us, getting the json and returning the haskell type and we put it into a """"varible""""
"pid <- runDB $ insert post"
now the GET method
getPostR :: Handler Value
getPostR = do
dt <- runDB $ selectList [] [Asc PostId]
sendStatusJSON ok200 (object ["content" .= dt])
imagem explicando o metodo get e blablabla
lets build the project with the command
stack build
if compile we can run the project
stack exec learn-yesod
open another terminal and lets use the Curl (that we had used in the last tutorial) and make a request to the server
to register a post with the POST method
curl -X POST localhost:3000/posts -d '{"name":"first post", "content":"this is the first post content", "likes":0}'
the response should be 201, you can see the logs in the terminal of the application
now lets get our informations with a GET
curl -X GET localhost:3000/posts
the response should be 200 ok with the content of the table posts
ok, lets finish the crud with the PUT, PATCH and DELETE method, if you understend how to get and insert works will be easy the other methods, there is just some diferences, lets add the routes
/posts/#PostId PostSpecificR PUT DELETE
/posts/#PostId/#Int PostLike PATCH
the first diference is after the route "/#PostId" this is a parameter that you will recive as a paramether in the function to get the specific
putPostSpecificR :: PostId -> Handler Value
putPostSpecificR idPost = do
_ <- runDB $ get404 idPost
newPost <- requireJsonBody :: Handler Post
runDB $ replace idPost newPost
sendStatusJSON noContent204 (object [])
imagem explicando
patchPostLike :: PostId -> Int -> Handler Value patchPostLike idPost likes = do _ <- runDB $ get404 idPost runDB $ update idPost [PostLikes =. likes] sendStatusJSON noContent204 (object [])
imagem explicando
deletePostSpecificR :: PostId -> Handler Value deletePostSpecificR idPost = do _ <- runDB $ get404 idPost runDB $ delete idPost sendStatusJSON noContent204 (object [])
imagem explicando