<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kati Frantz</title>
    <description>The latest articles on DEV Community by Kati Frantz (@bahdcoder).</description>
    <link>https://dev.to/bahdcoder</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F54584%2F6c2417ee-7bfc-4d9b-a1ad-f29be3cefbce.jpeg</url>
      <title>DEV Community: Kati Frantz</title>
      <link>https://dev.to/bahdcoder</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bahdcoder"/>
    <language>en</language>
    <item>
      <title>7 things to make your portfolio projects stand out and make any employer hire you</title>
      <dc:creator>Kati Frantz</dc:creator>
      <pubDate>Wed, 06 Oct 2021 07:53:12 +0000</pubDate>
      <link>https://dev.to/bahdcoder/7-things-to-make-your-portfolio-projects-stand-out-and-make-any-employer-hire-you-17gb</link>
      <guid>https://dev.to/bahdcoder/7-things-to-make-your-portfolio-projects-stand-out-and-make-any-employer-hire-you-17gb</guid>
      <description>&lt;p&gt;You do not need any job experience to land an amazing developer job.&lt;/p&gt;

&lt;p&gt;7 things to make your portfolio projects stand out and make any employer hire you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;🏘️ Build projects with real-world use cases and solutions. Consider yourself a contractor, and someone came to you with a real problem, asking for a real solution. Build it and launch it like you are working for a real business. Deploy it, purchase a domain, make it live and functional. Add it to your resume as prior experience. Make the code open-source. Do 7 of these, and not only your skills, but your experience itself will shoot to the roof.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;💅 No ugly projects. Build a beautiful, fully-branded solution. For example, you are building an airbnb clone. Don't call it that. Give the company a name, for example, Vacasa. Go to ui8.net, buy a Figma design for the kind of project you are building. Implement the design from scratch. Make it perfectly responsive, create a logo for the project. How do you know you did a great job? If someone sees the website online, they should be sold and think this is a legit existing business.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;📝Document your project completely. Write a useful and fully detailed case study. Explain why you used x framework or y technology. This explanation should be in the project readme, or be part of your projects on your website. Your documentation should have the designs for the project, explanation of design patterns used, project architecture, how it was deployed, development problems you ran into, and a list of all features and functionality.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🛠️ Your code should check all the boxes for production grade software: Accessibility, testability, scalability and maintainability. Write semantic HTML, create accessible components, fully test that your code works on both the backend and frontend, make sure its Easily maintainable by any developer, clean code best practices, fully typed, can scale to millions of users without performance drops, and beautifully structured and organized code. You can ask for feedback and make as many tweaks as needed to get the perfect code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🚀 Collaborate with other developers. Create a 3-4 people team, distribute tasks, work together to complete the project. Full history of collaboration should be on Github and a public Trello board. Show your employer you have team experience, and can fit into their team.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;👓 Take some time to find a very experienced developer to review your code on Github, provide Github PR reviews, and give you the best tips to make your solution the best quality possible. If you can't find someone willing to do this for you, pay for their time. I promise you its worth it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;📻 My final tip is a list of possible projects you can build that will match the quality I am talking about:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;i. &lt;a href="//circle.so"&gt;circle.so&lt;/a&gt;&lt;br&gt;
ii. &lt;a href="//udemy.com"&gt;udemy.com&lt;/a&gt;&lt;br&gt;
iii. &lt;a href="//tinder.com"&gt;tinder.com&lt;/a&gt;&lt;br&gt;
iv. &lt;a href="//ui8.net"&gt;ui8.net&lt;/a&gt;&lt;br&gt;
v. &lt;a href="//reform.app"&gt;reform.app&lt;/a&gt;&lt;br&gt;
vi. &lt;a href="//attract.io"&gt;attract.io&lt;/a&gt;&lt;br&gt;
vii. Implement a design system from scratch. Pick any one on Figma and write the code for it. Make it open-source. I got my eyes on &lt;a href="//sargam.design"&gt;sargam.design&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Please share this content and help someone in need of a guide.&lt;/p&gt;

&lt;p&gt;Did you love this content? You can get even much more by subscribing for free to my email list . No spam, just tons and tons of value for your career 👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://katifrantz.com/newsletter"&gt;https://katifrantz.com/newsletter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>developer</category>
      <category>portfolio</category>
      <category>career</category>
    </item>
    <item>
      <title>How to move from mid-level developer to senior engineer in 2021</title>
      <dc:creator>Kati Frantz</dc:creator>
      <pubDate>Thu, 14 Jan 2021 13:16:06 +0000</pubDate>
      <link>https://dev.to/bahdcoder/how-to-move-from-mid-level-developer-to-senior-engineer-in-2021-1np6</link>
      <guid>https://dev.to/bahdcoder/how-to-move-from-mid-level-developer-to-senior-engineer-in-2021-1np6</guid>
      <description>&lt;p&gt;I've spent a lot of time leading teams, training over 65 thousand developers via online courses and mentoring dozens. Here's a guide on how to move from mid-level developer to senior engineer in 2021:&lt;/p&gt;

&lt;h3&gt;
  
  
  Start mentoring someone today.
&lt;/h3&gt;

&lt;p&gt;Show empathy, support and a strong desire to see this person succeed. Find someone who is just starting out, make sure they don’t make the mistakes you made. Get on a consistent schedule to have video calls, check up on their progress, pain points, and be there for them. And yes, don't doubt yourself. You have what it takes! You can do this!&lt;/p&gt;

&lt;h3&gt;
  
  
  The value you provide needs to go beyond code.
&lt;/h3&gt;

&lt;p&gt;Get closer to the business. Schedule a call with your CEO now. Ask questions about the future of the business, the goals for the year. Get involved. Do market research about your company’s industry. Do this every week, attend marketing calls, make sure engineering is working towards the goals of the company. Love the business. Care for the business. Attend marketing calls, peaching calls, or watch past ones. Working at a large company ? Get super involved with the role of your team. &lt;/p&gt;

&lt;h3&gt;
  
  
  Level up technically.
&lt;/h3&gt;

&lt;p&gt;Don't learn more frameworks. Master the ones you're already using. Do something more advanced. Watch technical workshops. Every day, dive into the code and find those things that are making the business lose money (Performance, poor design, Bugs). Pay incredible attention to detail when writing code. Creating an endpoint to fetch data ? Don't end at fetching data. Handle 401s, 404s, data validation, sanitisation, rate limiting, caching, query performance, clean code, documentation. Every Single Detail Matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Become a master of soft skills.
&lt;/h3&gt;

&lt;p&gt;Your coding skills can only take you half way there. The higher you go, the more people skills you need. Communicate excessively. Every single detail matters. Have a deep empathy for the business you work for, and the people you collaborate with everyday. Put yourself in their shoes. Be open minded. Accept feedback joyfully from those you mentor, those you report to, and be willing to grow from it. The more people skills you have, the more doors you can open.&lt;/p&gt;

&lt;p&gt;🚀 Did you find this impactful? Consider &lt;a href="https://twitter.com/bahdcoder"&gt;following me on twitter&lt;/a&gt; for more nuggets, tips and guides to grow in your career as a software engineer.&lt;/p&gt;

</description>
      <category>career</category>
      <category>software</category>
      <category>engineer</category>
    </item>
    <item>
      <title>The Ultimate Guide to JWT server-side auth (with refresh tokens)</title>
      <dc:creator>Kati Frantz</dc:creator>
      <pubDate>Mon, 30 Nov 2020 06:16:02 +0000</pubDate>
      <link>https://dev.to/bahdcoder/the-ultimate-guide-to-jwt-server-side-auth-with-refresh-tokens-4jb3</link>
      <guid>https://dev.to/bahdcoder/the-ultimate-guide-to-jwt-server-side-auth-with-refresh-tokens-4jb3</guid>
      <description>&lt;p&gt;Hello, my name is Kati Frantz, and thank you so much for checking out this tutorial. I want to talk about how to handle JWTs effectively and securely on the server-side.&lt;/p&gt;

&lt;p&gt;Most tutorials only cover one part, issuing the access token, but this is only part of the solution and can be very insecure in a production application. Let's start by understanding the authentication strategy and learn how best to implement it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding access tokens and refresh tokens
&lt;/h3&gt;

&lt;p&gt;Let's take an example application, a social networking mobile app. We have two goals here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Maintain the user's login status for as long as possible, with no interruptions and a great user experience. For example, I've been logged in to Instagram for about two years now.&lt;/li&gt;
&lt;li&gt;We should make sure providing a great user experience to the user does not compromise on security.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's start with the first goal, a forever login. When the user downloads our application, they register a new account or log in to an existing account. The API for our mobile app returns an access token, which could be a JWT. Since we want the user to be logged in forever, we set the token expiry to 10 years. When the user wants to fetch their feed, search users, or perform any authenticated requests to the API, the mobile app sends along with this access token.&lt;/p&gt;

&lt;p&gt;Great, this solves the first goal. Now let's talk about the second goal. Security. If an attacker takes possession of the access token (and trust me, they can), we have a huge security problem because they have access to the user's account for the next 10 years.&lt;/p&gt;

&lt;h4&gt;
  
  
  Refresh tokens
&lt;/h4&gt;

&lt;p&gt;We can improve our application security to make it very tough for an attacker to hijack the access token, but we can never be completely secure. The best way to protect the user now is to make sure the access token is as short as possible. 10 minutes is ideal. Depending on how safe and secure your mobile app or browser client is, you can increase this time.&lt;/p&gt;

&lt;p&gt;Now we have a short-lived access token, it's only valid for 10 minutes, which means if an attacker takes possession of the token, their access expires in 10 minutes or less. But again, this breaks our first condition. If the user's access expires every 10 minutes and they have to login again, the user experience is very poor.&lt;/p&gt;

&lt;p&gt;This is where refresh tokens come in. When the user logs in, our API returns two tokens, an access token, and a refresh token. The access token expires in 10 minutes, and the refresh token expires in 5 years.&lt;/p&gt;

&lt;p&gt;This refresh token does not grant access to the API but can be used to request a new access token. After 10 minutes of usage, a few seconds before the user's session expires, we make an API call in the background to the API, sending the refresh token.&lt;/p&gt;

&lt;p&gt;The API identifies and authenticates this refresh token, and returns a new access token to the mobile app which expires in 10 minutes.&lt;/p&gt;

&lt;p&gt;Great. We've solved the first goal, and the user experience is back. Security is partially solved. Since we send the refresh token over the network, it becomes a little more difficult for a hijacker to get a hold of the refresh token.&lt;/p&gt;

&lt;p&gt;We are not completely safe yet. If they ever hijacked the refresh token, we're back to the same problem, because the attacker can now generate new access tokens. If your application has very good security to store the refresh token with a very low possibility of being compromised, then there's no need to fear. If not, such as in-browser environments, we need another way to secure refresh tokens.&lt;/p&gt;

&lt;h4&gt;
  
  
  Refresh token rotation
&lt;/h4&gt;

&lt;p&gt;The Internet Engineering Task Force suggests using a technique called refresh token rotation to secure refresh tokens. You can view the &lt;a href="https://tools.ietf.org/html/draft-ietf-oauth-browser-based-apps-05#section-8" rel="noopener noreferrer"&gt;details of the draft here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, every time the user authenticates, we generate new access and refresh tokens and return to the mobile app. We also persist the new refresh token to the database.&lt;/p&gt;

&lt;p&gt;Whenever the mobile app requests our backend with the refresh token to get a new access token, we'll generate a new refresh token and save it to a database. Next, we'll invalidate the refresh token that was just used.&lt;/p&gt;

&lt;p&gt;This means the mobile app can only use a refresh token once. In the event where an attacker gains access to the refresh token and attempt to use it, the backend automatically detects this, notices the token has already been used and immediately blocks the user's account. &lt;/p&gt;

&lt;p&gt;Now if the attacker uses the refresh token before the mobile app does, in less than ten minutes after hijacking the refresh token, the mobile app attempts a refresh, and this also results in the user's account being blocked, so we've protected both ways.&lt;/p&gt;

&lt;p&gt;At this point, the API notifies support that a user's credentials have been compromised, and once we figure out and patch any security issues, we can unblock the user's account and ask them to re-authenticate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fbahdcoder%2Fimage%2Fupload%2Fv1606712432%2Frefresh-token-rotation-flow.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fbahdcoder%2Fimage%2Fupload%2Fv1606712432%2Frefresh-token-rotation-flow.png" alt="diagram showing refresh token rotation flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a secure server-side JWT authentication with refresh tokens
&lt;/h3&gt;

&lt;p&gt;If you want this functionality out of the box with absolutely no effort, you can run &lt;code&gt;yarn create tensei-app my-app&lt;/code&gt; and get a fresh new project. The project has less than 18 lines of code and implements this backend architecture for you. Let's look at some code snippets from the &lt;a href="https://tenseijs.com" rel="noopener noreferrer"&gt;tensei&lt;/a&gt; codebase to see how this is done. &lt;/p&gt;

&lt;p&gt;We need two database tables: &lt;code&gt;users&lt;/code&gt; and &lt;code&gt;tokens&lt;/code&gt;. The &lt;code&gt;users&lt;/code&gt; table has the standard fields we need for authentication such as &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt;. The &lt;code&gt;tokens&lt;/code&gt; table has the &lt;code&gt;token&lt;/code&gt;, &lt;code&gt;expires_at&lt;/code&gt;, &lt;code&gt;last_used_at&lt;/code&gt; and &lt;code&gt;user_id&lt;/code&gt; fields. The &lt;code&gt;last_used_at&lt;/code&gt; field would help us know if a token has already been used once to acquire an access token before.&lt;/p&gt;

&lt;p&gt;First, a user attemps to login. This is how the login controller looks:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

    &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;email&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authenticationError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid credentials.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Check if the user's account has been blocked. The user can be automatically blocked &lt;/span&gt;
        &lt;span class="c1"&gt;// if data compromise is detected.&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;blocked_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forbiddenError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your account is temporarily disabled.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;Bcrypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compareSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authenticationError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid credentials.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUserPayload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getUserPayload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateJwt&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
            &lt;span class="p"&gt;}),&lt;/span&gt;
            &lt;span class="na"&gt;refresh_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateRefreshToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="na"&gt;expires_in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokensConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessTokenExpiresIn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;generateRefreshToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;plainTextToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateRandomToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;// Expire all existing refresh tokens for this customer.&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nativeUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="na"&gt;expires_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Dayjs&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;second&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
           &lt;span class="c1"&gt;// Also mark unused refresh token as used, in case the user logged in twice and got more than one&lt;/span&gt;
           &lt;span class="c1"&gt;// refresh token at a time&lt;/span&gt;
           &lt;span class="na"&gt;last_used_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Dayjs&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;second&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;plainTextToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TokenTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REFRESH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;expires_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Dayjs&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokensConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refreshTokenExpiresIn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;second&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;persistAndFlush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;plainTextToken&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;generateJwt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokensConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;secretKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokensConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessTokenExpiresIn&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
A few moments after we send the access and refresh tokens to the user, the mobile application attempts to use the refresh token to get a new access token:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

    &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;handleRefreshTokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;refreshToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refresh_token&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;refreshToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authenticationError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid refresh token.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;refreshToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TokenTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REFRESH&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authenticationError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid refresh token.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;last_used_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// This token has been used before.&lt;/span&gt;
            &lt;span class="c1"&gt;// We'll block the user's access to the API by marking this refresh token as compromised.&lt;/span&gt;
            &lt;span class="c1"&gt;// Human interaction is required to lift this limit, something like deleting the compromised tokens.&lt;/span&gt;

            &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;compromised_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Dayjs&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;

            &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;blocked_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Dayjs&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;

            &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;persist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;persist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authenticationError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid refresh token.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nc"&gt;Dayjs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expires_on&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;isBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Dayjs&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeAndFlush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authenticationError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid refresh token.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;last_used_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Dayjs&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="na"&gt;expires_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Dayjs&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;second&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;persistAndFlush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUserPayload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;So there you have it, how to implement refresh tokens and refresh token rotation in your application to ensure maximum security. A good thing to do is make sure your application's needs fit the security measures you are taking. &lt;/p&gt;

&lt;p&gt;Thank you very much for reading this far 🎉.&lt;/p&gt;

&lt;p&gt;If you've found this useful, please &lt;a href="https://twitter.com/bahdcoder" rel="noopener noreferrer"&gt;follow me on Twitter&lt;/a&gt;, and &lt;a href="https://katifrantz.com" rel="noopener noreferrer"&gt;subscribe to my newsletter&lt;/a&gt; to be instantly notified when I share a new post.&lt;/p&gt;

</description>
      <category>node</category>
      <category>jwt</category>
    </item>
    <item>
      <title>The Ultimate Guide to JWT client side auth (Stop using local storage!!!)</title>
      <dc:creator>Kati Frantz</dc:creator>
      <pubDate>Mon, 23 Nov 2020 04:41:46 +0000</pubDate>
      <link>https://dev.to/bahdcoder/the-ultimate-guide-to-jwt-client-side-auth-stop-using-local-storage-3an9</link>
      <guid>https://dev.to/bahdcoder/the-ultimate-guide-to-jwt-client-side-auth-stop-using-local-storage-3an9</guid>
      <description>&lt;p&gt;Hello, my name is Kati Frantz, and thank you so much for checking out this tutorial. I want to talk about how to handle JWTs effectively and securely on the client-side. &lt;/p&gt;

&lt;p&gt;The most popular practice in the industry today is to save your JWT in a cookie or local storage. I've done this for a couple of years, and I have even taught others to do the same, but I didn't think it was a big deal until one of the applications I worked on was hacked. &lt;/p&gt;

&lt;p&gt;This was an &lt;code&gt;XSS&lt;/code&gt; attack. This is an attack in which a malicious person runs malicious code on the client's browser directly attacking your application. &lt;/p&gt;

&lt;p&gt;Now, they could do this to get access to local storage or cookies and extract the JWT from there.&lt;/p&gt;

&lt;p&gt;These tokens used in sessions are usually long-lived, and the attackers can get access to your API for a very long time. &lt;/p&gt;

&lt;p&gt;The solution we want to talk about today is one that would, first of all, prevent us from saving our tokens in a risky place, and secondly, implementing another solution that makes sure even if the attacker manages to get hold of a token, the access to the API would expire almost immediately.&lt;/p&gt;

&lt;p&gt;Let's get started.&lt;/p&gt;

&lt;p&gt;For this tutorial, the first thing we need is a real project. I have set up a &lt;a href="https://github.com/bahdcoder/jwt-best-practices" rel="noopener noreferrer"&gt;sample project&lt;/a&gt; with user registration, login, and logout. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/api&lt;/code&gt; folder has a fully-featured graphql and auth server using just 20 lines of &lt;a href="https://github.com/tenseijs/tensei.git" rel="noopener noreferrer"&gt;Tensei.js&lt;/a&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@tensei/auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tensei&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@tensei/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;graphql&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@tensei/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;tensei&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nf"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Customer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nf"&gt;graphql&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;middlewareOptions&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;databaseConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sqlite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;dbName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tensei.sqlite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;/client&lt;/code&gt; folder is a React.js project generated with &lt;a href="https://create-react-app.dev" rel="noopener noreferrer"&gt;create react app&lt;/a&gt;. We have three routes: &lt;code&gt;Login&lt;/code&gt;, &lt;code&gt;Register&lt;/code&gt;, and &lt;code&gt;Dashboard&lt;/code&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  User registration
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fbahdcoder%2Fimage%2Fupload%2Fv1606002630%2Fregister-page-jwt-tutorial.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fbahdcoder%2Fimage%2Fupload%2Fv1606002630%2Fregister-page-jwt-tutorial.png" alt="registe-page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When a user registers a new account, we make a request to the backend to get a JWT so we can automatically login the customer. At this point, this is usually when we'll set the JWT to local storage, but we won't be doing that. Here's the implementation of the register function:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;client&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="na"&gt;register_customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nf"&gt;setCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We do not set the &lt;code&gt;token&lt;/code&gt; to local storage, but we save it in memory. Here, we're setting it on the HTTP client so we can make subsequent authenticated requests to the API.&lt;/p&gt;

&lt;p&gt;Next, we set the customer and redirect to the dashboard.&lt;/p&gt;

&lt;p&gt;There's something very important that happens when we receive a response from the backend. Let's have a look at the backend response:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fbahdcoder%2Fimage%2Fupload%2Fv1606003553%2FScreenshot_2020-11-22_at_1.05.20_AM_imp0pr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fbahdcoder%2Fimage%2Fupload%2Fv1606003553%2FScreenshot_2020-11-22_at_1.05.20_AM_imp0pr.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fbahdcoder%2Fimage%2Fupload%2Fv1606003552%2FScreenshot_2020-11-22_at_1.05.33_AM_emerbc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fbahdcoder%2Fimage%2Fupload%2Fv1606003552%2FScreenshot_2020-11-22_at_1.05.33_AM_emerbc.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The backend set's an &lt;code&gt;HttpOnly&lt;/code&gt; cookie called &lt;code&gt;___refresh_token&lt;/code&gt; on the response. This cookie has the unique property of not being accessible from the client-side. This means if you run &lt;code&gt;document.cookie&lt;/code&gt; in the developer console, you won't see the &lt;code&gt;___refresh_token&lt;/code&gt; cookie. &lt;/p&gt;

&lt;p&gt;This is because an &lt;code&gt;HttpOnly&lt;/code&gt; cookie can only be exchanged with the server, and cannot be accessed using client-side javascript.&lt;/p&gt;

&lt;p&gt;Using this kind of cookie to set the refresh token gives us additional security, and assurance that the token can't fall into the wrong hands.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding refresh tokens
&lt;/h3&gt;

&lt;p&gt;The token we received in the JSON response from the API is an access token. This type of token gives the customer access to the API resources. &lt;/p&gt;

&lt;p&gt;An access token should expire in about 10 to 15 minutes so that if it falls into the wrong hands, it becomes invalid as soon as possible.&lt;/p&gt;

&lt;p&gt;A refresh token on the other hand does not give access. Instead, it can be used to request a new access token. That way, before the access token expires, you can silently request a new access token to keep your customers logged in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling silent refresh
&lt;/h3&gt;

&lt;p&gt;After registration, the customer is redirected to the dashboard, and they can access the dashboard because they are logged in. What happens when she refreshes the page or opens the app in a new tab? &lt;/p&gt;

&lt;p&gt;Well, since we only set the token in memory, the customer loses access and is redirected to the sign-in page instead.&lt;/p&gt;

&lt;p&gt;This is not pleasant, and we need to persist the customer's session somehow. &lt;/p&gt;

&lt;p&gt;That's where a silent refresh comes in. Before actually redirecting the customer to the sign-in screen, we need to check if the user has an active session. We do this by calling the API to request a new access token.&lt;/p&gt;

&lt;p&gt;A good place to do this is &lt;a href="https://github.com/bahdcoder/jwt-best-practices/blob/master/client/src/store/index.js" rel="noopener noreferrer"&gt;when the app mounts&lt;/a&gt;, showing a loading indicator to the user while we make this request:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCustomer&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;working&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setWorking&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;refreshToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;client&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;refresh_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="na"&gt;refresh_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expires_in&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nf"&gt;setCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setWorking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;refreshToken&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As soon as the app mounts, we make an HTTP request to the backend to refresh the access token. Since the &lt;code&gt;___refresh_token&lt;/code&gt; is already set on the customer's browser, it is sent along with the request.&lt;/p&gt;

&lt;p&gt;The backend gets the cookie, authenticates this cookie, and sends back a new access token with the customer's information.&lt;/p&gt;

&lt;p&gt;We then set the &lt;code&gt;token&lt;/code&gt; on the HTTP client for subsequent requests and set the customer in the state. This means every time the customer visits the app, their session is fetched from the API and they are automatically logged in.&lt;/p&gt;

&lt;p&gt;This solves the first problem, and the customer has a persistent session, but the access token will expire in 10 minutes, and we need to handle this case too. &lt;/p&gt;

&lt;p&gt;The API also responds with how long the JWT takes to expire, so we can use this value to know when to silently call the API to get a new access token.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCustomer&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;working&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setWorking&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;refreshToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;client&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;refresh_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="na"&gt;refresh_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expires_in&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;refreshToken&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expires_in&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nf"&gt;setCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setWorking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;refreshToken&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're using the &lt;code&gt;expires_in&lt;/code&gt; value to set a &lt;code&gt;setTimeout&lt;/code&gt; to refresh the token. This means a few milliseconds before the token expires, the &lt;code&gt;refreshToken()&lt;/code&gt; method is called again, and it'll set a new access token.&lt;/p&gt;

&lt;p&gt;Great, we can now keep the customer always logged in with the access token only stored in memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling logout
&lt;/h3&gt;

&lt;p&gt;What happens when the user needs to logout? We do not have access to the &lt;code&gt;___refresh_token&lt;/code&gt; cookie from client-side javascript, so how do we clear it?&lt;/p&gt;

&lt;p&gt;We need to call the API, and the API would invalidate the &lt;code&gt;___refresh_token&lt;/code&gt;. On the &lt;a href="https://github.com/bahdcoder/jwt-best-practices/blob/master/client/src/pages/Dashboard.js" rel="noopener noreferrer"&gt;dashboard page&lt;/a&gt;, when the &lt;code&gt;logout&lt;/code&gt; button is clicked, we'll invoke the following function:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;remove_refresh_token&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/auth/signin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nf"&gt;setCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We call the &lt;code&gt;remove_refresh_token&lt;/code&gt; endpoint on the backend, and the response invalidates the &lt;code&gt;___refresh_token&lt;/code&gt; cookie as such:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fbahdcoder%2Fimage%2Fupload%2Fv1606006228%2FScreenshot_2020-11-22_at_1.49.50_AM_ntxxw7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fbahdcoder%2Fimage%2Fupload%2Fv1606006228%2FScreenshot_2020-11-22_at_1.49.50_AM_ntxxw7.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The backend response contains a &lt;code&gt;Set-Cookie&lt;/code&gt; header, which sets the &lt;code&gt;Max-Age&lt;/code&gt; of the &lt;code&gt;___refresh_token&lt;/code&gt; header to &lt;code&gt;0&lt;/code&gt; and its value to &lt;code&gt;''&lt;/code&gt;, thus expiring it and making it invalid.&lt;/p&gt;

&lt;p&gt;We then set the customer to &lt;code&gt;null&lt;/code&gt; and redirect to the sign-in page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cross domain considerations
&lt;/h3&gt;

&lt;p&gt;In the example project, the client and server run on separate domains. This would most likely be the case for your application, and to allow two domains to exchange sensitive information with each other, you need to set some configuration on both client and server.&lt;/p&gt;

&lt;p&gt;On the server, first, you need to enable &lt;code&gt;CORS&lt;/code&gt;, allowing the client domain to request resources from the server. Secondly, you need to allow the exchange of credentials. This informs the server to accept sensitive information such as cookies from the incoming client request. On our demo server, we configured this as such:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;middlewareOptions&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Tensei.js uses &lt;code&gt;apollo-server-express&lt;/code&gt; behind the scenes for the graphql server, and this configuration is directly passed to it.&lt;/p&gt;

&lt;p&gt;On the client, you need to configure your HTTP client such as Axios or Fetch to include sensitive credentials when making requests to an external API. In the demo project we used &lt;code&gt;graphql-request&lt;/code&gt;, which we configured as such:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GraphQLClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;graphql-request&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GraphQLClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REACT_APP_API_URL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&lt;a href="http://localhost:4500/graphql" rel="noopener noreferrer"&gt;http://localhost:4500/graphql&lt;/a&gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;include&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Conclusion&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;When building applications that are not customer-facing, for tutorials or just fun projects, security might not be a big deal, but if working with real customer data, security has to be a top priority. &lt;/p&gt;

&lt;p&gt;I highly recommend implementing a very secure JWT authentication system when building applications that would be used in the real world. &lt;/p&gt;

&lt;p&gt;Please consider &lt;a href="https://twitter.com/bahdcoder" rel="noopener noreferrer"&gt;following me on Twitter&lt;/a&gt; and also checking out &lt;a href="https://github.com/tenseijs/tensei" rel="noopener noreferrer"&gt;tensei.js&lt;/a&gt; and giving it a star. &lt;/p&gt;

&lt;p&gt;Thank you very much for reading so far, and I hope this changes the way you handle JWT.&lt;/p&gt;

</description>
      <category>react</category>
      <category>tenseijs</category>
      <category>jwt</category>
      <category>auth</category>
    </item>
    <item>
      <title>The React Testing Masterclass</title>
      <dc:creator>Kati Frantz</dc:creator>
      <pubDate>Wed, 15 Jul 2020 09:22:06 +0000</pubDate>
      <link>https://dev.to/bahdcoder/the-react-testing-masterclass-ao</link>
      <guid>https://dev.to/bahdcoder/the-react-testing-masterclass-ao</guid>
      <description>&lt;h3&gt;
  
  
  Learn the best practices and strategies to effectively test your react components and applications.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://testingreact.katifrantz.com"&gt;Get the free course here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hi 👋 I' m Kati Frantz , and I've built tons and tons of React applications. Everyone can ship software, but shipping quality software requires a lot of quality assurance and best practices.&lt;/p&gt;

&lt;p&gt;A professional ships confident, well tested, and stable code.&lt;/p&gt;

&lt;p&gt;This is the standard, but testing react applications can be painful and complex. What are the best practices ? What should I test ? How can I test a react router based, redux store connected stripe checkout form 😱😭?&lt;/p&gt;

&lt;p&gt;Be glad you found this course, because this course will show you the best practices for testing components and full scale applications.&lt;/p&gt;

&lt;p&gt;We'll be testing an e-commerce application 🛒💰 with Stripe Checkout. Here are some of the features we'll be testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;🍄 Reusable checkboxes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🌴 External API powered products listing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🌐 Product tiles accessibility&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🌾 Off canvas filter menu (with portals)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🌲 Prevent scroll on canvas open&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🎏 Event listener cleanups&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🔕 Debounced products search&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🍉 Context dependent components&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🔐 React router page navigation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;📪 Redux connected components and pages&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🏄 Custom hooks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;👜 Checkout modal&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🍇 Stripe checkout&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And so much more ! I've curated a real-world project so you learn techniques and strategies you can use in your companies starting today !&lt;/p&gt;

&lt;p&gt;&lt;a href="https://testingreact.katifrantz.com"&gt;Get the free course here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>testing</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Mysql backups with node.js</title>
      <dc:creator>Kati Frantz</dc:creator>
      <pubDate>Thu, 21 May 2020 00:58:39 +0000</pubDate>
      <link>https://dev.to/bahdcoder/mysql-backups-with-node-js-1bn1</link>
      <guid>https://dev.to/bahdcoder/mysql-backups-with-node-js-1bn1</guid>
      <description>&lt;p&gt;If you're running an application in production, it is critical to have an automated backup system for your database. This system could automatically dump database backups and upload to the cloud every couple of hours. &lt;/p&gt;

&lt;p&gt;In this tutorial, we'll create this script using node.js. This script would run the &lt;code&gt;mysqldump&lt;/code&gt; command using the node.js child process. First let's examine this command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;mysqldump &lt;span class="nt"&gt;-u&lt;/span&gt; &amp;lt;username&amp;gt; &lt;span class="nt"&gt;-p&lt;/span&gt;&amp;lt;password&amp;gt; &amp;lt;database-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Running this command would generate a record of the table structure and the data from the specified database in the form of a list of SQL statements. &lt;/p&gt;

&lt;p&gt;Let's create the node.js backup script that would run this command as a child process. First, we'll import all the modules we need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;spawn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;child_process&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;spawn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We need the &lt;code&gt;fs&lt;/code&gt; module to write the dump content to the dump file.&lt;/li&gt;
&lt;li&gt;We need the &lt;code&gt;spawn&lt;/code&gt; method from the &lt;code&gt;child_process&lt;/code&gt; module to run the &lt;code&gt;mysqldump&lt;/code&gt; command. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;spawn&lt;/code&gt; runs the command and returns a stream. The spawn would not wait for the command to finish running before returning the output to us. This is very important because some large databases can run for many hours. &lt;/p&gt;

&lt;p&gt;Next, we'll need a unique name for the database dump.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dumpFileName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;.dump.sql`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This uses the date object in javascript to generate the current epoch time and attaches &lt;code&gt;.dump.sql&lt;/code&gt; to it. We'll use this as the dump file name.&lt;/p&gt;

&lt;p&gt;Next, let's create a write stream. When we stream output from the spawn method, we'll pass the output to the write stream, which would write the output to a file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dumpFileName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;.dump.sql`&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;writeStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createWriteStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dumpFileName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The write stream will create a file with the specified file name.&lt;br&gt;
Next, let's create the child process using spawn .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dump&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mysqldump&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-u&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;username&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-p&amp;lt;password&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;database-name&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The first argument to the &lt;code&gt;spawn&lt;/code&gt; method is the command, and the second is a list of all arguments to be passed to this command. As seen above, we are passing through all the commands just like we did on the command line. &lt;/p&gt;

&lt;p&gt;This method returns a child process, and we can now stream for every output emitted from the child process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;dump&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;writeStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;finish&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here, we are &lt;code&gt;pipe&lt;/code&gt;ing the output from the dump as input to the writeStream. So as the child process runs, every time there's a new chunk of output, the write stream would write it to the file.&lt;/p&gt;

&lt;p&gt;We can also listen to the finish and error events and pass callbacks to handle them. In this case we just log a message.&lt;/p&gt;

&lt;p&gt;Here's the complete script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;spawn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;child_process&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;spawn&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dumpFileName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;.dump.sql`&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;writeStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createWriteStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dumpFileName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dump&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mysqldump&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-u&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ghost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-pghost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ghost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nx"&gt;dump&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;writeStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;finish&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To automate this process, you can create a cron job that executes this script every x amount of time. &lt;/p&gt;

</description>
      <category>node</category>
      <category>spawn</category>
      <category>mysql</category>
      <category>backups</category>
    </item>
    <item>
      <title>Free book: Deploying Node.js 🚀</title>
      <dc:creator>Kati Frantz</dc:creator>
      <pubDate>Wed, 20 May 2020 03:15:35 +0000</pubDate>
      <link>https://dev.to/bahdcoder/free-book-deploying-node-js-4n6m</link>
      <guid>https://dev.to/bahdcoder/free-book-deploying-node-js-4n6m</guid>
      <description>&lt;p&gt;&lt;a href="https://deployingnodejs.com"&gt;Get free, complete access here &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hi! I'm &lt;a href="https://katifrantz.com"&gt;Kati Frantz&lt;/a&gt;, a full-stack javascript engineer who has spent sleepless nights trying to figure out server management and node.js application deployments.&lt;/p&gt;

&lt;p&gt;I've always found there was a lack of a reliable resource on how to deploy, manage, and scale node.js applications in the cloud. Over the past few years, I have built, deployed, scaled and managed node.js applications of all sizes.&lt;/p&gt;

&lt;p&gt;I always wished there was a practical and simple guide for me to follow, teaching me how to deploy my own production ready servers, databases, queue workers, how to set them up, secure them, and deploy my node.js applications.&lt;/p&gt;

&lt;p&gt;That's why I took out time to write this book, to share all the knowledge I have acquired over the years: a simple practical guide to provisioning, setting up, &lt;strong&gt;securing&lt;/strong&gt;, &lt;strong&gt;scaling&lt;/strong&gt;, &lt;strong&gt;managing&lt;/strong&gt; and &lt;strong&gt;deploying&lt;/strong&gt; node.js servers.&lt;/p&gt;

&lt;p&gt;It's complete free, so go ahead and start learning. &lt;a href="https://deployingnodejs.com"&gt;Get complete access here &lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Please share this resource with someone who might find it useful.&lt;/p&gt;

&lt;p&gt;Thank you !&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>devops</category>
      <category>book</category>
    </item>
    <item>
      <title>How to deploy and manage Node.js apps with Nesabox</title>
      <dc:creator>Kati Frantz</dc:creator>
      <pubDate>Fri, 27 Sep 2019 20:20:49 +0000</pubDate>
      <link>https://dev.to/bahdcoder/how-to-deploy-and-manage-node-js-apps-with-nesabox-2c11</link>
      <guid>https://dev.to/bahdcoder/how-to-deploy-and-manage-node-js-apps-with-nesabox-2c11</guid>
      <description>&lt;p&gt;This is a step by step guide for deploying and managing a Node.js application using Nesabox. As a sample project, we'll be deploying a &lt;a href="https://github.com/bahdcoder/strapi-nesabox-demo"&gt;strapi application&lt;/a&gt; but this tutorial would work for any Node.js framework or project.&lt;/p&gt;

&lt;h5&gt;
  
  
  Requirements
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;A free &lt;a href="https://nesabox.com"&gt;Nesabox account&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Creating a virtual private server
&lt;/h5&gt;

&lt;p&gt;The first thing we need is a VPS to host our application. You can obtain one of almost any size from server providers such as &lt;a href="https://digitalocean.com"&gt;Digital Ocean&lt;/a&gt;, &lt;a href="https://vultr.com"&gt;Vultr&lt;/a&gt;, &lt;a href="https://linode.com"&gt;Linode&lt;/a&gt; and &lt;a href="https://aws.amazon.com"&gt;AWS&lt;/a&gt;. Nesabox connects your account with these providers to make it easy to provision a server. You can also obtain a VPS from any provider of your choice running Ubuntu 18.04 and connect it to your Nesabox account. &lt;/p&gt;

&lt;p&gt;In this example, we'll use Digital Ocean, but any provider would work just fine. Visit your &lt;a href="https://app.nesabox.com/account/applications"&gt;account settings page&lt;/a&gt; to connect your provider by adding an API token obtained from Digital Ocean (Or your chosen provider).  &lt;/p&gt;

&lt;p&gt;Once done, &lt;a href="https://app.nesabox.com"&gt;visit your dashboard&lt;/a&gt; to provision a server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c9n_70DW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569549685/Screenshot_2019-09-27_at_03.00.16_or5mg2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c9n_70DW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569549685/Screenshot_2019-09-27_at_03.00.16_or5mg2.png" alt="Provision server form"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Be sure to select the databases you'll need on this server. In this case, I selected all databases supported by &lt;a href="https://strapi.io"&gt;Strapi&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The provisioning takes abut 10 minutes. During this time, Nesabox installs all necessary packages, databases, and configures the server for production.&lt;/p&gt;

&lt;h5&gt;
  
  
  Creating a database
&lt;/h5&gt;

&lt;p&gt;Once your server is ready, click to manage it. You should see the &lt;code&gt;Mongodb&lt;/code&gt; tab. You can manage your Mongodb installation here by adding new databases and users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o6iHHHvN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569551552/Screenshot_2019-09-27_at_03.32.18_qgfyfm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o6iHHHvN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569551552/Screenshot_2019-09-27_at_03.32.18_qgfyfm.png" alt="Mongodb management tab"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add a new database. We'll connect our application to this database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--92YlT33a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569551779/Screenshot_2019-09-27_at_03.35.51_nsydev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--92YlT33a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569551779/Screenshot_2019-09-27_at_03.35.51_nsydev.png" alt="Mongodb management tab"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We need to add a new database user to this database to access it. Click on the &lt;code&gt;people&lt;/code&gt; icon to manage database users.&lt;/p&gt;

&lt;p&gt;Add a database user with password.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_XVS1j1c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569552427/Screenshot_2019-09-27_at_03.37.13_dan2h3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_XVS1j1c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569552427/Screenshot_2019-09-27_at_03.37.13_dan2h3.png" alt="Add a database user"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Creating a site
&lt;/h5&gt;

&lt;p&gt;Visit the &lt;code&gt;Sites&lt;/code&gt; tab and add a new site. The name of the site should match the domain of your site. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t4iyiGCD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569552596/Screenshot_2019-09-27_at_03.49.40_cgydtt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t4iyiGCD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569552596/Screenshot_2019-09-27_at_03.49.40_cgydtt.png" alt="Add a site"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Connecting site to Github repository.
&lt;/h5&gt;

&lt;p&gt;Click to manage the newly created site. To connect the site to Github, you need to connect your account to Github from your &lt;a href="https://app.nesabox.com/account/applications"&gt;account applications page&lt;/a&gt;. Once connected, provide the repository and branch for the application. In this case, &lt;a href="https://github.com/bahdcoder/strapi-nesabox-demo"&gt;bahdcoder/strapi-nesabox-demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2UBOwTSe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569594666/Screenshot_2019-09-27_at_15.30.31_xq5io0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2UBOwTSe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569594666/Screenshot_2019-09-27_at_15.30.31_xq5io0.png" alt="Add repository"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This process would take only a few seconds to clone and install your project repository. &lt;/p&gt;

&lt;h5&gt;
  
  
  Configuring PM2
&lt;/h5&gt;

&lt;p&gt;PM2 is an advanced production ready process manager for Node.js applications. Nesabox creates an &lt;code&gt;ecosystem.config.js&lt;/code&gt; file specific to your application on the server.&lt;br&gt;
On the Site &lt;code&gt;Settings&lt;/code&gt; tab, you can update this file to add execution commands, node versions, environment variables, log files and any other configurations supported by PM2.&lt;/p&gt;

&lt;p&gt;On the &lt;code&gt;Settings&lt;/code&gt; tab, when you click the &lt;code&gt;Edit PM2 Ecosystem file&lt;/code&gt;, the &lt;code&gt;ecosystem.config.js&lt;/code&gt; file for this site is fetched securely over &lt;code&gt;SSH&lt;/code&gt; and placed in an editor for you to edit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ef9rKlpI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569595972/Screenshot_2019-09-27_at_15.52.30_ob43kd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ef9rKlpI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569595972/Screenshot_2019-09-27_at_15.52.30_ob43kd.png" alt="PM2 Ecosystem configuration file"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first important configurations are the &lt;code&gt;script:npm&lt;/code&gt; and &lt;code&gt;args:start&lt;/code&gt;. This instructs PM2 to run the &lt;code&gt;npm start&lt;/code&gt; command when starting or restarting the application in production.&lt;/p&gt;

&lt;p&gt;Next, we have the &lt;code&gt;interpreter&lt;/code&gt; configuration which is a path to the specific node version this app will run on. By default, it's the latest stable version.&lt;/p&gt;

&lt;p&gt;We can also configure all environment variables for our project in the &lt;code&gt;env&lt;/code&gt; object. The &lt;code&gt;PORT&lt;/code&gt; environment variable is automatically generated by Nesabox. If you need to change this, then you also have to make sure the Nginx configuration proxies to your new port too.&lt;/p&gt;

&lt;p&gt;For this application, all the environment variables really needed are database configurations. The &lt;code&gt;DATABASE_NAME&lt;/code&gt;, &lt;code&gt;DATABASE_USERNAME&lt;/code&gt; and &lt;code&gt;DATABASE_PASSWORD&lt;/code&gt; all match the MongoDB credentials we created earlier.&lt;/p&gt;

&lt;h5&gt;
  
  
  Setting up deploy script
&lt;/h5&gt;

&lt;p&gt;The &lt;code&gt;Deploy Script&lt;/code&gt; is the script Nesabox runs on your server on every deployment. By default, it pulls the latest changes from the deployment branch, installs &lt;code&gt;npm&lt;/code&gt; dependencies and restarts the application with &lt;code&gt;PM2&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;For this application, we'll need to build the application before starting it. We'll modify it as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uk-3BShd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569599312/Screenshot_2019-09-27_at_16.48.12_p1z1lv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uk-3BShd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569599312/Screenshot_2019-09-27_at_16.48.12_p1z1lv.png" alt="Deploy Script"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It simply runs the &lt;code&gt;npm run build&lt;/code&gt; command before starting with PM2.&lt;/p&gt;

&lt;h5&gt;
  
  
  Deploy your application
&lt;/h5&gt;

&lt;p&gt;To deploy, click deploy 🚀 ! You can monitor the deployment in real time too.&lt;/p&gt;

&lt;p&gt;To view your application, be sure to point your site domain to your server. &lt;/p&gt;

&lt;p&gt;🎉🎉🎉&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZwrTHFyP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569600021/Screenshot_2019-09-27_at_17.00.02_nn6ca7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZwrTHFyP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569600021/Screenshot_2019-09-27_at_17.00.02_nn6ca7.png" alt="Deployed Site"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  BONUS - Secure your site with Let's Encrypt
&lt;/h5&gt;

&lt;p&gt;On the &lt;code&gt;SSL&lt;/code&gt; tab, with one click, you can secure your site with a Let's Encrypt SSL certificate.&lt;/p&gt;

&lt;p&gt;🎉🎉🎉&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Lo4TP8qV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569600299/Screenshot_2019-09-27_at_17.04.45_dm3sue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Lo4TP8qV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/bahdcoder/image/upload/v1569600299/Screenshot_2019-09-27_at_17.04.45_dm3sue.png" alt="Secured site"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Conclusion
&lt;/h5&gt;

&lt;p&gt;Nesabox makes deployments and application management really easy. This article is just the tip of the iceberg. Please reach out with questions if you have any. Thank you for reading this far!&lt;/p&gt;

</description>
      <category>node</category>
      <category>devops</category>
      <category>javascript</category>
      <category>nesabox</category>
    </item>
    <item>
      <title>I do this all the time to reduce bugs in my javascript code #1</title>
      <dc:creator>Kati Frantz</dc:creator>
      <pubDate>Thu, 27 Jun 2019 16:50:14 +0000</pubDate>
      <link>https://dev.to/bahdcoder/i-do-this-all-the-time-to-reduce-bugs-in-my-javascript-code-1-22a0</link>
      <guid>https://dev.to/bahdcoder/i-do-this-all-the-time-to-reduce-bugs-in-my-javascript-code-1-22a0</guid>
      <description>&lt;p&gt;Have a look at the following code sample:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enrolments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;enrolment&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;enrolment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sometimes the data we work with is not predictable, so to avoid errors or crashes we always have to take care of edge cases. The above code makes a lot of assumptions. It assumes that &lt;code&gt;data.course&lt;/code&gt; exists, &lt;code&gt;data.course.enrolments&lt;/code&gt; exists and is an array of objects with a &lt;code&gt;confirm&lt;/code&gt; property which is a valid &lt;code&gt;Function&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If the &lt;code&gt;data.course&lt;/code&gt; or &lt;code&gt;data.course.enrolments&lt;/code&gt; property is &lt;code&gt;undefined&lt;/code&gt; we get the following &lt;code&gt;TypeError&lt;/code&gt; error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Uncaught&lt;/span&gt; &lt;span class="nx"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Cannot&lt;/span&gt; &lt;span class="nx"&gt;read&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt; &lt;span class="nx"&gt;enrolments&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;
&lt;span class="nx"&gt;Uncaught&lt;/span&gt; &lt;span class="nx"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Cannot&lt;/span&gt; &lt;span class="nx"&gt;read&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt; &lt;span class="nx"&gt;forEach&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the &lt;code&gt;confirm&lt;/code&gt; property in each &lt;code&gt;enrolment&lt;/code&gt; object in the &lt;code&gt;data.course.enrolments&lt;/code&gt; array is not a valid &lt;code&gt;Function&lt;/code&gt;, we get the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Uncaught&lt;/span&gt; &lt;span class="nx"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;enrolment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confirm&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now have a look at this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;course&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nx"&gt;enrolments&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;enrolment&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;enrolment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confirm&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;enrolment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'll explain how the above snippet works.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;data.course || {}&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;This expression resolves to &lt;code&gt;data.course&lt;/code&gt; if the &lt;code&gt;course&lt;/code&gt; property exists on the &lt;code&gt;data&lt;/code&gt; object, and resolved to an &lt;code&gt;{}&lt;/code&gt; if it doesn't. The reason it resolves to &lt;code&gt;{}&lt;/code&gt; is because of the &lt;code&gt;||&lt;/code&gt; operator which checks if the left-hand side of the expression (&lt;code&gt;data.course&lt;/code&gt;) is &lt;code&gt;falsy&lt;/code&gt;, and if it is, resolves the expression to the right-hand side (&lt;code&gt;{}&lt;/code&gt;). If it's not &lt;code&gt;falsy&lt;/code&gt; then it resolved the expression to the  left-hand side. &lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;(data.course || {}).enrolments || [])&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;As explained above, the left-hand side will either be &lt;code&gt;data.course&lt;/code&gt; or &lt;code&gt;{}&lt;/code&gt;. Next, we try to access the &lt;code&gt;enrolments&lt;/code&gt; property on the object resolved from the first expression.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;enrolment.confirm &amp;amp;&amp;amp; enrolment.confirm()&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Unlike the &lt;code&gt;||&lt;/code&gt; operator, the &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; checks if the left-hand side of the expression (&lt;code&gt;enrolment.confirm&lt;/code&gt;) is &lt;code&gt;truthy&lt;/code&gt;, and if it is, resolves the expression to be the right-hand side (&lt;code&gt;enrolment.confirm()&lt;/code&gt;). This means the function &lt;code&gt;enrolment.confirm&lt;/code&gt; will never be executed if the &lt;code&gt;confirm&lt;/code&gt; property is not found on the &lt;code&gt;enrolment&lt;/code&gt; object.&lt;/p&gt;

&lt;h4&gt;
  
  
  Summary
&lt;/h4&gt;

&lt;p&gt;Taking care of edge cases as you write your code can save you a lot of debugging time, and also prevent unexpected application crashes.&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>Free modern, project based HTML/CSS course for beginners</title>
      <dc:creator>Kati Frantz</dc:creator>
      <pubDate>Sat, 04 May 2019 14:53:54 +0000</pubDate>
      <link>https://dev.to/bahdcoder/free-modern-project-based-html-css-course-for-beginners-4fid</link>
      <guid>https://dev.to/bahdcoder/free-modern-project-based-html-css-course-for-beginners-4fid</guid>
      <description>&lt;p&gt;When I started out as a developer, I learnt so many &lt;code&gt;HTML&lt;/code&gt; tags, and as many &lt;code&gt;CSS&lt;/code&gt; rules as I could learn, but every time I built a website, it somehow did not end up looking like a website. To learn, I used to download free  templates online, just to have a look at how they were built so I could learn. &lt;/p&gt;

&lt;p&gt;Why the hell are there so many nested &lt;code&gt;div&lt;/code&gt;s ? How do these people even think about such structures? It always felt like building websites was a complicated science. Well if you are at that point in your career, this course is for you.&lt;/p&gt;

&lt;p&gt;I created a &lt;a href="https://bahdcasts.com/courses/modern-responsive-web-design"&gt;free, practical course&lt;/a&gt; to teach you how to implement this design &lt;a href="https://www.figma.com/file/36lnVYiFkIERJJyhxGH6ka/HTML5-CSS3-FLEXBOX-RESPONSIVE-DESIGN-COURSE"&gt;this mockup&lt;/a&gt;, covering so much about implementing practical designs in HTML &amp;amp; CSS, with a little touch of flexbox, just enough to get you going as a developer.&lt;/p&gt;

&lt;p&gt;Here's a list of things we'll learn in this short course:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to get started setting up a new project&lt;/li&gt;
&lt;li&gt;Moving from any design tool to HTML/CSS&lt;/li&gt;
&lt;li&gt;How to build responsive layouts with flex box&lt;/li&gt;
&lt;li&gt;Designing common web components&lt;/li&gt;
&lt;li&gt;Design tips for more responsive web designs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And so much more.&lt;/p&gt;

&lt;p&gt;Please share this course with someone who needs it 🔥. &lt;/p&gt;

&lt;p&gt;Don't forget checkout my other free courses on &lt;a href="https://bahdcasts.com"&gt;my website&lt;/a&gt;&lt;/p&gt;

</description>
      <category>html</category>
      <category>css</category>
      <category>flexbox</category>
    </item>
    <item>
      <title>TESTING LARAVEL WORKSHOP FOR BEGINNERS 🎉🎉🎉</title>
      <dc:creator>Kati Frantz</dc:creator>
      <pubDate>Sat, 02 Jun 2018 12:42:32 +0000</pubDate>
      <link>https://dev.to/bahdcoder/testing-laravel-workshop-for-beginners--47ci</link>
      <guid>https://dev.to/bahdcoder/testing-laravel-workshop-for-beginners--47ci</guid>
      <description>&lt;p&gt;Hey friends 😀. I would love to announce an upcoming online workshop (happening on the &lt;em&gt;Friday, 06 July 2018 12:00:00 PM GMT&lt;/em&gt;), meant to introduce testing of laravel applications.&lt;/p&gt;

&lt;p&gt;If you've never had any experience testing laravel applications, this &lt;strong&gt;absolutely free&lt;/strong&gt; workshop is for you. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Why am I doing this?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, to give you some context, I have a &lt;a href="https://udemy.com/best-laravel"&gt;really great course&lt;/a&gt; where I teach how to build simple to advanced laravel applications to absolute beginners.&lt;/p&gt;

&lt;p&gt;A few days ago, I saw this &lt;a href="https://vueschool.io/jobs"&gt;job posting on VueSchool&lt;/a&gt;, looking for a laravel developer to manage their application.&lt;/p&gt;

&lt;p&gt;I began to think about my students, who have considerable experience building laravel applications after taking my course, but would most likely not meet the requirements of the job 😔.&lt;/p&gt;

&lt;p&gt;No matter what applications you've built, no company would hire you to manage their applications if you do not master the skill of testing applications. &lt;/p&gt;

&lt;p&gt;That's why I'm doing this. I want to introduce you to testing. Teach you the basics. Everything you need to proceed thereon.&lt;/p&gt;

&lt;p&gt;A mastery of this skill would massively improve the quality of the applications you build using the amazing Laravel Framework.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://emailoctopus.com/lists/c1eb6370-645b-11e8-a3c9-06b79b628af2/forms/subscribe"&gt;REGISTER FOR THE FREE WORKSHOP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you've never written a single test for your laravel applications, this workshop is for you. I'll be there live, teaching you from the beginner's steps, answering every question you have.&lt;/p&gt;

&lt;p&gt;I've also been thinking of creating an online course laser-focused on testing laravel applications too 🤔, but I'll let you know if that thought still stands after the workshop. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://emailoctopus.com/lists/c1eb6370-645b-11e8-a3c9-06b79b628af2/forms/subscribe"&gt;REGISTER FOR THE FREE WORKSHOP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please tweet about this. Share it on your timeline. This is an opportunity to change someone's life somewhere. Let's make this happen together.&lt;/p&gt;

&lt;p&gt;🙏🙏🙏&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>phpunit</category>
      <category>unittesting</category>
    </item>
    <item>
      <title>Announcing the complete React/Redux course 🎉🎉🎉</title>
      <dc:creator>Kati Frantz</dc:creator>
      <pubDate>Sat, 19 May 2018 10:06:13 +0000</pubDate>
      <link>https://dev.to/bahdcoder/announcing-the-complete-reactredux-course--4gam</link>
      <guid>https://dev.to/bahdcoder/announcing-the-complete-reactredux-course--4gam</guid>
      <description>&lt;p&gt;Hello good people ! Over the past 6 months, I've been working on a React/Redux course. I just published it (finally ! 😀), and I thought I'd share it with you. It's a great opportunity to master reactjs and redux, and also build a portfolio with these technologies. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.bahdcasts.com/course/learn-react-redux?coupon=DEV_TO_90_OFF"&gt;GET THE COURSE USING THIS LINK AT 90% OFF&lt;/a&gt; (valid for a short time)&lt;/p&gt;

&lt;p&gt;Here's the course description:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://reactjs.org"&gt;ReactJS&lt;/a&gt; is one of the most in-demand javascript frameworks, and its popularity keeps rising as more companies begin to adopt it. Have a look at &lt;a href="http://www.npmtrends.com/@angular/core-vs-angular-vs-react-vs-vue"&gt;the charts here on npm trends&lt;/a&gt;. It keeps getting popular, and there's no better time to add it to your portfolio. This course is the cumulation of everything you need to become a professional React developer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://redux.js.org/"&gt;Redux&lt;/a&gt; is a state management library and is very popular in the world of frontend development. In this course, you'll not just learn how to use Redux independently with vanilla Javascript, but you'll master the art of using this state management library hand in hand with React.  &lt;/p&gt;

&lt;p&gt;This course starts from the very basics, explaining exactly what React and Redux are, when and how to use it. The problem with most of the React community is lack of understanding of the fundamental principles. This course focuses on providing a very deep understanding of React and Redux.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.bahdcasts.com/course/learn-react-redux?coupon=DEV_TO_90_OFF"&gt;GET THE COURSE USING THIS LINK AT 90% OFF&lt;/a&gt; (valid for a short time)&lt;/p&gt;

&lt;p&gt;Thereafter, we'll go all the way from basic to advanced. We'll not just scratch the surface but dive deeply into React as well as popular libraries like React Router, and Redux Thunk.  &lt;/p&gt;

&lt;p&gt;By the end of the course, you'll be able to build almost any sort of single page application.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What will you build?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
This course uses a project driven approach, which means you will learn by doing. In every single lesson, you'll learn something new, while building on real-world projects. &lt;br&gt;&lt;br&gt;
At the end of this course, you will build and deploy numerous real-world applications, thus having a complete portfolio in React and Redux. Here are some of the projects you'll build:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://bahdcasts-react-crud.netlify.com/"&gt;Complete todos manager application with React&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://bahdblog.herokuapp.com/"&gt;Complete blog single page application with ReactJS&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://react-redux-forum.netlify.com/"&gt;Forum application with React &amp;amp; Redux&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What should you bring to succeed in this course?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A basic understanding of HTML, CSS, and Javascript are required to succeed in this course.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.bahdcasts.com/course/learn-react-redux?coupon=DEV_TO_90_OFF"&gt;GET THE COURSE USING THIS LINK AT 90% OFF&lt;/a&gt; (valid for a short time)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What will you learn?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You'll learn:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  How to build an deploy real-world applications with React and Redux&lt;/li&gt;
&lt;li&gt;  Frontend state management with redux&lt;/li&gt;
&lt;li&gt;  Single page application routing with React router.&lt;/li&gt;
&lt;li&gt;  Asynchronous server communication in React / Redux applications using redux thunk.&lt;/li&gt;
&lt;li&gt;  Debugging React / Redux applications&lt;/li&gt;
&lt;li&gt;  Building powerful, fast and user-friendly reactive web applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.bahdcasts.com/course/learn-react-redux?coupon=DEV_TO_90_OFF"&gt;GET THE COURSE USING THIS LINK AT 90% OFF&lt;/a&gt; (valid for a short time)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who was this course built for?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This course is for you if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  You're just getting started with frontend / Javascript development and only JS basics set (No prior knowledge of ReactJS or any other frontend framework is required)&lt;/li&gt;
&lt;li&gt;  You know the basics of React and want to five deeper&lt;/li&gt;
&lt;li&gt;  You want to understand how ReactJS works with Redux&lt;/li&gt;
&lt;li&gt;  You want to build a portfolio with numerous ReactJS projects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.bahdcasts.com/course/learn-react-redux?coupon=DEV_TO_90_OFF"&gt;GET THE COURSE USING THIS LINK AT 90% OFF&lt;/a&gt; (valid for a short time)&lt;/p&gt;

</description>
      <category>react</category>
      <category>redux</category>
      <category>reactredux</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
