Lesson Tuesday

At the end of the previous lesson we discussed the Spark routes our RESTful blog application will require. Now that we know what our application needs, let's get to work! In this lesson we'll add CREATE and READ routes to our Epicodus Blog. Along the way we'll also learn about another important acronym in web development: CRUD, and how to use a single route to render multiple, different pages using a concept called dynamic routing. We’ll learn more about CRUD below.

Coding RESTful Routes

Let’s first briefly update our layout.hbs to include links and basic styling. The example in this lesson also includes a coding related image, which you can download here. Or, feel free to choose a different image.

Our code now looks like this:

my-epicodus-blog/src/main/resources/templates/layout.hbs
<!DOCTYPE html>
<html>
<head>
   <title>{{#block "title"}}Blog{{/block}}</title>

   <link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css'>
</head>
<body>
<ul class="nav nav-tabs">
   <li role="presentation" class="active"><a href="#">Home</a></li>
   <li role="presentation"><a href="/posts">See All Posts</a></li>
   <li role="presentation"><a href="/posts/new">Add a New Post</a></li>
</ul>
<div class="container">
   <!--begin main template-->
   <div class="page-header">
       <img src="/images/caspar-rubin-224229.jpg" width="70%">
       <h1>Welcome to my Epicodus Blog. <small>A journey into code!</small></h1>
   </div>

   <div class="col-md-12">
       {{#block "content"}}
       {{/block}}

   </div>
</div>
<!--end main template-->
{{#block "footer"}}
   </body>
   </html>
{{/block}}

Try it out. The links won’t work, though, because we haven’t added corresponding route handlers to our App.java file yet. That’s OK.

Next, let's update App.java with new routes according to REST standards. The example code below includes comments to remind us that we will add more functionality later; specifically, ability to update and delete posts, which is obviously kind of important.

src/main/java/App.java
   public static void main(String[] args) { //type “psvm + tab” to autocreate this
       staticFileLocation("/public");

       //get: show new post form

       //post: process new post form
       post("/posts/new", (request, response) -> { //URL to make new post on POST route
           Map<String, Object> model = new HashMap<>();

           String content = request.queryParams("content");
           Post newPost = new Post(content);
           model.put("post", newPost);
           return new ModelAndView(model, "success.hbs");
       }, new HandlebarsTemplateEngine());

       //get: show all posts
       get("/", (req, res) -> {
           Map<String, Object> model = new HashMap<>();
           ArrayList<Post> posts = Post.getAll();
           model.put("posts", posts);

           return new ModelAndView(model, "index.hbs");
       }, new HandlebarsTemplateEngine());

       //get: show an individual post

       //get: show a form to update a post

       //post: process a form to update a post

       //get: delete an individual post

       //get: delete all posts

   }
}

  • Our get("/") and get("/posts/new") routes simply render templates (the welcome page and the new post form, respectively).

  • In our get("/posts") route, we use our new method Post.all() to place the ArrayList of all Posts into the model, thereby making it available to the template rendered in this route (posts.hbs). In that template we can iterate through each Post and display whatever property we'd like.

  • Our post("/posts/new") route is where the form submits to (because its action and method attributes look like this: <form action="/posts/new" method="post">). This route handles gathering info from the form and creating the Post, which automatically adds it to the list of all Posts.

The ordering of route handlers here is NOT random - it is intentional. And here it is - another important topic and acronym for today you need to memorize: CRUD.

CRUD

The primary functionality any application includes to interact with its objects is often referred to as CRUD. The acronym CRUD stands for Create, Read, Update, Destroy. These four verbs represent the four primary interactions users have with data and objects in any application. This acronym is language agnostic, so you’ll hear people refer to it for Java, PHP, Ruby, JavaScript, Python…If you look at the App.java, you’ll see the route handlers are ordered according to this acronym. This isn’t necessary for CRUD actions to work, but it does help keep code organized.

Updating our Templates

Next, we'll need to update our templates. Let’s make a new hbs file, namely a template that can hold our “add a new post” form. We’ll call it newpost-form.hbs:

my-epicodus-blog/src/main/resources/templates/newpost-form.hbs
{{#partial "content"}}

   <h1>Add a new Post!</h1>

   <form action="/posts/new" method="post">
       <label for="content">Post Content</label>
       <input id="content" name="content" type="text">

       <button type="submit" class="btn btn-default">Add new blog post</button>
   </form>
{{/partial}}

{{> layout.hbs}}

Let’s edit our route in App.java that corresponds with serving this template:

my-epicodus-blog/src/main/java/App.java
public class App {
   public static void main(String[] args) { //type “psvm + tab” to autocreate this
       staticFileLocation("/public");

       //get: show new post form
       get("/posts/new", (req, res) -> {
           Map<String, Object> model = new HashMap<>();
           return new ModelAndView(model, "newpost-form.hbs");
       }, new HandlebarsTemplateEngine());
...

We can run our app, and see that our link in the header to the new post form now works. Great! Let’s keep rolling. Next, let’s edit our loop that shows all of our posts, and add a handy link to a Posts detail page that we’ll write a template and a route handler for next. We'll change our index.hbs to look as follows:

my-epicodus-blog/src/main/resources/templates/index.hbs
{{#partial "content"}}

   <h1>Post list!</h1>

   {{#if posts}}
       {{#each posts }}
        <p>Current post:</p>
        <p>{{ content }} <a href="/posts/{{id}}">read more...</a></p>
       {{/each}}
   {{else}}
    <p>No posts!</p>
   {{/if}}
{{/partial}}

{{> layout.hbs}}

Did you see that clever thing we did there? We actually built a dynamic URL in the <a> tag using the id property of the object! This will create a unique link for each individual post that contains that post's unique id property. When this link is selected, a "/posts/:id" route will execute. Really? Really.

If we run the app, add a post, and click it, we’ll get a 404, but look at the URL bar in the browser:

spark-url-in-browser

Add a couple more posts, click on one, and we’ll see the id in the browser URL change! We can use this to target an individual post, regardless of which one it is. Let’s code the route handler for this, and you’ll see how we are actually able to pluck that id out of the URL and use it for our routing! I don’t know about you, but this is pretty cool.

In our App.java, add:

src/main/java/App.java
...
get("/posts/:id", (req, res) -> {
   Map<String, Object> model = new HashMap<>();
   int idOfPostToFind = Integer.parseInt(req.params("id")); //pull id - must match route segment
   Post foundPost = Post.findById(idOfPostToFind); //use it to find post
   model.put("post", foundPost); //add it to model for template to display
   return new ModelAndView(model, "post-detail.hbs"); //individual post page.
}, new HandlebarsTemplateEngine());
...

Dynamic Routing

OK, slow down. What is going on here? Look at the route above - as you can see, something new is happening. It’s called Dynamic Routing!

  • . The :id portion of this route is a placeholder, because this route will be executed for viewing any Post's detail page. As we discussed, clicking on the first Post in the list will result in a route that looks like "/posts/1". When that occurs, :id will represent 1. If we clicked on the 23rd Post in the list it would create a link that looked like "/posts/23". After the user executed that GET request, :id would represent 23.

  • In the line Post post = Post.find(Integer.parseInt(request.params(":id")));, the request.params(":id") portion retrieves the value currently represented by :id. It retrieves this information from the link we clicked to execute this route. So, if we clicked on the fifth Post in our list, the link would be /posts/5. When this route executed, request.params(":id") would return 5.

  • The term “id” is nothing magical - if the route were written like this:

get("/posts/:dogscatsandponies", (req, res) -> {
   Map<String, Object> model = new HashMap<>();
   int idOfPostToFind = Integer.parseInt(req.params("dogscatsandponies"));
...

It would still work, as long as the named segment in the URL and the parameter we are pulling are the same.

  • Since the value retrieved from :id is a String, we need to use Integer.parseInt() to convert it to an int. Then we use our Post.find() method to retrieve the Post whose mId matches the :id. That is, the Post a user is requesting to view. When we locate this object, we assign it to the variable post, and place it into our model.

  • When the corresponding post-detail.hbs template loads, it will then display the details for that particular Post.

Let's create this template now.

my-epicodus-blog/src/main/resources/templates/post-detail.hbs
{{#partial "content"}}
   <p>Content:</p>
   <p>{{ post.content }}</p>
   <h5>Created At:</h5>
   <p> {{ post.createdAt }}</p>
   <h5>Post status:</h5>
   <p>
       {{#if post.published }}
           <span class="glyphicon glyphicon-star" aria-hidden="true"></span>  Published
       {{else}}
           <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Draft
       {{/if}}
   </p>
{{/partial}}

{{> layout.hbs}}

We threw some sweet glyphicons in there for good measure, too.

Phew, that was a lot, but now we can do a whole lot more with our app, and the routes match REST conventions as well! We’ll move on to UPDATE and DELETE next.


Example GitHub Repo for Epicodus Blog