biru is a full stack beer journal web app. You can try biru out here.
The front end is implemented on top of Next.js. User authentication state is stored globally using React context, and a higher order component (HOC) handles redirects when a user visits a page that they do not have permission to see. When a user that isn't authenticated visits the site, they are redirected to the login page. After logging in, they are redirected to the home page, where they are shown a feed of beers added by users they follow. The profile page contains the user's information, along with the beers that they've added. A user can add and edit beers, as well as edit their profile. They can add an image for each beer and to their profile. Users can search for others by username, visit their profile and follow them. The UI was developed with a mobile-first approach and is fully. responsive.
The back end is an Express application, and PostgreSQL is the relational database used for storing data. The Sequelize object relational mapper (ORM) is used to mediate interactions with the server and to define schemas. Session-based authentication is used, with Passport.js as the middleware for handling incoming requests. Images uploaded on the front end are sent to the server, where they are then uploaded to Cloudinary.
Working on this personal project gave me valuable experience with relational databases and ORMs, as well as different user authentication architectures. This was my first time working with sessions and cookies, and I learned a lot through the development and deployment process (which involved many dreaded cors issues). Overall, this project greatly improved my understanding of and skills in server-side programming. I also improved my front end skills, as using httpOnly cookies for authorization requires more complex management tools than token-based authentication systems, because httpOnly cookies aren't accessible by client-side Javascript. I originally read session tokens on the Next.js server in getServerSideProps
, which enables server-side rendering, but ultimately chose a static generation method, showing a loading screen until the authentication status is determined through an API call.
The server
subdirectory contains the code for the back end application, and the client
subdirectory contains the code for the front end application.
Data is stored in a PostgreSQL database. Cloudinary is used for managing image uploads and storage. The deployed server and client-side applications are hosted separately on Heroku.
express
: web application frameworkexpress-session
: session management middleware for authenticationcors
: middleware for enabling cross-origin requestsdotenv
: loads environment variablespg
: modules for interacting with Postgres databasessequelize
: ORM packagepassport
: authentication middlewarepassport-local
: strategy for authenticating with username and passwordbcrypt
: library for hashing passwordsconnect-session-sequelize
: sequelize session storepg-hstore
: serializes and deserializes JSON data (required for usingpg
withconnect-session-sequelize
)cloudinary
: SDK for managing cloudinary API callsmulter
: middleware for handing form data in requestsmorgan
: request logginghttp-status-codes
: constants for HTTP status codes
next
: producionreact
framework with static and server side rendering, pre-fetching, etc.react
: UI libraryreact-dom
: DOM-specific methods forreact
sass
: css preprocessoraxios
: HTTP client for making requests to the serverreact-easy-crop
: component for cropping images@mui/material
: component library@mui/icons-material
: icon libraryreact-icons
: icon library@emotion/react
: required for MUI css styles@emotion/styled
: required for MUI styled components
Enter the server
subdirectory and install the dependencies:
cd server
npm install
The backend stores data in a PostgreSQL database, so you must have it installed on your machine. Use the following commands to create and initialize the database:
npm run db:create
npm run db:migrate
npm run db:seeds
Several environment variables must be configured in order to run the back end application. You must have a Postgres user and host configured on your local machine, a Cloudinary account and a session secret for express-session
. Create a file called .env
in the server
subdirectory with the following contents:
PORT=3001
POSTGRES_HOST_DEV="your postgres host"
POSTGRES_USER_DEV="your postgers user"
POSTGRES_PASSWORD_DEV="your postgres user password"
SESSION_SECRET="your session secret"
CLOUDINARY_CLOUD_NAME="your cloudinary cloud name"
CLOUDINARY_API_KEY="your cloudinary API key"
CLOUDINARY_API_SECRET="your cloudinary API secret"
Now, the app can be started:
npm run dev
The app will be listening on http://localhost:3001.
Enter the client subdirectory, install the dependencies and run the app in development mode:
cd client
npm install
npm run dev
Open http://localhost:3000 with your browser to see the result.