This course repository requires Node.js and a TursoDB account
Install dependencies in the root directory and the client directory, respectively.
- In root directory:
npm install
- Navigate to client directory in a separate terminal:
cd client
npm install
- Create a Turso DB account (free).
Install the Turso CLI:
# Mac
brew install tursodatabase/tap/turso
# Linux/Windows
curl -sSfL https://get.tur.so/install.sh | bash
Signup and Login to Turso (this will open your browser)
Note: The docs tell you to run turso auth signup
but if you created a Turso account, you can skip this step and run the following command to login. You also won't need to create a replica.
turso auth login
Create an ecommerce database:
turso db create <database-name>
- Create a
.env
file in the root directory and add the following. You can also reference the.env.example
file in the root.
TURSO_CONNECTION_URL=
TURSO_AUTH_TOKEN=
JWT_SECRET=
- To get the
TURSO_CONNECTION_URL
and generate a token for your database forTURSO_AUTH_TOKEN
run the following commands:
turso db show --url <database-name>
turso db tokens create <database-name>
- Generate a secure
JWT_SECRET
using OpenSSL or Node.js
openssl rand -base64 32
# or
node -e "console.log(require('crypto').randomBytes(32).toString('base64'));"
- In root directory:
npm run generate
npm run db:push
To populate the database with some sample data, as well as a list of products run:
npm run seed
You can explore the data tables by running:
npm run studio
- In the root directory, start the server:
npm start
Server will run on http://localhost:3000
.
- Start React app in the client directory:
npm run dev
You can view the app on http://localhost:5173
. After creating an account, you'll be able to view the products and shop around. No refunds!
This a e-commerce application that allows users to perform the following actions:
- View a List of Items: Display items available for purchase.
- Add Items to Shopping Cart: Enable users to add items they wish to buy.
- Checkout Process: Allow users to "submit" an order.
- Purchase Confirmation: Show a confirmation message with the purchase amount and a transaction ID.
- User Authentication
- JWT tokens are used for maintaining user sessions.
- Token is stored in localStorage client-side.
- Flow: User logs in -> JWT token is generated -> Token is stored in localStorage -> Token is sent in the header for routes.
- List of Products
- Flow: User signs in or sings up ->
AuthProvider
updates state, settingisAuthenticated
to true -> This triggersProductsProvider
to fetch products from backend.
- Cart Management
-
State Management
- The cart state is managed using useReducer with actions for ADD, REMOVE, QUANTITY, and SUBMIT.
- The CartContext provides the cart state and dispatch function to child components.
-
Adding Items to Cart
- Flow: User Interaction → dispatch(ADD) → Reducer → State Update → Backend Sync
- The ADD action handles both new item addition and quantity increment for existing items.
-
Removing Items from Cart
- Flow: User Interaction → dispatch(REMOVE) → Reducer → State Update → Backend Sync
- The REMOVE action filters out the item from the cart array.
- Data Storage
Schema Design
- The database schema is defined in schema.ts using Drizzle ORM. It consists of five main tables:
- products: Stores product information.
- users: Stores user account information.
- carts: Represents shopping carts for users.
- cart_product: A junction table for the many-to-many relationship between carts and products.
- orders: Stores order information, linking users and carts
Relationships
The schema defines relationships between tables using Drizzle ORM's relations function:
- Products have many CartProducts
- Users have many Carts and Orders
- Carts belong to a User and have many CartProducts
- CartProducts belong to a Cart and a Product
- Orders belong to a User and a Cart
Data Seeding
The database is seeded with initial data using the seedDatabase function in ./server/db/seedData.ts. This function performs the following operations:
- Inserts sample products into the products table.
- Creates user accounts with hashed passwords.
- Generates shopping carts for each user with random products.
- Creates sample orders for each user based on their cart contents.
- Initial Design and Planning
Database Schema Design:
I started by creating a rough design of the data model using Excel since it has an easy to use interface for rows and columns. This helps me map out a visual representation of the data and relationships between tables. This initial schema design served as a blueprint for the DrizzleORM models, which I tweaked as I built out the application.
- API Endpoint Planning
I mapped out potential API endpoints early in the process to ensure that the frontend and backend would be in sync. This helped me identify the data I needed to send and receive, as well as the actions that would trigger these requests.
- Challenges
There were some challenges faced with cart management. The first implementation made an API request for every cart update and had a different route for every type of cart interaction. This got messy and complicated quickly. I refactored the cart management to a more efficent cart syncing method.
- Implement multi-factor authentication
- Integrate social media login options (Google, Facebook, etc.)
- Integrate multiple payment gateways (PayPal, Stripe, etc.)
- Implement personalized product recommendations
- Create a personalized user homepage experience based on user browsing and purhcase history.
- Create a review system for products.
- Implement a "wishlist" feature for users to save items for later.
- React Testing Library: Chosen for its focus on testing components in a way that resembles user interactions.
- Vitest: Chosen for compatibility and convenience. Vitest also has a very similar API compared to Jest.
For testing, I focused on unit tests for key components of the frontend application including:
- Product.test.tsx
- ProductList.test.tsx
- Checkout.test.tsx
- Receipt.test.tsx
Areas for Future Testing:
- End-to-End Tests: To validate user flows and interactions.