- Fullstack framework: NextJS
- Using the latest version NextJS 14 with React Server Component
- Components are rendered on the server first, alleviating the pressure of rendering fully on the client side compared to older versions of
React
. Developers can opt out of server-side components by addinguse client
at the top of a file, giving them the flexibility to fully control the behavior of the app. - NextJS 14 introduced server actions, which are similar to the concept of
Rest APIs
, but now we can fetch data directly inside our components instead of using hooks likeuseEffect
. However,Rest APIs
can still be used in the app; we use them specifically when interacting with third-party services.
- Language: TypeScript
- Styling: TailwindCSS, ShadCN
TailwindCSS
andShadCN
are among the modern choices for web development. Compared to traditional component libraries that are downloaded and hidden innode_modules
,ShadCN
, as a component library, exposes the source code of its components to developers, allowing them to fully customize the behavior of reusable web components.
- State management: Zustand
- Used to improve the interactivity of the map, this allows the map to listen to and react to custom events. For example, when clicking the details of a track, the
Zustand store
dispatches an event that is listened to by the map to perform a zoom in.
- Used to improve the interactivity of the map, this allows the map to listen to and react to custom events. For example, when clicking the details of a track, the
- Database and Orm: PostgreSQL, DrizzleORM
DrizzleORM
is one of the best choices for modernTypeScript/JavaScript
backend development as it generates editableSQL
for migrations, which allows developers to gain control of the process instead of black-boxingSQL
queries
- Testing: Jest, Cypress
- See full testing explanation.
- Third Party Services
- Map: Mapbox
- Payment: Stripe
- File Storage and Hosting: Vercel
- CI/CD: Github Actions
- When a user performs a register or login form action, client-side validation is first performed before the form submission reaches the server. This validation checks whether the form values are acceptable.
- When the form submission reaches the server, server-side validation is performed, checking the form values. For registrations, the server checks whether there are duplicate usernames, while for login, it checks whether the user exists and if the password is correct. The server then attempts to generate a sessionId and a userId (the latter only during registration) and stores them in the database.
- If an error occurs, the server responds with error messages, and the client renders a toast with error information.
- If no error occurs, the server sets the cookie and returns success to the client; the client will then be redirected to the main app page.
- When a user performs an upload file action, the client first performs client-side validation to check whether the file is a valid
GPX
file. Then the form submission reaches an API that interacts with theVercel Blob
file storage service to store the file. Upon successful upload,Vercel storage
returns the download URL of the file. - With the success API call for uploading the file, another API to update the data is then been called, which parses the data of the
GPX
file to calculate the distance and elevation change of it. The API then submits the data to the database. Upon successful submission, message are sent back to the client and a toast is rendered. - When user performs a download file actions, the client directly call the download URL provided by
Vercel Storage
and performs a download.
- When a user interacts with the map-related part of the website, the state of the app changes and triggers an action dispatched by the
Zustand store
, which is then listened to by the map to perform the corresponding action. - When a user successfully uploads a file, the track state of the app changes since the total number of tracks to display has increased. The
Zustand store
dispatches an action to re-render the map and zoom in on the track just uploaded. - When a user switches between the light and dark color themes of the map or changes data to view between 'my tracks' and 'all tracks', the
Zustand store
dispatches an action to re-render the whole map. - When a user clicks on the sidebar to view details of a track or interacts directly with the map to view map details, the zoom state of the map changes, and the
Zustand store
dispatches an action to zoom the map to the corresponding track. And when the user clicks the back button, another zoom action is dispatched, zooming out the map to the original state.
- When a user wants to subscribe or change their subscription plan, the user will be redirected to a checkout session managed by Stripe. Upon successful payment, the user will be redirected back to an info page showing the details of their subscription. Then, a server action is called to update the subscription info in the database.
- When a user wants to cancel a subscription, a server action is called to cancel the subscription by interacting with the Stripe service. The server then updates the state of the subscription in the database.
- When a user logs in or enters the app, a validation of the subscription status is automatically performed to validate the state of the user.