{"id":15110100,"url":"https://github.com/btc415/devconnector","last_synced_at":"2025-04-12T01:16:28.286Z","repository":{"id":255782494,"uuid":"853599563","full_name":"BTC415/DevConnector","owner":"BTC415","description":"DevConnector is a MERN stack application that serves as a social network for developers. It provides features such as authentication, user profiles, and forum posts.","archived":false,"fork":false,"pushed_at":"2024-09-30T02:13:58.000Z","size":457,"stargazers_count":10,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-12T01:16:12.846Z","etag":null,"topics":["express","gravatar","mongoatlas","mongodb","mongoose","nextjs14","nodejs","react","redux","redux-toolkit","tailwindcss","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BTC415.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-09-07T02:23:22.000Z","updated_at":"2025-04-03T07:26:33.000Z","dependencies_parsed_at":"2024-09-07T04:39:25.844Z","dependency_job_id":"28a668e1-8120-42bd-809a-587a684d73c3","html_url":"https://github.com/BTC415/DevConnector","commit_stats":null,"previous_names":["btc415/devconnector"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BTC415%2FDevConnector","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BTC415%2FDevConnector/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BTC415%2FDevConnector/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BTC415%2FDevConnector/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BTC415","download_url":"https://codeload.github.com/BTC415/DevConnector/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248501858,"owners_count":21114684,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["express","gravatar","mongoatlas","mongodb","mongoose","nextjs14","nodejs","react","redux","redux-toolkit","tailwindcss","typescript"],"created_at":"2024-09-25T23:40:57.403Z","updated_at":"2025-04-12T01:16:28.255Z","avatar_url":"https://github.com/BTC415.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DevConnector tutorial with BTC415 \u0026\u0026 Steven \n\n\u003e Social network for developers\n\nThis is a MERN stack application from the \"MERN Stack Front To Back\" course on [Udemy](https://www.udemy.com/mern-stack-front-to-back/?couponCode=TRAVERSYMEDIA). It is a small social network app that includes authentication, profiles and forum posts.\n\n# Updates since course published\n\nSuch is the nature of software; things change frequently, newer more robust paradigms emerge and packages are continuously evolving.\nHopefully the below will help you adjust your course code to manage the most notable changes.\n\nThe master branch of this repository contains all the changes and updates, so if you're following along with the lectures in the Udemy course and need reference code to compare against please checkout the [origionalcoursecode](https://github.com/bradtraversy/devconnector_2.0/tree/originalcoursecode) branch. Much of the code in this master branch is compatible with course code but be aware that if you adopt some of the changes here, it may require other changes too.\n\nAfter completing the course you may want to look through this branch and play about with the changes.\n\n## Changes to GitHub API authentication\n\nSince the course was published, GitHub has [deprecated authentication via URL query parameters](https://developer.github.com/changes/2019-11-05-deprecated-passwords-and-authorizations-api/#authenticating-using-query-parameters)\nYou can get an access token by following [these instructions](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line)\nFor this app we don't need to add any permissions so don't select any in the _scopes_.\n**DO NOT SHARE ANY TOKENS THAT HAVE PERMISSIONS**\nThis would leave your account or repositories vulnerable, depending on permissions set.\n\nIt would also be worth adding your `default.json` config file to `.gitignore`\nIf git has been previously tracking your `default.json` file then...\n\n```bash\ngit rm --cached config/default.json\n```\n\nThen add your token to the config file and confirm that the file is untracked with `git status` before pushing to GitHub.\nGitHub does have your back here though. If you accidentally push code to a repository that contains a valid access token, GitHub will revoke that token. Thanks GitHub 🙏\n\nYou'll also need to change the options object in `routes/api/profile.js` where we make the request to the GitHub API to...\n\n```javascript\nconst options = {\n  uri: encodeURI(\n    `https://api.github.com/users/${req.params.username}/repos?per_page=5\u0026sort=created:asc`\n  ),\n  method: 'GET',\n  headers: {\n    'user-agent': 'node.js',\n    Authorization: `token ${config.get('githubToken')}`\n  }\n};\n```\n\n### npm package request deprecated\n\nAs of 11th February 2020 [request](https://www.npmjs.com/package/request) has been deprecated and is no longer maintained.\nWe already use [axios](https://www.npmjs.com/package/axios) in the client so we can easily change the above fetching of a users GitHub repositories to use axios.\n\nInstall axios in the root of the project\n\n```bash\nnpm i axios\n```\n\nWe can then remove the frontend installation of axios.\n\n```bash\ncd frontend\nnpm uninstall axios\n```\n\nFrontend use of the axios module will be resolved in the root, so we can still use it in frontend.\n\nChange the above GitHub API request to..\n\n```javascript\nconst uri = encodeURI(\n  `https://api.github.com/users/${req.params.username}/repos?per_page=5\u0026sort=created:asc`\n);\nconst headers = {\n  'user-agent': 'node.js',\n  Authorization: `token ${config.get('githubToken')}`\n};\n\nconst gitHubResponse = await axios.get(uri, { headers });\n```\n\nYou can see the full change in [routes/api/profile.js](https://github.com/bradtraversy/devconnector_2.0/blob/4be414c6a54994c18397dba9c927ad67b878508b/routes/api/profile.js#L324)\n\n## uuid no longer has a default export\n\nThe npm package [uuid](https://www.npmjs.com/package/uuid) no longer has a default export, so in our [client/src/actions/alert.js](https://github.com/bradtraversy/devconnector_2.0/blob/master/client/src/actions/alert.js) we need to change the import and use of this package.\n\nchange\n\n```javascript\nimport uuid from 'uuid';\n```\n\nto\n\n```javascript\nimport { v4 as uuidv4 } from 'uuid';\n```\n\nAnd where we use it from\n\n```javascript\nconst id = uuid();\n```\n\nto\n\n```javascript\nconst id = uuidv4();\n```\n\n## Addition of normalize-url package 🌎\n\nDepending on what a user enters as their website or social links, we may not get a valid clickable url.\nFor example a user may enter _**traversymedia.com**_ or _**www.traversymedia.com**_ which won't be a clickable valid url in the UI on the users profile page.\nTo solve this we brought in [normalize-url](https://www.npmjs.com/package/normalize-url) to well.. normalize the url.\n\nRegardless of what the user enters it will ammend the url accordingly to make it valid (assuming the site exists).\nYou can see the use here in [routes/api/profile.js](https://github.com/bradtraversy/devconnector_2.0/blob/31e5318592b886add58923c751dba73274c711de/routes/api/profile.js#L71)\n\n## Fix broken links in gravatar 🔗\n\nThere is an unresolved [issue](https://github.com/emerleite/node-gravatar/issues/47) with the [node-gravatar](https://github.com/emerleite/node-gravatar#readme) package, whereby the url is not valid. Fortunately we added normalize-url so we can use that to easily fix the issue. If you're not seeing Gravatar avatars showing in your app then most likely you need to implement this change.\nYou can see the code use here in [routes/api/users.js](https://github.com/bradtraversy/devconnector_2.0/blob/master/routes/api/users.js#L44)\n\n## Redux subscription to manage local storage 📥\n\nThe rules of redux say that our [reducers should be pure](https://redux.js.org/basics/reducers#handling-actions) and do just one thing.\n\nIf you're not familiar with the concept of pure functions, they must do the following..\n\n1. Return the same output given the same input.\n2. Have no side effects.\n\nSo our reducers are not the best place to manage local storage of our auth token.\nIdeally our action creators should also just dispatch actions, nothing else. So using these for additional side effects like setting authentication headers is not the best solution here.\n\nRedux provides us with a [`store.subscribe`](https://redux.js.org/api/store#subscribelistener) listener that runs every time a state change occurs.\n\nWe can use this listener to **_watch_** our store and set our auth token in local storage and axios headers accordingly.\n\n- if there is a token - store it in local storage and set the headers.\n- if there is no token - token is null - remove it from storage and delete the headers.\n\nThe subscription can be seen in [client/src/store.js](https://github.com/bradtraversy/devconnector_2.0/blob/master/client/src/store.js)\n\nWe also need to change our [client/src/utils/setAuthToken.js](https://github.com/bradtraversy/devconnector_2.0/blob/master/client/src/utils/setAuthToken.js) so it now handles both the setting of the token in local storage and in axios headers.\n`setauthToken.js` in turn depends on [client/src/utils/api.js](https://github.com/bradtraversy/devconnector_2.0/blob/master/client/src/utils/api.js) where we create an instance of axios. So you will also need to grab that file.\n\nWith those two changes in place we can remove all setting of local storage from [client/src/reducers/auth.js](https://github.com/bradtraversy/devconnector_2.0/blob/master/client/src/reducers/auth.js). And remove setting of the token in axios headers from [client/src/actions/auth.js](https://github.com/bradtraversy/devconnector_2.0/blob/master/client/src/actions/auth.js). This helps keep our code predictable, manageable and ultimately bug free.\n\n## Component reuse ♻️\n\nThe EditProfile and CreateProfile have been reduced to one component [ProfileForm.js](https://github.com/bradtraversy/devconnector_2.0/blob/master/client/src/components/profile-forms/ProfileForm.js)  \nThe majority of this logic came from the refactrored EditProfile Component, which was initially changed to fix the issues with the use of useEffect we see in this component.\n\nIf you want to address the linter warnings in EditProfile then this is the component you are looking for.\n\n## Log user out on token expiration 🔐\n\nIf the Json Web Token expires then it should log the user out and end the authentication of their session.\n\nWe can do this using a [axios interceptor](https://github.com/axios/axios#interceptors) together paired with creating an instance of axios.  \nThe interceptor, well... intercepts any response and checks the response from our api for a `401` status in the response.  \nie. the token has now expired and is no longer valid, or no valid token was sent.  \nIf such a status exists then we log out the user and clear the profile from redux state.\n\n**You can see the implementation of the interceptor and axios instance in [utils/api.js](https://github.com/bradtraversy/devconnector_2.0/blob/master/client/src/utils/api.js)**\n\nCreating an instance of axios also cleans up our action creators in [actions/auth.js](https://github.com/bradtraversy/devconnector_2.0/blob/master/client/src/actions/auth.js), [actions/profile.js](https://github.com/bradtraversy/devconnector_2.0/blob/master/client/src/actions/profile.js) and [actions/post.js](https://github.com/bradtraversy/devconnector_2.0/blob/master/client/src/actions/post.js)\n\nNote that implementing this change also requires that you use the updated code in [utils/setAuthToken.js](https://github.com/bradtraversy/devconnector_2.0/blob/master/client/src/utils/setAuthToken.js)\nWhich also in turn depends on [utils/api.js](https://github.com/bradtraversy/devconnector_2.0/blob/master/client/src/utils/api.js)\nI would also recommending updating to use a [ redux subscription ](https://github.com/bradtraversy/devconnector_2.0#redux-subscription-to-manage-local-storage-) to mange setting of the auth token in headers and local storage.\n\n## Remove Moment 🗑️\n\nAs some of you may be aware, [Moment.js](https://www.npmjs.com/package/moment) which [ react-moment ](https://www.npmjs.com/package/react-moment) depends on has since become _legacy code_.\\\nThe maintainers of Moment.js now recommend finding an alternative to their package.\n\n\u003e Moment.js is a legacy project, now in maintenance mode.\\\n\u003e  In most cases, you should choose a different library.\\\n\u003e  For more details and recommendations, please see Project Status in the docs.\\\n\u003e  Thank you.\n\nSome of you in the course have been having problems installing both packages and meeting peer dependencies.\\\n We can instead use the browsers built in [Intl](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) API.\\\n First create a [ utils/formatDate.js ](https://github.com/BTC415/DevConnector/blob/master/client/src/utils/formatDate.js) file, with the following code...\n\n```javascript\nfunction formatDate(date) {\n  return new Intl.DateTimeFormat().format(new Date(date));\n}\n\nexport default formatDate;\n```\n\nThen in our [Education.js](https://github.com/BTC415/DevConnector/blob/master/client/src/components/dashboard/Education.js) component, import the new function...\n\n```javascript\nimport formatDate from '../../utils/formatDate';\n```\n\nAnd use it instead of Moment...\n\n```jsx\n\u003ctd\u003e\n  {formatDate(edu.from)} - {edu.to ? formatDate(edu.to) : 'Now'}\n\u003c/td\u003e\n```\n\nSo wherever you use `\u003cMoment /\u003e` you can change to use the `formatDate` function.\\\nFiles to change would be...\n\n- [Education.js](https://github.com/BTC415/DevConnector/blob/master/client/src/components/dashboard/Education.js)\n- [Experience.js](https://github.com/BTC415/DevConnector/blob/master/client/src/components/dashboard/Experience.js)\n- [CommentItem.js](https://github.com/BTC415/DevConnector/blob/master/client/src/components/post/CommentItem.js)\n- [PostItem.js](https://github.com/BTC415/DevConnector/blob/master/client/src/components/posts/PostItem.js)\n- [ProfileEducation.js](https://github.com/BTC415/DevConnector/blob/master/client/src/components/profile/ProfileEducation.js)\n- [ProfileExperience.js](https://github.com/BTC415/DevConnector/blob/master/client/src/components/profile/ProfileExperience.js)\n\nIf you're updating your project you will now be able to uninstall **react-moment** and **moment** as project dependencies.\n\n## React Router V6 🧭\n\nSince the course was released [React Router](https://reactrouter.com) has been updated to version 6\nwhich includes some breaking changes.\nYou can see the official migration guide from version 5 [ here ](https://reactrouter.com/docs/en/v6/upgrading/v5).\n\n### To summarize the changes to the course code\n\nInstead of a `\u003cSwitch /\u003e` we now use a [ `\u003cRoutes /\u003e` ](https://reactrouter.com/docs/en/v6/api#routes-and-route) component.\n\nThe [ `\u003cRoute /\u003e` ](https://reactrouter.com/docs/en/v6/api#routes-and-route) component no longer receives a **_component_** prop, instead we\npass a **_element_** prop which should be a React element i.e. JSX. Routing is\nalso now relative to the component.\n\nFor redirection and Private routing we can no longer use `\u003cRedirect /\u003e`, we now\nhave available a [ `\u003cNavigate /\u003e` ](https://reactrouter.com/docs/en/v6/api#navigate) component.\n\nWe no longer have access to the **_match_** and **_history_** objects in our\ncomponent props. Instead of the match object for routing parameters we can use\nthe [**useParams**](https://reactrouter.com/docs/en/v6/api#useparams) hook, and in place of using the history object to _push_\nonto the router we can use the [**useNavigate**](https://reactrouter.com/docs/en/v6/api#usenavigate) hook.\n\nThe above changes do actually clean up the routing considerably with all\napplication routing in one place in [App.js](client/src/App.js).\nOur [PrivateRoute](client/src/components/routing/PrivateRoute.js) is a good deal\nsimpler now and no longer needs to use a render prop.\n\nWith moving all of the routing to App.js this did affect the styling as all\nroutes needed to be inside the original `\u003csection className=\"container\"\u003e`.\nTo solve this each page component in App.js (any child of a `\u003cRoute /\u003e`) gets\nwrapped in it's own `\u003csection className=\"container\"\u003e`, So we no longer need that\nin App.js. In most cases this just replaces the outer `\u003cFragment /\u003e` in the\ncomponent.\n\nThe styling also affected the [ `\u003cAlert /\u003e`\n](client/src/components/layout/Alert.js) component as this will show in\naddition to other page components adding it's own `\u003csection\u003e` would mean extra\ncontent shift when the alerts show. To solve this the alerts have been given\ntheir [ own styling ](https://github.com/bradtraversy/devconnector_2.0/blob/c5b1fc48ccfecf30b6ed85f228a337f82d93e3f9/client/src/App.css#L579) so they are `position: fixed;` and we get no content shift,\nwhich additionally makes for a smoother UI with the alerts popping up in the top\nright of the screen.\n\n---\n\n# Quick Start 🚀\n\n### Add a default.json file in config folder with the following\n\n```json\n{\n  \"mongoURI\": \"\u003cyour_mongoDB_Atlas_uri_with_credentials\u003e\",\n  \"jwtSecret\": \"secret\",\n  \"githubToken\": \"\u003cyoursecrectaccesstoken\u003e\"\n}\n```\n\n### Install server dependencies\n\n```bash\nnpm install\n```\n\n### Install frontend dependencies\n\n```bash\ncd frontend\nnpm install\n```\n\n### Run both Express \u0026 React from root\n\n```bash\nnpm run dev\n```\n\n### Build for production\n\n```bash\ncd frontend\nnpm run build\n```\n\n### Test production before deploy\n\nAfter running a build in the frontend 👆, cd into the root of the project.  \nAnd run...\n\nLinux/Unix\n\n```bash\nNODE_ENV=production node server.js\n```\n\nWindows Cmd Prompt or Powershell\n\n```bash\n$env:NODE_ENV=\"production\"\nnode server.js\n```\n\nCheck in browser on [http://localhost:5000/](http://localhost:5000/)\n\n### Deploy to Heroku\n\nIf you followed the sensible advice above and included `config/default.json` and `config/production.json` in your .gitignore file, then pushing to Heroku will omit your config files from the push.  \nHowever, Heroku needs these files for a successful build.  \nSo how to get them to Heroku without commiting them to GitHub?\n\nWhat I suggest you do is create a local only branch, lets call it _production_.\n\n```bash\ngit checkout -b production\n```\n\nWe can use this branch to deploy from, with our config files.\n\nAdd the config file...\n\n```bash\ngit add -f config/production.json\n```\n\nThis will track the file in git on this branch only. **DON'T PUSH THE PRODUCTION BRANCH TO GITHUB**\n\nCommit...\n\n```bash\ngit commit -m 'ready to deploy'\n```\n\nCreate your Heroku project\n\n```bash\nheroku create\n```\n\nAnd push the local production branch to the remote heroku main branch.\n\n```bash\ngit push heroku production:main\n```\n\nNow Heroku will have the config it needs to build the project.\n\n\u003e **Don't forget to make sure your production database is not whitelisted in MongoDB Atlas, otherwise the database connection will fail and your app will crash.**\n\nAfter deployment you can delete the production branch if you like.\n\n```bash\ngit checkout main\ngit branch -D production\n```\n\nOr you can leave it to merge and push updates from another branch.  \nMake any changes you need on your main branch and merge those into your production branch.\n\n```bash\ngit checkout production\ngit merge main\n```\n\nOnce merged you can push to heroku as above and your site will rebuild and be updated.\n\n---\n\n### Version\n\n2.0.0\n\n### License\n\nThis project is licensed under the MIT License","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbtc415%2Fdevconnector","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbtc415%2Fdevconnector","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbtc415%2Fdevconnector/lists"}