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 Method | GET |
| API Endpoint | /api/courses |
| Request Path Parameter | |
| Request Query Parameter | |
| Request Body | |
| Response Body | JSON array of courses |
| Response Status | 200 |
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:
| Status | Meaning |
|---|---|
| 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.
