Building Resilia Academy With Contentful
by Taihua Rubin, Software Engineer @ Resilia
Why Resilia Academy?
In May 2021, we launched Resilia Academy on our platform, intending to help nonprofits bring their professional development to the next level by facilitating and building an engaging content experience. We believe that this new core feature on our platform will add a tremendous amount of value to nonprofits.
Who are our users?
To achieve this, we had to cater the experiences to two main types of users: our Curriculum & Content Developer (C&C developer) and our Nonprofit Users. In general, our C&C developer should draft and publish course contents without using any code. Our non-profit users should be able to have a seamless learning experience.
But where is our content going to live?
- CMS vs. LMS
After scoping out the user requirements, it was necessary to decide if we would use a Content Management System (CMS) or a Learning Management System (LMS). After reviewing 8 LMS vendors, our team decided that a CMS would be a better fit for our needs because:
- LMS creates user friction in using another platform. Given the lack of integration for these LMS solutions with our product/tech stack, we did not want users to leave our platform and have to login into a third-party system to get the benefits of Resilia Academy.
- Advanced LMS provides many sophisticated features that we don’t need, such as grading and live coaching.
- Given the integration capabilities most CMS solutions provide, we have more flexibility in building a more integrated solution for Resilia Academy in our product and iterate progressively as we add more features.
- Contentful
Contentful is one of the most popular CMS solutions available in the market. Given their popularity and powerful GraphQL APIS, we decided to use Contentful for our MVP.
Implementation
- Spike
We used a technical spike to experiment and drive out risks and uncertainties. Some of the most significant risk items that we solved for during the spike were:
The flexibility of the contentful model
Contentful allows us to group all the related resources for a project (in our case, course) together. The resources include content entries, media assets, localizing content settings. Each of the content types shares a standard set of fields that contains some basic information and metadata.
Each of the content types can then be defined with up to 50 fields. These fields each have a corresponding JSON type.
An example use case would be our Course content model, where we have fields such a short text, media, long text, and references.
GraphQL API integration
Contentful’s GraphQL Content API provides a GraphQL interface to our content in Contentful. The GraphQL schema is defined by our content models and generated at request time so that it’s always up-to-date with the current space status.
We use Apollo Client in our codebase to fetch, cache, and modify data. Since we already have an Apollo client pointing to our own GraphQL server, we chose to use the split method of an ApolloLink instance to work with two server endpoints. We did this with the following steps:
First, we created a separate ApolloLink instance by using createHttpLink for contentful:
Then we configured our ApolloClient. By default, our ApolloClient uses the original link that points to our own GraphQL server.
Finally, when executing the query to Contentful’s GraphQL API, we must pass the correct client name to point our ApolloClient to the right place.
Data formatting and rendering capabilities
Rendering JSON payload with React is pretty straightforward with most contentful field types, except for the Rich Text field type. Rich Text allows our C&C Developer to create rich text content, similar to the traditional “what you see is what you get” editors. But to engineers, we get a JSON array of nodes that looks like an abstract syntax tree instead.
For example, a course slide, which has a Rich Text field called Content, with very minimal input & its not-so-minimal payload:
We use the documentToReactComponents function provided by Contentful to serialize a Contentful Rich Text document to React tree.
First, we customize how we want to render each node type:
The documentToReactComponents function then takes in the data payload and the options provided, and the result gets passed in as a rendered element in the React component.
Lastly, if you look back to the screenshot of the GraphQL query, you may notice that the image was an “embedded-asset-block” node type in the JSON payload. Media such as images are considered “Link” type in Contentful API and links to an asset. We handle the embedded images using Apollo’s useLazyQuery.
- Product Delivery
The spike ruled out technical uncertainties and gave product and design time to work on user interface and experience (we do Kanban at the Enterprise team). Once our product designer had almost finished the design, we followed the user flow and worked on state management.
To facilitate storing and retrieving user progress within Resilia Academy, we worked on data model and API design during working sessions.
The state management allows us to store, track and display user progress within Resilia Academy. When the user comes back to take courses, they can easily see their course completion status.
When clicking on Resume Course, the user can be taken to where they left off last time because the progress is stored and retrieved at execution time.
Next Steps
Resilia Academy v1 has just launched. We are excited about the future of Resilia Academy on our platform and look forward to hearing feedback from our nonprofit users.