- Overview
- Features
- Technologies Used
- Getting Started
- Deployment (very interesting)
This is a full-stack recipe application built with the MERN (MongoDB, Express.js, React, Node.js) stack.
- User Authentication: User authentication system implemented using JWT mechanism.
- Recipe Creation: Users can create and share their favorite recipes, including details such as ingredients, instructions, and images.
- Recipe Storage: Save your favorite recipes to access them later conveniently.
- Recipe Deletion: Users can remove recipes they no longer need.
-
Frontend:
- React.js: A JavaScript library for building user interfaces.
- React Query: A library for managing, caching, and updating server state.
- Axios: A promise-based HTTP client for making requests to the server.
- Tailwind CSS: A utility-first CSS framework for rapid UI development.
- React Router DOM: Enables navigation and routing in the React application.
- React Hot Toast: A lightweight toast notification library for React.
-
Backend:
- Express.js: A minimal and flexible Node.js web application framework.
- MongoDB: A NoSQL database for storing recipe and user data.
- Mongoose: An ODM (Object Data Modeling) library for MongoDB and Node.js, providing a schema-based solution.
- JSON Web Token (jsonwebtoken): Used for creating and verifying web tokens for user authentication.
- Bcrypt: A library for hashing passwords.
- Clone the repository:
git clone https://github.com/korngsamnang/mern-recipe-app
- Install dependencies:
npm install
in both the client and server directories. - Set up MongoDB and configure the connection string in the server's
.env
file. - Run the development server:
npm run dev
for both the client and server.
For this project, I deployed the backend and frontend separately to different cloud service platforms by just using this single repository.
- For the backend, I used Heroku to deploy the server.
- For the frontend, I used Netlify to deploy the client.
The problem was authentication. I used JWT (json web token) to implement authentication. So the token needs to be sent from the server to the client, and the client needs to be sent back to the server. So instead of using an authorization header or local storage for storing cookies, I use an HTTP-only cookie. The cookie is always sent through an HTTP connection automatically.
It's perfectly fine if we have the backend and frontend on the same domain, but if not, it's very hard to configure it to send cookies from one to another because the HTTP-only cookie design is very secure to prevent XSS attacks.
However, it is possible to do that by doing some configuration in both the backend and frontend.
We need to properly set up cors along with credentials
set to true
.
app.use(
cors({
origin: process.env.CLIENT_URL,
credentials: true,
})
);
I use Axios, so along with baseURL, we need to add withCredentials: true
.
const api = axios.create({
baseURL: import.meta.env.VITE_API_URL,
withCredentials: true,
});
When sending a cookie from the backend to the frontend, we have to specify options like this:
res.cookie("jwt", token, {
expires: new Date(
Date.now() + process.env.JWT_COOKIE_EXPIRES_IN * 24 * 60 * 60 * 1000
),
withCredentials: true,
sameSite: "None",
httpOnly: true,
secure: true,
});
Check out the documentation to learn more about these options.
That's it.
Feel free to contribute, report issues, or suggest improvements. Happy cooking!