I used this small app to experiment with some of the tools in the react.js ecosystem.
The app is deployed using vercel, that allows to easily deploy and scale jamstack apps using serverless functions
It consists in a simple database-persisted todo app with register/login functionality
Optimistic updates
Optimistic updates is a way to refer to the UX pattern in which the app performs the success action before actually receiving information back from the server. It's a notoriously tricky pattern to implement, as the frontend has to partially reimplement the backend's logic. In addition to that, the frontend has to handle many edge cases such as handling the errors after the client showed a confermation feedback, or taking care of race conditions with some combinations of fast user actions. While it is possibile to implement manually all of this, a pragmatic solution is to use libraries such as react query, that take care of all the logic that is not app-specific.
Continuos integration
Using a combination of vercel and github actions, it's possible to implement a CI/CD pipeline that performs automated checks before deploying. In order to merge the code to the main branch (so that vercel deploys to the production environment) the following checks are performed:
- formatting (using prettier)
- linting (using eslint)
- typecheck (using typescript)
- unit/integration tests (using vitest)
- end to end tests (using cypress)
The tests are designed using the so-called test pyramid in mind:
- unit tests against simple units of code
- integrations tests to check that different units of code work within each other
- e2e tests to check the actual behaviour of the whole app
Each kind of test has its own tradeoffs: the more you go up in the testing pyramid, the more powerful and abstract the test is (so that even if the internal behaviour changes, the high-level, e2e specification should remain valid). On the other hand, e2e tests tend to be extremely fleaky, complex to write and mantain, take more time to execute, and don't always provide a clear answer of what is actually broken when the test is red.
Backend
The backend is realized using serverless functions and persisting the state via a supabase remote sql database.
Prisma is used as ORM for the great developer experience it offers. Some of its greatest features are:
- Ergonomic, type safe, easy to use typescript client
- A tool to visualize the sql table called prisma studio
- Developer tools to help generate, prototype and apply migrations
- A custom DSL to define the schema, that looks like the following:
model Todo {
id Int @id @default(autoincrement())
text String
completed Boolean
createdAt DateTime @default(now())
User User @relation(fields: [userId], references: [id])
userId Int
}
model User {
id Int @id @default(autoincrement())
username String @unique
hashedPassword String
todos Todo[]
}