Members Only is a private message board built as part of The Odin Project's Node.js curriculum. It demonstrates secure authentication, session-based access control, and role-based authorization using a clean MVC architecture.
Visitors can browse public messages anonymously, while registered users can log in, join the club, and create new posts. Members see message authors and timestamps, and admins can remove posts from the board. The app uses PostgreSQL for persistence, Passport.js for authentication, and server-side validation to keep the experience secure and predictable.
- Secure User Authentication
- Password Hashing with bcrypt
- PostgreSQL Database
- Passport.js Authentication
- Express Sessions
- Role-Based Authorization
- Member-Only Content
- Admin Dashboard
- Create Messages
- Delete Messages
- Responsive Interface
- Input Validation
- MVC Architecture
- Environment Variables
- Secure SQL Queries
| Technology | Purpose |
|---|---|
| Node.js | Server-side runtime |
| Express.js | HTTP server and routing |
| PostgreSQL | Relational database for users, sessions, and messages |
| pg | PostgreSQL client for Node.js |
| Passport.js | Authentication middleware |
| Passport Local Strategy | Email and password login flow |
| express-session | Session management |
| connect-pg-simple | Stores sessions in PostgreSQL |
| bcryptjs | Hashes and compares passwords securely |
| express-validator | Validates and sanitizes form input |
| EJS | Server-rendered views |
| HTML5 | Semantic page structure |
| CSS3 | Responsive styling and layout |
| JavaScript | Application logic |
| dotenv | Loads environment variables |
| nodemon | Development server restart workflow |
View folder tree
members-only/
├── app.js
├── config/
│ └── passport.js
├── controllers/
│ ├── authController.js
│ ├── homeController.js
│ ├── membershipController.js
│ └── messageController.js
├── db/
│ ├── pool.js
│ └── queries.js
├── middleware/
│ └── authMiddleware.js
├── public/
│ └── css/
│ └── style.css
├── routes/
│ ├── authRouter.js
│ ├── membershipRouter.js
│ └── messageRouter.js
├── views/
│ ├── partials/
│ │ ├── footer.ejs
│ │ ├── formErrors.ejs
│ │ ├── head.ejs
│ │ └── navbar.ejs
│ ├── becomeAdmin.ejs
│ ├── index.ejs
│ ├── join.ejs
│ ├── login.ejs
│ ├── newMessage.ejs
│ └── sign-up.ejs
├── package.json
└── README.md
Members Only uses two primary application tables:
usersstores account data, hashed passwords, and role flags.messagesstores message content and links each post to a user.
| Column | Purpose |
|---|---|
id |
Primary key |
first_name |
User first name |
last_name |
User last name |
username |
Login identifier, stored as email |
password |
Hashed password |
membership_status |
Marks a user as a club member |
is_admin |
Grants admin privileges |
| Column | Purpose |
|---|---|
id |
Primary key |
title |
Message title |
text |
Message body |
created_at |
Creation timestamp |
user_id |
Foreign key to users.id |
Users (1) -------- (<) Messages
Each user can create many messages, and every message belongs to exactly one user.
-
Clone the repository:
git clone https://github.com/your-username/members-only.git
-
Move into the project folder:
cd members-only -
Install dependencies:
npm install
-
Create the PostgreSQL database and tables using your preferred SQL client.
-
Add your environment variables in a local
.envfile.
Create a .env file in the project root with the following values:
DB_HOST=
DB_USER=
DB_PASSWORD=
DB_NAME=
DB_PORT=
SESSION_SECRET=
CLUB_PASSCODE=
ADMIN_PASSCODE=Start the development server:
npm run devStart the production server:
npm start- Register a new account with your name, email, and password.
- Log in with your registered credentials.
- Join the club by entering the secret club passcode.
- Become an admin by entering the admin passcode.
- Create new messages from the authenticated message form.
- View author names and timestamps as a member or admin.
- Delete messages as an admin.
- Log out when finished.
Security overview
bcryptjshashes passwords before they are stored in the database.Passport.jshandles local authentication with the Passport Local Strategy.express-sessionkeeps users signed in across requests.connect-pg-simplestores session data in PostgreSQL.- Parameterized SQL queries reduce the risk of SQL injection.
express-validatorvalidates and sanitizes incoming form data.- Environment variables keep secrets out of source code.
- Route guards protect member-only and admin-only pages.
- User profiles
- Edit messages
- Profile pictures
- Rich text editor
- Pagination
- Search
- Dark mode
- Email verification
- Password reset
- Docker support
Building Members Only strengthened my understanding of:
- Express.js routing and middleware
- PostgreSQL schema design and relationships
- Passport.js authentication flows
- Authentication and authorization separation
- bcrypt password hashing
- Session-based login persistence
- MVC architecture in a real project
- SQL relationships between users and messages
- Input validation with express-validator
This project is licensed under the MIT License.
- The Odin Project
- Express Documentation
- Passport.js Documentation
- PostgreSQL Documentation


