Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

Killeon Patterson
Killeon Patterson
18,527 Points

Refactoring user storage on the header to storage on MongoDb.

Hello, I'm refactoring my MERN app. Before, I was using basic authorization and storing the user on the req.body. Now, I'm attempting to create a JWT token and store the user in a Mongodb collection. I need the UserContext.js to access the db folder that has the Mongodb connection. All of the solutions that I have attempted has broken the app in some other way. The current version of the project is showing these errors...

ERROR in ./src/context/UserContext.js 8:0-55 Module not found: Error: You attempted to import ../../db/connection.js which falls outside of the project src/ directory. Relative imports outside of src/ are not supported. You can either move it inside src/, or add a symlink to it from project's node_modules/. ERROR in ./src/context/UserContext.js 9:0-35 Module not found: Error: Can't resolve 'mongodb' in 'C:\react-auth\client\src\context'

For context, I built this project with the React Authentication course. It was suggested to me to understand usercontext better when I was inquiring about my previous project. I finished that course and I'm using some of the code from the earlier project on this one. It appears that I'm getting stuck on the same concept, but different project.

github

'use strict';

import express from 'express';
import { check, validationResult } from 'express-validator';
import bcryptjs from 'bcryptjs';
import connectToDatabase from './db/connection.js';
import { ObjectId } from 'mongodb';
import jwt from 'jsonwebtoken';

// Construct a router instance.
const router = express.Router();

async function getCollection() {
  const db = await connectToDatabase();
  return db.collection('users');
}

// This array is used to keep track of user records
// as they are created.
const users = [];

/**
 * Middleware to authenticate the request using Basic Authentication.
 * @param {Request} req - The Express Request object.
 * @param {Response} res - The Express Response object.
 * @param {Function} next - The function to call to pass execution to the next middleware.
 */
const authenticateUser = async (req, res, next) => {
  let message = null;


  // Get the user's credentials from the Authorization header.
  const credentials = req.body;


  if (credentials) {
    // Look for a user whose `username` matches the credentials `name` property.

    const collection = await getCollection();
    const query = { email: new ObjectId(req.params.email) };
    const user = await collection.findOne(query);
    if (user) {
      const authenticated = bcryptjs
        .compareSync(credentials.pass, user.password);
      if (authenticated) {
        //console.log(`Authentication successful for username: ${user.username}`);
        jwt.sign({email: user.email, id: user._id, name: user.name, kind: user.kind}, process.env.TOKEN_SECRET, {}, (err, token) => {
          if(err) throw err;
          res.cookie('token', token).json(user)

      })
        // Store the user on the Request object.

      } else {
        message = `Authentication failure for username: ${user.username}`;
      }
    } else {
      message = `User not found for username: ${credentials.name}`;
    }
  } else {
    message = 'Auth header not found';
  }

  if (message) {
    console.warn(message);
    res.status(401).json({ message: 'Access Denied' });
  } else {
    next();
  }
};


// Route that returns the current authenticated user.
router.get('/users', authenticateUser, (req, res) => {
  const user = req.currentUser;

  res.json({
    name: user.name,
    username: user.username,
  });
});

// Route that creates a new user.
router.post('/users', [
  check('name')
    .exists({ checkNull: true, checkFalsy: true })
    .withMessage('Please provide a value for "name"'),
  check('username')
    .exists({ checkNull: true, checkFalsy: true })
    .withMessage('Please provide a value for "username"'),
  check('password')
    .exists({ checkNull: true, checkFalsy: true })
    .withMessage('Please provide a value for "password"'),
  check('email')
    .exists({ checkNull: true, checkFalsy: true })
    .withMessage('Please provide a value for "email"'),
  check('kind')
    .exists({ checkNull: true, checkFalsy: true })
    .withMessage('Please provide a value for "kind"'),
], async (req, res) => {
  // Attempt to get the validation result from the Request object.
  const errors = validationResult(req);

  // If there are validation errors...
  if (!errors.isEmpty()) {
    // Use the Array `map()` method to get a list of error messages.
    const errorMessages = errors.array().map(error => error.msg);

    // Return the validation errors to the client.
    return res.status(400).json({ errors: errorMessages });
  }

  // Get the user from the request body.
  const user = req.body;

  // Hash the new user's password.
  user.password = bcryptjs.hashSync(user.password);
  const collection = await getCollection();
  const result = await collection.insertOne(user);

  // Add the user to the `users` array.
  users.push(user);

  // Set the status to 201 Created and end the response.
  return res.status(201).end();
});

export default router;
import { createContext, useState } from "react";
import Cookies from "js-cookie";
import { api } from "../utils/apiHelper.js";
import connectToDatabase from '../api/db/connection.js';
import { ObjectId } from 'mongodb';

async function getCollection() {
  const db = await connectToDatabase();
  return db.collection('users');
}

const UserContext = createContext(null);

export const UserProvider = (props) => {
  const cookie = Cookies.get("authenticatedUser");
  const [authUser, setAuthUser] = useState(cookie ? JSON.parse(cookie) : null);

  const signIn = async (credentials, req, res) => {


    const response = await api("/users", "GET", null, credentials);
    if (response.status === 200){
      const collection = await getCollection();
      const query = { email: new ObjectId(req.params.email) };
      const user = await collection.findOne(query);
     setAuthUser(user);
    // Cookies.set('authenticatedUser', JSON.stringify(user), {expires: 1});
     return user
       } else if (response.status === 401) {
     return null
   } else {
     throw new Error();
   } 
  }

  const signOut = () => {
      setAuthUser(null);
      Cookies.remove("authenticatedUser");
  }

  return (
    <UserContext.Provider value={{
      authUser,
      actions: {
        signIn,
        signOut
      }
    }}>
      {props.children}
    </UserContext.Provider>
  );
}

export default UserContext;

1 Answer

Rohald van Merode
seal-mask
STAFF
.a{fill-rule:evenodd;}techdegree
Rohald van Merode
Treehouse Staff

Hey Killeon Patterson 👋

There are a couple of things going on here. Let's break down the error messages:

You attempted to import ../../db/connection.js which falls outside of the project src/ directory. 
Relative imports outside of src/ are not supported. 

When using Create React App you're not able to import anything from outside of the src directory. The src directory is considered the root directory for your application code. CRA is preconfigured to build and bundle the app, and it attempts to resolve modules within the src directory and its subdirectories during this process. When you try to import anything outside of this directory with a relative path, the bundling process will fail because it's outside of the scope of the bundler.

A little further down the error stack you're getting the error Module not found: Error: Can't resolve 'monbodb'. Here you're trying to access the mongodb package in your client-side code. But the mongodb package needs to be used on the server-side using Node.js, it only lives in your api directory. You'll want to keep the backend logic separate from the client project, and essentially treat them as two separate projects. This will

Instead of directly trying to access the MongoDB connection in the client-side code you should make API requests to your routes to perform database operations. You'll want to set up API endpoints on your Express server to handle any kind of authentication and data retrieval logic. Once those are setup you'll be able to update the functionalities in your UserContext.js to make API requests to the server to authenticate the user.

Hope this helps to get you going again 🙂

Killeon Patterson
Killeon Patterson
18,527 Points

Thank you. I was not aware of that setup with create React.