For backend setup and development instructions, refer to the sections below.
For frontend-related information and setup, please check the apps/mocaverse/README.md file.
For smart-contract related information and setup, please check the Mocaverse NFT Gate Repository.
- One user can have one invite code (Inviter)
- One invite code can be used by many users (InviteCode)
- One user can has many refresh tokens (session management)
- One user can has many providers (for future multi-login support)
- A Provider has a type (ProviderType):
- EVM Wallet
Options | Decision | Reasoning |
---|---|---|
Invite code can have two types: ONCE or MULTIPLE |
β | - We don't have lots of types of invite codes, so it's not worth having two types. |
Invite code has a remaining count (remaining ), when it is used up (0), it is no longer valid |
β | - It is easier to implement and understand. |
Decision made:
- Invite code has a remaining count (
remaining
), when it is used up (0), it is no longer valid
Consideration | Details |
---|---|
User Base | ~1 million users |
Ease of Use | - Easy to type - Avoid lowercase/uppercase confusion - No special characters |
Security | Short but hard to guess |
Decision made:
- Format: 8 characters, alphanumeric, uppercase only
- Collision rate: 0.002686% (pool of 36^8 = 2,821,109,907,456 combinations)
- Benefits:
- Easy for users to input and communicate
- Reduced ambiguity by using only uppercase letters
- Frontend can auto-convert input to uppercase
Options | Decision | Reasoning |
---|---|---|
Invite code is associated with a unique email address (a provider) | β | - If a user has multiple providers in the future, it would be difficult to determine which provider the invite code is for. |
Invite code is associated with a user | β | - Allows for flexibility with multiple providers per user - Simplifies tracking and management of invite codes |
Decision made:
- Invite code is associated with a user
Reasoning:
- If invite code is associated with a provider, in the future, if a user has many providers, it is hard to tell which provider the invite code is for.
- invite code is associated with an inviter
- initial invite code does not have inviter
- Add index to
code
field to speed up the search - Let's say a database transaction may take 30ms (for example) for running
findFirst
andupdate
methods (and network latency). - Request per second (RPS) = 1000 / 30 = 33.33
- Invite code is hard to guess, so it is hard to hack (because of the low collision rate)
- We have a rate limit on API (register with email) to prevent abuse
The system exposes several RESTful API endpoints for interacting with invite codes:
POST /auth/register-with-email
: Register a new user with an invite codePOST /auth/login-with-email
: Authenticate a userPOST /auth/refresh
: Refresh an authentication tokenPOST /auth/logout
: Log out a user
- Backend: Node.js with Express.js
- Database: PostgreSQL with Prisma ORM
- API Design: ts-rest for type-safe API contracts
- Authentication: JSON Web Tokens (JWT)
- Testing: Jest for unit and integration tests
- Node.js (v18 or later)
- pnpm package manager
- PostgreSQL database
-
Clone the repository
-
Run
pnpm install
to install dependencies -
Set up environment variables (refer to
.env.example
) -
Start the database using Docker Compose:
docker-compose up -d
This will start a PostgreSQL container and create a sample database
mocaverse
with the following credentials:- Username:
johndoe
- Password:
randompassword
- Database:
mydb
- Port:
5434
- Username:
-
Push the database schema:
pnpm nx run mocaverse-prisma-schema:push
-
Seed the database:
pnpm nx run mocaverse-prisma-schema:seed
-
Start the server:
pnpm nx run mocaverse-server:serve
-
The API will be available at
http://localhost:3000
Run unit tests with: pnpm nx run-many -t test
Run integration tests with: pnpm nx run mocaverse-server-e2e:e2e
We chose PostgreSQL as our database system for the following reasons:
- One of the most popular relational databases
- Enough features for our system
- Mature and well-documented
We paired PostgreSQL with Prisma ORM due to:
- Very strong type safety
- Easy to add models, and it also handles database migrations
- One of the best database ORM for TypeScript
- Bcrypt for password hashing
- JWT for secure authentication
- Rate limiting on API endpoints to prevent abuse
- For larger amount of request per second, we need to use a more scalable database system, such as Redis.
- Limit one user can only have one email provider