Run app
cd
into the04_simple_json_api/
dir:cd 04_simple_json_api/
Fire up R:
R
Restore package dependencies:
::restore() renv
Once done, exit R.
server.R
is the entry point. To start the app, run this on the terminal:Rscript index.R
Explanation
This app starts a server and listens on port 3000 for connections.
It has two endpoints:
/api/members
/api/members/:id
Ideally, the backend works hand-in-hand with the database. But for the sake of simplicity, let’s hardcode a list of members:
<- data.frame(
members id = as.character(1:3),
name = c("John Doe", "Bob Williams", "Shannon Jackson"),
email = c("john@gmail.com", "bob@gmail.com", "shannon@gmail.com"),
status = c("active", "inactive", "active")
)
You don’t have to worry about converting members
into JSON because the res$json()
method does it for you.
Create a GET request to get all members:
$get("/api/members", \(req, res) {
app$json(members)
res })
Create a GET request to get a single member by id:
$get("/api/members/:id", \(req, res) {
app# get the supplied id:
<- req$params$id
member_id
# filter member with that id:
<- members |> dplyr::filter(id == member_id)
found
$json(found)
res })
You can also dictate how the response changes if no member is found:
# get a single member:
$get("/api/members/:id", \(req, res) {
app# get the supplied id:
<- req$params$id
member_id
# filter member with that id:
<- members |> dplyr::filter(id == member_id)
found
# if a member with that id was found, return the member:
if (nrow(found) > 0) {
return(res$json(found))
}
# otherwise, change response status to 400 (Bad Request)
# and provide a message:
<- list(msg = sprintf("No member with the id of %s", member_id))
msg $set_status(400L)$json(msg)
res })
A POST request to create a new member:
$post("/api/members", \(req, res) {
app# parse form-data:
<- parse_multipart(req)
body
# details of the new member:
<- data.frame(
new_member id = uuid::UUIDgenerate(),
name = body$name,
email = body$email,
status = body$status
)
# save new member:
<<- dplyr::bind_rows(members, new_member)
members
# respond with a message and details of the newly created member:
<- list(
response msg = "Member created successfully!",
member = new_member
)
$json(response)
res })
You can of course ensure all the details are sent (name, email & status) before creating the new member:
$post("/api/members", \(req, res) {
app# parse form-data:
<- parse_multipart(req)
body
<- body$name
name <- body$email
email <- body$status
status
# require all member details:
if (is.null(name) || is.null(email) || is.null(status)) {
<- list(msg = "Please include a name, email & status")
msg return(res$set_status(400L)$json(msg))
}
# details of the new member:
<- data.frame(
new_member id = uuid::UUIDgenerate(),
name = name,
email = email,
status = status
)
# save new member:
<<- dplyr::bind_rows(members, new_member)
members
# respond with a message and details of the newly created member:
<- list(
response msg = "Member created successfully!",
member = new_member
)
$json(response)
res })
Create a PUT request to update a member:
$put("/api/members/:id", \(req, res) {
app# get the supplied id:
<- req$params$id
member_id
# filter member with that id:
<- members |> dplyr::filter(id == member_id)
found
# if a member with that id is NOT found, change response status
# and provide a message:
if (nrow(found) == 0) {
<- list(msg = sprintf("No member with the id of %s", member_id))
msg return(res$set_status(400L)$json(msg))
}
# otherwise, proceed to update member:
<- parse_multipart(req)
body
# only update provided fields:
$name <- body$name %||% found$name
found$email <- body$email %||% found$email
found$status <- body$status %||% found$status
found
$id == found$id, ] <- found
members[members
<- list(
response msg = "Member updated successfully",
member = found
)$json(response)
res })
Create a delete request to, well, delete a member:
$delete("/api/members/:id", \(req, res) {
app# get the supplied id:
<- req$params$id
member_id
# filter member with that id:
<- members |> dplyr::filter(id == member_id)
found
# if a member with that id is NOT found, change response status
# and provide a message:
if (nrow(found) == 0) {
<- list(msg = sprintf("No member with the id of %s", member_id))
msg return(res$set_status(400L)$json(msg))
}
# otherwise, proceed to delete member:
<<- members |> dplyr::filter(id != member_id)
members
<- list(
response msg = "Member deleted successfully",
members = members
)$json(response)
res })
Router
Things are getting out of hand in server.R
. Take a look at ✨routers✨.