back to all blogsSee all blog posts

Using Open Liberty and MicroProfile to power the RealWorld Conduit Application

image of author
David Shi on Mar 3, 2021
Post available in languages:

In the sphere of web applications, a developer can easily feel overwhelmed by the large selection of options for a frontend, backend, and their supporting technologies. You might strategically choose what’s most popular or trending. Maybe you stumbled across stats showing that a certain framework performs a certain function particularly well. Or maybe, you just like its style. Choosing a framework can feel like buying new clothes; wouldn’t you like to try out your choice before rocking it?

Let RealWorld serve as your fitting room for playing around and building "real world" applications. Created as an open source project, RealWorld enables users to choose any combination of frontend and backend to see how they work together in a sample app called "Conduit". RealWorld manages interoperability by enforcing API specs for Conduit’s endpoints, providing developers a great sandbox environment for exploring new or familiar technologies. What if you’re a frontend developer with no interest in backend, or vice versa? Since the frontend or backend can run independently of each other, you can for example, dedicate your efforts to finding the frontend that suits you without knowing any details of how the backend performs its tasks.

For today’s blog post, I explore some MicroProfile technologies and Open Liberty guides for powering the Conduit backend on Open Liberty. Specifically, we’ll prop up a JAX-RS application for our REST endpoints, add some security to them, then persist our data with JPA.

Starting a RESTful App

Let’s begin by deploying a JAX-RS application with some initial endpoints so we can start playing with it. By following the guide to create a RESTful web service, we can prop up an endpoint to return simple messages or JSON-formatted classes. As a simplified example, for Conduit we can start with an endpoint to return an instantiated user:

    @GET
    @Path("/user")
    @Produces(MediaType.APPLICATION_JSON)
    public Response getUser() {
        User testUser = new User("Johnny Appleseed", "[email protected]");
        return Response.ok(testUser.toJson()).build();
    }

Conduit has defined endpoints in their API specs, so we won’t need to worry about design and can focus on providing the necessary content. You can view my implementation in the example app GitHub repo.

The only exception in that directory is the HealthEndpoint.java file. You can insert statistics or checks in that file that you want MicroProfile Health to report. We have another guide to creating a dedicated health endpoint. In my simple case, it served as a basic test endpoint for me to determine whether the application started successfully.

Protecting Your App

Now that we can reach and interact with our application, we want to secure our endpoints by restricting certain behaviors from unauthorized users. MicroProfile JWT serves this exact purpose, and it works for us with minimal configuration.

In the Conduit application, we generate and provide a new token whenever we create a new user or after any successful login. Open Liberty provides a guide to securing microservices with JWT, and I used it to write a JwtGenerator to wrap in the User API.

Since most operations that involve users require authentication, I locked all endpoints to a certain role by default with @RolesAllowed, and then chose to allow anyone to register specifically with @PermitAll:

@Path("/")
@RolesAllowed("users")
public class UsersAPI {
    /* Register */
    @POST
    @Path("/users")
    @PermitAll // allow anyone to register
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response createUser(CreateUser requestBody) {
        ...
    }

    /* Update User */
    @PUT
    @Path("/user")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response update(CreateUser requestBody)
        ...
    }
}

Adding Persistence

After developing your application to allow users or objects to interact with one another, we must consider persisting these objects. Otherwise, they vanish from memory when the server stops. And—-you guessed it—​we have a guide to persisting data with JPA as well.

With JPA, we can organize create, read, update, and delete (CRUD) operations into a Data Access Object (DAO), without having to fret over other data management implementation details. As JPA is a collection of interfaces, your code looks the same across the different persistence providers that can house your data implementation.

Continuing with Users as an example, here’s a UserDao, with the other DAO files located in a directory in the example app GitHub repo:

public class UserDao {

    @PersistenceContext(name = "realWorld-jpa")
    private EntityManager em;

    public void createUser(User user) {
        try {
            em.persist(user);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public User findUser(Long userId) {
        try {
            return (userId == null) ? null: em.find(User.class, userId);
        } catch (NoResultException e) {
            return null;
        }
    }

    public User updateUser(User user, User newUser) {
        if (user == null) return null;
        user.update(
            newUser.getEmail(),
            newUser.getUsername(),
            newUser.getPassword(),
            newUser.getImg(),
            newUser.getBio());
        return em.merge(user);
    }

    public void deleteUser(Long userId) {
        em.remove(em.find(User.class, userId));
    }

    public User login(String email, String password) {
        return em.createQuery("SELECT u FROM User u WHERE u.email = :email
                               AND u.password = :password", User.class)
            .setParameter("email", email)
            .setParameter("password", password)
            .getSingleResult();
    }
}

Try it Out!

You can pull the code in and play with it on your own machine. Our full implementation can be found on our Open Liberty GitHub where the README explains how to build it locally. To view Conduit locally, I personally chose Angular as my frontend, and followed their instructions on starting it. Of course, you can choose whichever frontend you like!

With the Open Liberty guides as a resource, we can follow along to put together a backend with RESTful endpoints, secure them with MicroProfile JWT, and then add persistence by using JPA. Although these were the minimal ingredients I covered in this blog, MicroProfile and Open Liberty offer many more accessible tools that are ready for in-production use.

We encourage you to wander around the open source space. You’re welcome to pull our example app code and play with it directly.

Build it, run it, make an issue, or open a pull request. Don’t forget to check out the RealWorld repository for hands-on experience with various other backends and frontends.