Scilim

blog

About

LendARead2

2024-2-5

Table of contents

Introduction

The goal of this project was migrating a MVC full-stack webapp to a SPA-REST architecture while keeping total compatibility with the original data, no loss of information was allowed. Moreover, heavy emphasis was made in creating a truly REST api as defined in the dissertation of Roy Fielding. Finally, altough the frontend's code quality was not a prime concern, the design of the structure was diligently crafted.

The objective of the web application is to create a community of readers, where all of them can upload their books and request loans from each other. The aim is to allow readers to access books that would not normally be easily accessible, creating like a decentralized library.

For example, let's imagine the user Julian, who enjoys reading philosophy in multiple languages. Thanks to other readers with German heritage (for example), he can borrow a philosophy book from a German author that he could not have read by going to a library in Argentina.

REST

The main premise we took from the REST architecture was the correct utilization of all the features of the HTTP protocol. That is, matching endpoints to resources, using the HTTP methods idiomatically upon the resources, utilizing query params and custom media types for each resource representation and so on.

As an example, here is the LendingsController.java controller of the REST api responsible for the lending resource:

/api/lendings

GET

  • Produces: application/vnd.asset-instance-lending+json
  • Description: Retrieves lendings with optional filtering, sorting, and pagination.
  • Query Params:
    • page: Page number for pagination.
    • itemsPerPage: Number of items per page.
    • assetInstanceId: Filter by asset instance ID.
    • borrowerId: Filter by borrower ID.
    • state: Filter by lending state.
    • lenderId: Filter by lender ID.
    • sort: Sort field.
    • sortDirection: Sort direction (asc or desc).
    • startingBefore: Filter for lendings starting before a certain date.
    • startingAfter: Filter for lendings starting after a certain date.
    • endBefore: Filter for lendings ending before a certain date.
    • endAfter: Filter for lendings ending after a certain date.

POST

  • Consumes: application/vnd.asset-instance-lending+json
  • Produces: application/vnd.asset-instance-lending+json
  • Description: Creates a new lending.

/api/lendings/{id}

GET

  • Produces: application/vnd.asset-instance-lending+json
  • Description: Retrieves a specific lending by its ID.

PATCH

  • Consumes: application/vnd.asset-instance-lending-state+json
  • Produces: application/vnd.asset-instance-lending-state+json
  • Description: Updates the state of a specific lending.

SPA

In terms of the SPA structure we settled for defining custom hooks that encapsulate api calls and logic so that the jsx components only include the actual rendering of components. We also considered using a more layered approach, adding a services layer so that the custom hooks do not directly use axios, but we decided that it would add more boilerplate than encapsulation so we use axios directly from the hooks. Much of the considerations and inspiration was borrowed from Juntao Qiu in Martin Fowler's blog

Project Structure

├── src
│   ├── App.tsx
│   ├── __mocks__
│   │   └── .... 
│   ├── __tests__
│   │   └── .... 
│   ├── components
│   │   └── .... 
│   ├── contexts
│   │   └── .... 
│   ├── hooks
│   │   ├── __mocks__
│   │   │   └── ...
│   │   ├── api
│   │   │   └── ...
│   │   ├── asset
│   │   │   └── ...
│   │   ├── assetInstance
│   │   │   └── ...
│   │   ├── languages
│   │   │   └── ...
│   │   ├── lendings
│   │   │   └── ...
│   │   ├── locations
│   │   │   └── ...
│   │   ├── reviews
│   │   │   └── ...
│   │   └── users
│   │   │   └── ...
│   ├── i18n.js
│   ├── locales
│   │   ├── en
│   │   │   └── translation.json
│   │   └── es
│   │       └── translation.json
│   ├── main.tsx
│   ├── setupTests.ts
│   ├── testUtils
│   │   └── setupTests.js.ts
│   └── views
│       └── ...
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts

Authentication

In terms of authentication, it was all built with JWT from Spring Security and then handled from the frontend through a context that was responsible for managing the acess and refresh token through their lifecycle in session or local storage.

Axios

Upon reviewing different alternatives, we settled for axios for making the calls to our REST api as it provided the best interface as well as allowing us to easily manage refresh token logic through interceptors.

Testing

Testing was carried out of the frontend through vitest and @testing-library of numerous components, both of jsx components as well as the custom hooks.