Building the API

We are ready to complete our API.

Recall the following HTTP verbs (operations):

  • POST: to create a resource
  • PUT: to update it
  • GET: to read it
  • DELETE: to delete it

We already handle one HTTP GET request.

Read Courses
HTTP MethodGET
API Endpoint/api/courses
Request Path Parameter
Request Query Parameter
Request Body
Response BodyJSON array of courses
Response Status200

Here is the code that enables our server to listen for and handle the HTTP GET request directed to /api/courses:

get("/api/courses",  (req, res) -> {
  res.type("application/json");
  return courses;
}, gson::toJson);

The /api/courses is known as a route and so the method above is called a route method. Spark supports route methods that correspond to all HTTP requests: get, post, and so on.

The callback (lambda function) provided as the second argument to the above route method is known as a route handler. We need to update this handler to use a CourseDao to connect our API to our database. Here is the updated ApiServer.main method:

public static void main(String[] args) throws URISyntaxException {
  port(getHerokuAssignedPort());

  Gson gson = new GsonBuilder().disableHtmlEscaping().create();
  CourseDao courseDao = getCourseDao();

  get("/api/courses", (req, res) -> {
    try {
      List<Course> courses = courseDao.readAll();
      res.type("application/json");
      return gson.toJson(courses);
    } catch (DaoException ex) {
      throw new ApiError(ex.getMessage(), 500);
    }
  });
}

Notice the use of getCourseDao() method. Here is its implementation:

private static CourseDao getCourseDao() throws URISyntaxException {
  String databaseUrl = System.getenv("DATABASE_URL");
  URI dbUri = new URI(databaseUrl);

  String username = dbUri.getUserInfo().split(":")[0];
  String password = dbUri.getUserInfo().split(":")[1];
  String dbUrl = "jdbc:postgresql://" + dbUri.getHost() + ':'
      + dbUri.getPort() + dbUri.getPath() + "?sslmode=require";

  Sql2o sql2o = new Sql2o(dbUrl, username, password);
  return new Sql2oCourseDao(sql2o);
}

Also notice the use of ApiError. Here is its implementation:

/**
 * A generic exception for API errors.
 */
public class ApiError extends RuntimeException {
  private final int status;

  /**
   * Construct ApiError.
   *
   * @param message Error message.
   * @param status API response code.
   */
  public ApiError(String message, int status) {
    super(message);
    this.status = status;
  }

  /**
   * Get Error code.
   *
   * @return the API response (error) code.
   */
  public int getStatus() {
    return status;
  }
}

Aside: The response status is a "code" (number) returned to the client that signals the success/failure of their request. Here are the common status codes and their meaning:

StatusMeaning
200 (OK)This is the standard response for successful HTTP requests.
201 (CREATED)This is the standard response for an HTTP request that resulted in an item being successfully created.
204 (NO CONTENT)This is the standard response for successful HTTP requests, where nothing is being returned in the response body.
400 (BAD REQUEST)The request cannot be processed because of bad request syntax, excessive size, or another client error.
403 (FORBIDDEN)The client does not have permission to access this resource.
404 (NOT FOUND)The resource could not be found at this time. It is possible it was deleted, or does not exist yet.
500 (INTERNAL SERVER ERROR)The generic answer for an unexpected failure if there is no more specific information available.

Run the ApiServer. Then, open Postman and make a GET request to http://localhost:4567/api/courses. This must return the list of courses stored in the database.