This is the frontend documentation for the Gomoku Royale game.
- Introduction
- Pages
- Code Structure
- API
- React Context
- Webpack Configuration
- Authentication
- Tests
- Implementation Challenges
- Further Improvements
The frontend is an SPA and acts as a web-based client for the Gomoku Royale API. It is written mainly in TypeScript and uses the React
Some dependencies used in this project are:
- React Router - Used to manage the application routes;
- React Dom - Used to render the React components with the DOM;
- Webpack - Used to bundle the application;
The application uses the React Router to manage the routes, and can be seen in the App component.
The routes are the following:
- Path:
/
- Component:
Home
- Description: The main landing page of the application.
- Path:
/login
- Component:
Login
- Description: Page for user authentication and login.
- Path:
/me
- Component:
Me
- Description: User profile page displaying personalized information.
- Path:
/register
- Component:
Register
- Description: Page for user registration.
- Path:
/rankings
- Component:
Rankings
- Description: Page displaying overall rankings of users.
- Path:
/rankings/:id
- Component:
UserStats
- Description: Page displaying detailed statistics for a specific user identified by id
- Path:
/lobby/:lobbyId
- Component:
Lobby
- Description: Page displaying details and status of a specific lobby identified by lobbyId.
- Path:
/games
- Component:
FindGame
- Description: Page for users to find and join available games.
- Path:
/games/:gameId
- Component:
Game
- Description: Page displaying details and status of a specific game identified by gameId.
- Path:
/logout
- Component:
Logout
- Description: Page for user logout and session termination.
- Path:
/about
- Component:
About
- Description: Page providing information about the application.
- Path:
/error
- Component:
Error
- Description: Page displaying an error or handling unexpected situations.
The frontend code is organized in the following way:
js
public
- Contains theindex.html
and theindex.css
files;src
api
- Exposes generic modules to communicate with the API;components
- Contains some React components used globally in the application;pages
- Contains the React components and pages used in the application;domain
- Contains the domain classes used in the application;services
- Contains the services used in the application, the media types used and the input and output models;App.js
- The main component of the application;index.js
- The entry point of the application;
tests
- Contains the tests of the application;
In the js
folder, there are other files used for the development of the application; that are equally relevant to
mention:
package.json
- Contains the dependencies of the application;webpack.config.js
- Contains the configuration of the Webpack bundler;tsconfig.json
- Contains the configuration of the TypeScript compiler;eslintrc.json
- Contains the configuration of the ESLint linter;
To abstract the API connection, the apiConnection module was implemented and exposes all
HTTP methods supported
by the API, such as get
, post
, put
and delete
, using a generic fetchAPI
method.
export type ApiResponse<T> = {
contentType: string;
json: T;
}
async function fetchApi<T>(path: string, options: Options): Promise<ApiResponse<T>> {
// ...
}
The media types used in the communication with the API are the following:
application/json
- Used in the request bodies;application/problem+json
- Used in the response bodies when an error occurs;application/vnd.siren+json
- Used in the response bodies when the request is successful.
To abstract an API service, the apiService module was implemented that exposes a
generic callApi
method that
receives the HTTP method, the URI and the optional request body and returns a Promise
with the response.
export async function callApi<B, T>(uri: string, method: Method, body?: B)
: Promise<ApiResponse<T | ProblemModel>> {
// ...
}
Service implementations can be found in the services folder.
Currently, the following services are implemented:
- UsersService - Services for user-related operations;
- GamesService - Services for game-related operations;
- SystemService - Services for system-related operations;
The apiRecipes module provides functions for obtaining URI templates corresponding to all API resources exposed by the backend. These templates can be utilized to construct the actual URIs.
This module was developed to address requests related to Deep Linking, enabling users to access specific resources directly without navigating through the application, thanks to prior bookmarking, sharing the link with other users, or reloading the page.
Given that the application operates as a Single Page Application (SPA), conventional methods described above would lead to a 404 error since the server lacks the information on how to handle such requests explicitly.
By utilizing these URI templates, the application can dynamically populate these URIs and seamlessly navigate to the desired resource without requiring users to navigate through the entire application.
If the resource requires authentication, the application will redirect the user to the login page and, after a successful authentication, will redirect the user to the desired resource. If the resource is not found, the application will redirect the user to the home page.
The React Context is used to share data between components without having to pass properties through all of them in a given subtree of React nodes.
The context is used to share the following data:
- User logged-in information, that can be accessed through the
useCurrentUser
anduseSetUser
hooks, to consult and update the user information, respectively; Such hooks are used in the beginning of the application to check if the user is logged-in and update the user information when the user logs-in or logs-out;
To establish communication with the backend API, the webpack dev server has been set up to route all requests through a proxy to the backend API. This configuration helps circumvent CORS issues and requires no additional configuration on the backend side.
The fallback page is set to the index.html
file, which is the entry point of the application.
Details mentioned above can be seen in the webpack.config.js file.
The user authentication is done in the Login
or Register
pages.
The backend API sets a cookie when the user is successfully authenticated, and that cookie is sent in all subsequent requests by the browser.
When logged-in, another cookie representing the user information is set in the browser. When logging out, the cookie will present an expired token.
The frontend tests use the Playwright framework to test the application in a browser.
Playwright is a powerful, open-source framework developed by Microsoft that enables developers to automate browser tasks with ease. It supports all modern web browsers including Chromium, Firefox, and WebKit, and allows for testing on multiple browser types with a single API.
The tests are located in the tests folder and can be run with the following command inside the js
folder:
npm run test
Inside the tests folder we have the following files:
homePageTest.spec.ts
- Contains the tests for the Home page;loginPageTest.spec.ts
- Contains the tests for the Login page;mePageTest.spec.ts
- Contains the tests for the Me page;variantsPageTest.spec.ts
- Contains the tests for the Variants page;
Sadly, we did not have enough time to implement more tests. We plan to implement more tests for the other pages like we mention in the Further Improvements section.
-
API Integration - The API integration was a challenge because the API was not fully implemented, at least the way we wanted it to be, and the documentation was updated regularly. We had to make some changes to the API, particularly using the siren media type, and integrate it with the frontend, was not an easy task.
-
Concurrency - The concurrency was a challenge because we had to deal with multiple asynchronous operations, such as the initial call to the API to obtain the uri templates. We had to do a mechanism to wait for the response of the API and then continue the execution of the application. We resolved this issue by using the
Promise
class that was not easy to understand at first, but we managed to solve the problem.
- Add css - We planned to add css to the application, but we did not have enough time to do it.
- Add more tests - We only implemented the basic tests for the application, but we could add more tests to improve the code coverage and ensure that the application is working as expected in all possible scenarios.
- Improve user experience - We could improve the user experience by adding more features to the application, such as notifications, animations, skeleton loading, etc.
- Add more features and pages - We could add more features and pages to the application.