<?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: Sarah 🦄</title>
    <description>The latest articles on DEV Community by Sarah 🦄 (@sarahcodes_dev).</description>
    <link>https://dev.to/sarahcodes_dev</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%2F135768%2F90b98e1b-6aec-4464-a03e-6a6633bf1636.png</url>
      <title>DEV Community: Sarah 🦄</title>
      <link>https://dev.to/sarahcodes_dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sarahcodes_dev"/>
    <language>en</language>
    <item>
      <title>A Guide to Integrating with Adyen Web for 3D Secure 2 Payments</title>
      <dc:creator>Sarah 🦄</dc:creator>
      <pubDate>Fri, 12 Jan 2024 14:36:30 +0000</pubDate>
      <link>https://dev.to/adyen/a-guide-to-integrating-with-adyen-web-for-3d-secure-2-payments-p3j</link>
      <guid>https://dev.to/adyen/a-guide-to-integrating-with-adyen-web-for-3d-secure-2-payments-p3j</guid>
      <description>&lt;p&gt;The goal of this guide is to provide the necessary context and examples to empower developers to handle 3D Secure 2 payments with the Adyen Web solution for the browser. This guide has a companion demo application with code examples for each flow which can be found in our &lt;a href="https://github.com/adyen-examples/adyen-node-online-payments/tree/main/3ds2-example" rel="noopener noreferrer"&gt;github examples&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is 3D Secure 2?
&lt;/h2&gt;

&lt;p&gt;3 Domain Secure (3DS) is a security measure for online payments. The 3 domains (acquirer, scheme, and issuer) interact with each other using a 3DS protocol where they exchange information, providing an authentication mechanism for the consumer during a transaction.&lt;/p&gt;

&lt;p&gt;3D Secure helps prevent fraud and is available for Card Not Present (CNP) transactions with all major card networks, and is mandatory in the EU, following the Revised Payment Services Directive (PSD2). For more in depth details about PSD2 and SCA you can refer to our &lt;a href="https://docs.adyen.com/online-payments/psd2-sca-compliance-and-implementation-guide/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When facilitating a card payment online we want to do all we can to ensure the shopper is who they say they are. This way we reduce fraudulent transactions and improve authorisation rates for real shoppers.&lt;/p&gt;

&lt;p&gt;There are two different ways customers can verify themselves using 3D Secure: frictionless and challenge. The frictionless flow is based on background information that doesn't require the customer to actively verify themselves. The challenge flow means the issuer has determined the transaction needs additional verification from the customer. For example; the shopper may have to enter a passcode they receive on their phone. The more information about the shopper we send in the payment request the higher the chances of a frictionless flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to support 3DS2 transactions with Adyen
&lt;/h2&gt;

&lt;p&gt;Our &lt;a href="https://github.com/adyen/adyen-web" rel="noopener noreferrer"&gt;Adyen Web SDK&lt;/a&gt; and &lt;a href="https://github.com/adyen-examples" rel="noopener noreferrer"&gt;server libraries&lt;/a&gt; support two options for 3DS2 payments: &lt;/p&gt;

&lt;h3&gt;
  
  
  Native
&lt;/h3&gt;

&lt;p&gt;The authentication experience occurs on your merchant page. Our SDK renders the necessary components and communicates directly with the ACS. The shopper never leaves your page. &lt;/p&gt;

&lt;h3&gt;
  
  
  Redirect
&lt;/h3&gt;

&lt;p&gt;The authentication experience occurs on an Adyen page. Our SDK will redirect the shopper to an Adyen domain. The shopper is then redirected back to your merchant page once the authentication is complete. &lt;/p&gt;

&lt;p&gt;Both Native and Redirect flows can be either frictionless or challenged. Which option to choose depends on a multitude of factors and is out of scope for this guide. For more in depth information about each flow you can refer to our &lt;a href="https://docs.adyen.com/online-payments/3d-secure/#authentication-flows" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. This guide will show you how to integrate for both flows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparing Adyen Solutions
&lt;/h2&gt;

&lt;p&gt;When choosing a solution with Adyen you have a couple of choices to make; on the client side you can choose to integrate with our Drop-in or our Components. On the server side you can choose to integrate with our Sessions flow or our Advanced flow. This tutorial will demonstrate the Drop-in, you can follow along with our &lt;a href="https://github.com/adyen-examples/adyen-node-online-payments/tree/main/3ds2-example" rel="noopener noreferrer"&gt;Github example&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Drop-in vs. Components
&lt;/h3&gt;

&lt;p&gt;Drop-in is our pre-built UI for accepting payments. We recommend Drop-in as it renders a full list of available payment methods and does a lot of the heavy lifting for you. It also has 3D Secure 2 support built-in. &lt;/p&gt;

&lt;p&gt;Components are our customizable UI components which render individual payment methods. Choose Components if you prefer to compose your own UI. &lt;/p&gt;

&lt;p&gt;Both Drop-in and Components are available from our &lt;a href="https://github.com/Adyen/adyen-web" rel="noopener noreferrer"&gt;Adyen Web SDK&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Sessions flow vs. Advanced flow
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Sessions flow&lt;/strong&gt; consists of one API call that creates a payment session which is used by the Adyen Web SDK to facilitate the payment flow. The advantage is that with the Sessions flow you don’t have to do any extra work for 3DS2, everything is handled by the Drop-in/components. However, it’s important to note that the Sessions flow does not support 3DS2 redirect flow so if you wish to use redirect then you need to use the Advanced flow. &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Advanced flow&lt;/strong&gt; consists of three API calls: /paymentMethods, /payments and /payments/details. You will need to configure and support each of these API calls in the advanced flow to facilitate the payment. Luckily you can use one of our Adyen &lt;a href="https://github.com/adyen#server-side" rel="noopener noreferrer"&gt;server libraries&lt;/a&gt; to make this a lot easier. &lt;/p&gt;

&lt;p&gt;When integrating with Adyen you can break it down to three core parts; the server, the client and the webhook. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server:&lt;/strong&gt; Handle the payment request(s) on your server using one of our API flows. &lt;br&gt;
&lt;strong&gt;Client:&lt;/strong&gt; Show the payment on your web page using our pre-built Drop-in or composing your own UI with our Components. &lt;br&gt;
&lt;strong&gt;Webhook:&lt;/strong&gt; Configure and receive webhook notifications with the outcome of each payment. Webhooks are out of scope for this guide. &lt;/p&gt;

&lt;p&gt;How 3DS2 is applied is dependent on your Dynamic 3DS rules. For more information on this please refer to our &lt;a href="https://docs.adyen.com/risk-management/dynamic-3d-secure/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. &lt;/p&gt;
&lt;h2&gt;
  
  
  Integrate with Sessions Flow
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This guide assumes you already have your Adyen API key and client key and are pointing to our TEST environment. If you haven’t you can find directions &lt;a href="https://docs.adyen.com/get-started-with-adyen/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s start with configuring the Sessions flow on our backend. The Sessions flow consists of one API call /sessions. &lt;/p&gt;
&lt;h3&gt;
  
  
  Install Adyen API library
&lt;/h3&gt;

&lt;p&gt;The first step is to install the Adyen API library, these examples will be in TypeScript so we’re going to use the &lt;a href="https://github.com/Adyen/adyen-node-api-library" rel="noopener noreferrer"&gt;Node.js library&lt;/a&gt;. These examples are built using version 15.1.0 of the api-library which utilizes the latest version of Checkout API (71). &lt;/p&gt;

&lt;p&gt;If you have npm installed, you install the library in your app by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save @adyen/api-library
npm update @adyen/api-library

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure CheckoutAPI
&lt;/h3&gt;

&lt;p&gt;Now we have the library installed, let’s write our payment service. The first step is to initialize the Client object with your API key and environment, then we need to pass this client into the CheckoutAPI constructor to initialize our CheckoutAPI object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../backend/../payments.ts

import { Client, CheckoutAPI } from '@adyen/api-library';

// initialise the client object
 const client = new Client({
      apiKey: 'YOUR_API_KEY_HERE',
      environment: 'TEST',
 });

 // intialise the API object with the client object
 const paymentsAPI = new CheckoutAPI(client).PaymentsApi;

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

&lt;/div&gt;



&lt;p&gt;The CheckoutAPI object exposes a few different API helpers, we want to use the PaymentsApi. Now we have initialized checkout, we can use it to call the sessions API. &lt;/p&gt;

&lt;h3&gt;
  
  
  Handle /sessions on the server
&lt;/h3&gt;

&lt;p&gt;First we create an asynchronous function to submit the sessions request. We build our request object with all of the fields we want to pass to our API. In the example below we include the fields we recommend to increase the chances of a frictionless flow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../backend/../payments.ts

async postForSessions(data): Promise&amp;lt;CreateCheckoutSessionResponse&amp;gt; {
 const sessionsRequestData: CreateCheckoutSessionRequest = {
      amount: {
        currency: "EUR",
        value: 1000, 
      },
      countryCode: "NL",
      shopperName: {
        firstName: "FirstName",
        lastName: "LastName",
      },
      telephoneNumber: "0612345678",
      billingAddress: {
        houseNumberOrName: "1",
        street: "Shopper Billing Street",
        city: "Amsterdam",
        country: "NL",
        postalCode: "1234AB",
      },
      deliveryAddress: {
        houseNumberOrName: "1",
        street: "Shopper Delivery Street",
        city: "Amsterdam",
        country: "NL",
        postalCode: "1234AB",
      },
      shopperIP: "shopperIP",
      shopperEmail: "shopperEmail",
      channel: PaymentRequest.ChannelEnum.Web,
      reference: "YOUR_PAYMENT_REFERENCE",
      returnUrl: "https://your-company.com/checkout?shopperOrder=12xy..",
      merchantAccount: "YOUR_MERCHANT_ACCOUNT",
 };

 const sessionsResponse = await this.paymentsAPI.sessions(sessionsRequestData);
 return sessionsResponse;
}

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

&lt;/div&gt;



&lt;p&gt;Above we pass the request data to the sessions function on the paymentsAPI object and return the awaited response. &lt;/p&gt;

&lt;p&gt;We have now set up our backend for the sessions flow. Let’s review what we did: &lt;br&gt;
✅ We installed the Adyen API library&lt;br&gt;
✅ We configured the Client object with our API key and environment &lt;br&gt;
✅ We instantiated the CheckoutAPI with our Client object and called the PaymentsAPI&lt;br&gt;
✅ We created a function to handle the /sessions API call&lt;/p&gt;

&lt;p&gt;Now let’s set up our frontend to send the request. &lt;/p&gt;
&lt;h3&gt;
  
  
  Install Adyen Web SDK
&lt;/h3&gt;

&lt;p&gt;In our frontend app we will use the &lt;a href="https://github.com/Adyen/adyen-web" rel="noopener noreferrer"&gt;Adyen Web SDK&lt;/a&gt; so let’s install it by running the command below. These examples are using version 5.55.1.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @adyen/adyen-web --save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make sure our Drop-in looks as expected we need to import the Adyen web stylesheet in our app. This might look different depending on your set up. Here we will import it in our index.js file. You can override the styling rules to add your own styles. We also import the AdyenCheckout module so we can use it later to create our checkout instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../frontend../dropin.js

import AdyenCheckout from "@adyen/adyen-web";
import "@adyen/adyen-web/dist/adyen.css";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuring the Web Drop-in
&lt;/h3&gt;

&lt;p&gt;Since we are using session flow our Drop-in requires the response from the sessions API call in its configuration. So let’s first make a call to our backend to receive the response from the /sessions endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//  ../frontend../dropin.js 

const sessionsRequest = {
// data for your request object, may include shopper details, the amount etc...  
}

const sessionsResponse = await postDoSessions(sessionsRequest);

// ../frontend../payments.js

export const postDoSessions = async (data) =&amp;gt; {
// send the sessions request to your backend  
const response = await fetch("/api/sessions", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ data }),
  });
  return await response.json();

};

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

&lt;/div&gt;



&lt;p&gt;Now we have the sessions response we can now build our configuration object and create our checkout instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../frontend../dropin.js 

// create configuration object to pass into AdyenCheckout
const checkoutConfig = {
    locale: "en_US",
    environment: "test",
    clientKey: YOUR_ADYEN_CLIENT_KEY,
    session: sessionsResponse, // response from the /sessions call 
    onPaymentCompleted: onPaymentCompleted,
    onError: onError,
};

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

&lt;/div&gt;



&lt;p&gt;Our configuration object has two events which handle the payment:&lt;br&gt;
&lt;strong&gt;onPaymentCompleted&lt;/strong&gt; is triggered once the payment completes, here you can handle what should happen afterwards. &lt;br&gt;
&lt;strong&gt;onError&lt;/strong&gt; is triggered if an error is thrown. Use this event to gracefully handle errors for your shoppers. &lt;/p&gt;

&lt;p&gt;We create callback functions for each which will be executed when the events are invoked by the Drop-in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../frontend../dropin.js

  const onPaymentCompleted = (result, dropin) =&amp;gt; {
    // handle payment completed here
  };

  const onError = (error, dropin) =&amp;gt; {
    // handle error here
  };

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

&lt;/div&gt;



&lt;p&gt;For a full list of all configuration options you can refer to our &lt;a href="https://docs.adyen.com/online-payments/build-your-integration/?platform=Web&amp;amp;integration=Drop-in&amp;amp;version=5.55.1#drop-in-configuration" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now we have our configuration object set up with our session data, let's instantiate the Drop-in,  pass the configuration to AdyenCheckout and mount the instance as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../frontend../dropin.js

const checkout = await AdyenCheckout(checkoutConfig);

// This will mount the checkout component to `&amp;lt;div id="dropin-container"&amp;gt;&amp;lt;/div&amp;gt;`
checkout.create("dropin").mount("#dropin-container");

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

&lt;/div&gt;



&lt;p&gt;The component will be mounted to an element with an id of #dropin-container. For our last step let’s add a div element to our HTML with this id.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../frontend../dropin.html

&amp;lt;div id="dropin-container"&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we’ve set up our frontend to handle payments with Sessions flow and Drop-in. Let’s review what we just did: &lt;br&gt;
✅ We installed the Adyen Web SDK&lt;br&gt;
✅ We called our sessions API session data.&lt;br&gt;
✅ We created our configuration object passing the sessions response object and creating callback functions to handle the events. &lt;br&gt;
✅ We instantiated our checkout object with our config. &lt;br&gt;
✅ We created and mounted our Drop-in to our DOM. &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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz6kw5bejpqnsfpdvb930.jpg" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz6kw5bejpqnsfpdvb930.jpg" alt="Diagram explaining sessions flow across client, server and Adyen domain"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Integrate with Advanced Flow
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This guide assumes you already have your Adyen API key and client key and are pointing to our TEST environment. If you haven’t you can find directions &lt;a href="https://docs.adyen.com/get-started-with-adyen/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;Let’s start with configuring the advanced flow on our backend. The advanced flow consists of three API calls &lt;a href="https://docs.adyen.com/api-explorer/Checkout/71/post/paymentMethods" rel="noopener noreferrer"&gt;/paymentMethods&lt;/a&gt;, &lt;a href="https://docs.adyen.com/api-explorer/Checkout/71/post/payments" rel="noopener noreferrer"&gt;/payments&lt;/a&gt; and &lt;a href="https://docs.adyen.com/api-explorer/Checkout/71/post/payments/details" rel="noopener noreferrer"&gt;/payments/details&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Install Adyen API library
&lt;/h3&gt;

&lt;p&gt;The first step is to install the Adyen API library, these examples will be in TypeScript so we’re going to use the &lt;a href="https://github.com/Adyen/adyen-node-api-library" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; library. These examples are built using version 15.1.0 of the api-library which utilizes the latest version of Checkout API (71). &lt;/p&gt;

&lt;p&gt;If you have npm installed, you install the library in your app by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save @adyen/api-library
npm update @adyen/api-library

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure CheckoutAPI
&lt;/h3&gt;

&lt;p&gt;Now we have the library installed, let’s write our payment service. The first step is to initialize the Client object with your API key and environment, then we need to pass this client into the CheckoutAPI constructor to initialize our CheckoutAPI object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../backend/../payments.ts

import { Client, CheckoutAPI } from '@adyen/api-library';

// initialise the client object
 const client = new Client({
   apiKey: 'YOUR_API_KEY_HERE',
   environment: 'TEST',
});

// intialise the API object with the client object
const paymentsAPI = new CheckoutAPI(client).PaymentsApi;

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

&lt;/div&gt;



&lt;p&gt;The CheckoutAPI object exposes a few different API helpers, we want to use the PaymentsApi. &lt;/p&gt;

&lt;p&gt;The advanced flow consists of three API calls, let’s create functions to handle each of these requests. &lt;/p&gt;

&lt;h3&gt;
  
  
  Handle /paymentMethods on the server
&lt;/h3&gt;

&lt;p&gt;The initial &lt;a href="https://docs.adyen.com/api-explorer/Checkout/71/post/paymentMethods" rel="noopener noreferrer"&gt;/paymentMethods&lt;/a&gt; call is used to get a list of the available payment methods. We need to pass the response of this call to our Drop-in configuration. Which payment methods are returned depends on your merchant configuration. We build our request object with the fields we want to pass to the API. For this example we will just use the required field of merchantAccount. You can find the optional parameters &lt;a href="https://docs.adyen.com/api-explorer/Checkout/71/post/paymentMethods#request" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../backend/../payments.ts 

async postForPaymentMethods(): Promise&amp;lt;PaymentMethodsResponse&amp;gt; {
    const postData = {
      merchantAccount: 'YOUR_MERCHANT_ACCOUNT',
    };

    const paymentMethodsResponse: PaymentMethodsResponse = await     this.paymentsAPI.paymentMethods(postData);

    return paymentMethodsResponse;
  }

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handle /payments on the server
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://docs.adyen.com/api-explorer/Checkout/71/post/payments" rel="noopener noreferrer"&gt;/payments&lt;/a&gt; request sends the data related to the payment and the shopper. We want to call this when our shopper submits on the Drop-in. Our payments request may look different depending on whether we want to implement the native flow or the redirect flow. &lt;/p&gt;

&lt;h4&gt;
  
  
  Payment request for Native flow:
&lt;/h4&gt;

&lt;p&gt;If we want to implement the native flow then we need to set the threeDSRequestData object in the AuthenticationData object of the API request like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../backend/../payments.ts

const authenticationData: AuthenticationData = {
      threeDSRequestData: {
        nativeThreeDS: ThreeDSRequestData.NativeThreeDSEnum.Preferred, 
      },
};

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

&lt;/div&gt;



&lt;p&gt;Here we have set nativeThreeDS to be preferred, this will ensure we get the native flow. You can find the other optional parameters &lt;a href="https://docs.adyen.com/api-explorer/Checkout/71/post/payments#request-authenticationData-threeDSRequestData" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Now let’s build the rest of our request object and make the payments request. In the example below we include the fields we recommend to increase the chances of a frictionless flow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../backend/../payments.ts

async postForPaymentsNative(data): Promise&amp;lt;PaymentResponse&amp;gt; {

    const paymentRequestData: PaymentRequest = {
      amount: {
        currency: "EUR",
        value: 1000,
      },
      authenticationData: {
        ...authenticationData, // pass our authenticationData object 
      },
      countryCode: "NL",
      shopperName: {
        firstName: "FirstName",
        lastName: "LastName",
      },
   telephoneNumber: "0612345678",
   billingAddress: {
     houseNumberOrName: "1",
     street: "Shopper Billing Street",
     city: "Amsterdam",
     country: "NL",
     postalCode: "1234AB",
   },
   deliveryAddress: {
      houseNumberOrName: "1",
      street: "Shopper Delivery Street",
      city: "Amsterdam",
      country: "NL",
      postalCode: "1234AB",
   },
      shopperIP: "ShopperIP",
      shopperEmail: "ShopperEmail",
      channel: PaymentRequest.ChannelEnum.Web,
      browserInfo: data.browserInfo, 
      origin: url, 
      paymentMethod: data.paymentMethod, 
   reference: "YOUR_PAYMENT_REFERENCE",
   returnUrl: "https://your-company.com/checkout?shopperOrder=12xy..",
   merchantAccount: "YOUR_MERCHANT_ACCOUNT",
    };

 const paymentResponse: PaymentResponse = await this.paymentsAPI.payments(paymentRequestData);

    return paymentResponse;
  }

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

&lt;/div&gt;



&lt;p&gt;Above we pass the request data to the payments function on the paymentsAPI object and return the awaited response. &lt;/p&gt;

&lt;h4&gt;
  
  
  Payment request for Redirect
&lt;/h4&gt;

&lt;p&gt;Since redirect is the default in advanced flow we don’t need to pass any additional data to our authenticationData object in our payments request. &lt;/p&gt;

&lt;p&gt;We can just build our request object and make the payment request. In the example below we include the fields we recommend to increase the chances of a frictionless flow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../backend/../payments.ts

async postForPaymentsRedirect(data): Promise&amp;lt;PaymentResponse&amp;gt; {

    const paymentRequestData: PaymentRequest = {
      amount: {
        currency: "EUR",
        value: 1000,
      },
     countryCode: "NL",
      shopperName: {
        firstName: "FirstName",
        lastName: "LastName",
      },
   telephoneNumber: "0612345678",
   billingAddress: {
     houseNumberOrName: "1",
     street: "Shopper Billing Street",
     city: "Amsterdam",
     country: "NL",
     postalCode: "1234AB",
   },
   deliveryAddress: {
      houseNumberOrName: "1",
      street: "Shopper Delivery Street",
      city: "Amsterdam",
      country: "NL",
      postalCode: "1234AB",
   },
      shopperIP: "ShopperIP",
      shopperEmail: "ShopperEmail",
      channel: PaymentRequest.ChannelEnum.Web,
      browserInfo: data.browserInfo, 
      origin: url, 
      paymentMethod: data.paymentMethod, 
   reference: "YOUR_PAYMENT_REFERENCE",
   returnUrl: "https://your-company.com/checkout?shopperOrder=12xy..",
   merchantAccount: "YOUR_MERCHANT_ACCOUNT",
    };

    const paymentResponse: PaymentResponse = await this.paymentsAPI.payments(paymentRequestData);

    return paymentResponse;
  }


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

&lt;/div&gt;



&lt;h4&gt;
  
  
  Handling PaymentMethod and BrowserInfo
&lt;/h4&gt;

&lt;p&gt;In both native and redirect examples you may notice we set paymentMethod and browserInfo with the data object passed from the client. This data comes from the Drop-in state and is returned in the onSubmit event callback that we will define later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handle /payments/details on the server
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://docs.adyen.com/api-explorer/Checkout/71/post/payments/details" rel="noopener noreferrer"&gt;/payments/details&lt;/a&gt; request returns the details of the payment request we made. This response will include the result of the Authentication. What we pass to the details call is different depending on native or redirect flow, we will delve into this in the upcoming client section. For now we can create our request like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../backend/../payments.ts

 async postForPaymentDetails({ details }: { details: PaymentCompletionDetails }): Promise&amp;lt;PaymentDetailsResponse&amp;gt; {
    const paymentDetailsResponse: PaymentDetailsResponse = await this.paymentsAPI.paymentsDetails({ details });

    return paymentDetailsResponse;
  }


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

&lt;/div&gt;



&lt;p&gt;We have now set up our backend for the advanced flow. Let’s review what we did: &lt;br&gt;
✅ We installed the Adyen API library&lt;br&gt;
✅ We configured the Client object with our API key and environment &lt;br&gt;
✅ We instantiated the CheckoutAPI with our Client object and called the PaymentsAPI&lt;br&gt;
✅ We created a function to handle the /paymentMethods API call&lt;br&gt;
✅ We created a function to handle the /payments API call&lt;br&gt;
✅ We created a function to handle the /payments/details API call&lt;/p&gt;

&lt;p&gt;Now let’s set up our frontend to send the request. &lt;/p&gt;
&lt;h3&gt;
  
  
  Install Adyen Web SDK
&lt;/h3&gt;

&lt;p&gt;In our frontend app we will use the Adyen Web SDK so let’s install it by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @adyen/adyen-web --save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make sure our Drop-in looks as expected we need to import the Adyen web stylesheet in our app. This might look different depending on your set up. Here we will import it in our index.js file. You can override the styling rules to add your own styles. We also import the AdyenCheckout module that we will use later to create our checkout instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../frontend/../dropin.js

import AdyenCheckout from "@adyen/adyen-web";
import "@adyen/adyen-web/dist/adyen.css";

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuring the Web Drop-in
&lt;/h3&gt;

&lt;p&gt;Since we are using advanced flow the first thing our client needs is the paymentMethods response from the /paymentMethods API so we can configure the drop-in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../frontend/../dropin.js

 const paymentMethods = await getPaymentMethods();

// create configuration object to pass into AdyenCheckout
const checkoutConfig = {
      paymentMethodsResponse: paymentMethods,
      locale: "en_US",
      environment: "test",
      clientKey: CLIENT_KEY,
      onSubmit: onSubmit,
      onAdditionalDetails: onAdditionalDetails,
 };

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

&lt;/div&gt;



&lt;p&gt;Our configuration object has two events which handle the payment. We create callback functions for each which will be executed when the events are invoked by the Drop-in.  &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;onSubmit&lt;/strong&gt; function is triggered when the shopper clicks the Pay button, here you can send the payment request and handle the response actions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../frontend/../dropin.js

const onSubmit = async (state, dropinComponent) =&amp;gt; {
  if (state.isValid) {
     const paymentResponse = await postDoPayment(state.data);
     // check result code and handle action here if required  
     if (paymentResponse.resultCode === "Authorised") {
        // no threeDS required
     } else if(paymentResponse.action) {
       dropinComponent.handleAction(paymentResponse.action);    
     }
  }
};

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

&lt;/div&gt;



&lt;p&gt;In the code example above you can see we first check the state is valid to ensure there are no errors in the input and then pass the state.data to our payment request. This is the object that has the data for paymentMethod and browserInfo which we set in our API request. We await the payment response, the response will have a resultCode and depending on that resultCode it could also have an action object. An action is an additional step required which can be handled by the Drop-in handleAction method. Once we receive the response we check the resultCode and check if there is an action. Since we are building for 3DS2 we know we can expect an action of either &lt;strong&gt;redirect&lt;/strong&gt; or &lt;strong&gt;threeDS2&lt;/strong&gt; (for native). For more information on actions you can go &lt;a href="https://docs.adyen.com/online-payments/build-your-integration/additional-use-cases/advanced-flow-integration/?platform=Web&amp;amp;integration=Drop-in&amp;amp;version=5.53.2#additional-front-end" rel="noopener noreferrer"&gt;refer to our documentation&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;onAdditionalDetails&lt;/strong&gt; function is triggered if a payment method requires additional details. In our context this event will be invoked to handle the native 3DS2 result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../frontend/../dropin.js

 const onAdditionalDetails = async (state, dropinComponent) =&amp;gt; {
      const paymentDetailsResponse = await postDoPaymentDetails(state.data);
      // handle result 
    };

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

&lt;/div&gt;



&lt;p&gt;When integrating with the 3DS2 native flow we use the onAdditionalDetails event callback to submit the /payments/details API call and get the final result. The onAddtionalDetails event is automatically triggered in native flow by the Drop-in once the shopper has completed the 3DS2 action. &lt;/p&gt;

&lt;p&gt;For a full list of all configuration options you can refer to our &lt;a href="https://docs.adyen.com/online-payments/build-your-integration/additional-use-cases/advanced-flow-integration/?platform=Web&amp;amp;integration=Drop-in&amp;amp;version=5.53.2#configuring-drop-in" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. Now we have built our configuration object and handlers for the callbacks, let’s instantiate the drop-in, pass the configuration to AdyenCheckout and mount the instance as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../frontend../dropin.js

const checkout = await AdyenCheckout(checkoutConfig);

// This will mount the checkout component to `&amp;lt;div id="dropin-container"&amp;gt;&amp;lt;/div&amp;gt;`
checkout.create("dropin").mount("#dropin-container");

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

&lt;/div&gt;



&lt;p&gt;The component will be mounted to an element with an id of #dropin-container. For our last step let’s add a div element to our HTML with this id.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// frontend/../dropin.html

&amp;lt;div id="dropin-container"&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Handling the 3DS2 result
&lt;/h4&gt;

&lt;p&gt;We mentioned above that the &lt;em&gt;onAdditionalDetails&lt;/em&gt; callback is automatically triggered for native 3DS2. Here you will get the details result in the state.data object which is the first parameter of the callback event. Pass this data to the &lt;code&gt;/payments/details&lt;/code&gt; API to complete the 3DS2 transaction. &lt;/p&gt;

&lt;p&gt;If the integration is for 3DS2 redirect then we need to handle the redirect result differently. In a redirect flow when the shopper is redirected back to your merchant page the returnUrl will have the redirectResult appended, it might look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://your-company.com/checkout?shopperOrder=12xy&amp;amp;redirectResult=X6XtfGC3%21Y...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case we need to parse the url to get the redirectResult and then pass this to our &lt;code&gt;/payments/details&lt;/code&gt; call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ../frontend/../dropin.js

// example of how to parse redirect result from the url 
const parseRedirectResultToRequestData = (url) =&amp;gt; {
    const redirectResult = url.substring(url.indexOf("=") + 1, url.length);
  return {
    details: { redirectResult },
  };
};

const requestData = parseRedirectResultToRequestData(url);
const paymentDetailsResponse = await postDoPaymentDetails(requestData);

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

&lt;/div&gt;



&lt;p&gt;And that’s it, now we’ve set up our frontend to handle payments with advanced flow and Drop-in. Let’s review what we just did: &lt;br&gt;
✅ We installed the Adyen Web SDK&lt;br&gt;
✅ We called our paymentMethods API to get the paymentMethods data &lt;br&gt;
✅ We created our configuration object passing the paymentMethods data and creating callback functions to handle the events. &lt;br&gt;
✅ We instantiated our checkout object with our configuration. &lt;br&gt;
✅ We created and mounted our Adyen Drop-in in our DOM. &lt;br&gt;
✅ We handle the 3DS2 result in our onAdditionalDetails event callback in the native flow&lt;br&gt;
✅ We handle the 3DS2 result by parsing the redirectResult from the url in the redirect flow&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcdvq3fj8igk409fy3kp5.jpg" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcdvq3fj8igk409fy3kp5.jpg" alt="Diagram explaining advanced flow across client, server and Adyen domain"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Mobile integrations
&lt;/h2&gt;

&lt;p&gt;This guide was intended for a browser flow of 3DS2 with Adyen Web. If you wish to integrate with 3DS2 for mobile we highly recommend using one of our dedicated mobile SDKs available for &lt;a href="https://docs.adyen.com/online-payments/build-your-integration/?platform=Android&amp;amp;integration=Drop-in&amp;amp;version=5.5.0" rel="noopener noreferrer"&gt;Android&lt;/a&gt; or &lt;a href="https://docs.adyen.com/online-payments/build-your-integration/?platform=iOS&amp;amp;integration=Drop-in&amp;amp;version=5.5.0" rel="noopener noreferrer"&gt;iOS&lt;/a&gt;. If you still prefer to use a web integration then we strongly recommend using the redirect flow over native flow. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Thanks for reading! In this blog we’ve covered: &lt;br&gt;
✅ What is 3D Secure 2&lt;br&gt;
✅ How Adyen supports 3DS2 transactions&lt;br&gt;
✅ A comparison between the different integration options&lt;br&gt;
✅ How to integrate using the Sessions flow&lt;br&gt;
✅ How to integrate using the Advanced flow&lt;br&gt;
✅ How to handle 3DS2 on the client-side using Adyen Drop-in &lt;/p&gt;

&lt;p&gt;We hope you find this technical blog helpful. Don’t forget to check out the &lt;a href="https://github.com/adyen-examples/adyen-node-online-payments/tree/main/3ds2-example" rel="noopener noreferrer"&gt;github repository&lt;/a&gt; which contains a fully working integration-example. It’s important to note that, while this guide promotes an ideal flow with our recommended best practices, all payment flows are uniquely complicated, so if this guide doesn’t exactly suit your needs, you can refer to our &lt;a href="https://docs.adyen.com/online-payments/" rel="noopener noreferrer"&gt;extensive documentation guides&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>payments</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Web fundamentals and the importance of context</title>
      <dc:creator>Sarah 🦄</dc:creator>
      <pubDate>Wed, 13 Jul 2022 09:03:13 +0000</pubDate>
      <link>https://dev.to/sarahcodes_dev/web-fundamentals-and-the-importance-of-context-3lnn</link>
      <guid>https://dev.to/sarahcodes_dev/web-fundamentals-and-the-importance-of-context-3lnn</guid>
      <description>&lt;p&gt;There was a very interesting &lt;a href="https://youtu.be/hWjT_OOBdOc"&gt;talk&lt;/a&gt; at the WWC22 conference by Laurie Voss that has sparked some discourse in the web developer community. The talk was great and I highly recommend you watch it. &lt;/p&gt;

&lt;p&gt;Laurie takes us on a journey through the many, many cycles of the web and how we got to where we are and he predicts the future. While I agree with a lot of Laurie's points, I disagree with one fundamental (pun intended) thing, he states: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There’s no such thing as fundamentals. If you take one thing away from this talk it should be that there’s no such thing as fundamentals. That’s gatekeeping nonsense.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a sweeping statement and no doubt a pointedly provocative one but it's also in my opinion incorrect and potentially damaging. Now I know by even writing this I am falling right into the stereotype that the talk pokes fun at.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--awGJBHJ---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0w2rsw1w9w2il3u0nh6q.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--awGJBHJ---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0w2rsw1w9w2il3u0nh6q.jpeg" alt="The Simpsons meme of newspaper with Abe Simpson shaking his fist at a cloud and headline reads old man yells at cloud" width="477" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However I believe this topic is more nuanced and I want to explore this statement a bit. First of all gatekeeping &lt;strong&gt;is&lt;/strong&gt; an issue in web and software engineering in general but &lt;strong&gt;advocating for fundamentals is not gatekeeping&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;But here's the thing, what are the fundamentals? And in this, I agree with the sentiment raised in the talk. The web is ever changing, things get abstracted away and we no longer have to think about them and that's great because there is already too much to have to think about. &lt;/p&gt;

&lt;p&gt;At each stage of abstraction though you do have fundamentals. You have the core knowledge required for you to do your job well and effectively. And of course which fundamentals apply depend on what kind of work you are doing, where in the stack you are and the scale of the project you are working on. Context matters.&lt;/p&gt;

&lt;p&gt;This is why it's so important for senior engineers and thought leaders to advocate for the fundamentals of their space, &lt;strong&gt;not to gate keep but to empower.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Let's take the React Framework as an example. You can build a web app with React without ever learning underlying JavaScript concepts of course but I don't encourage this. Your life will be a lot easier if you spend some time learning JavaScript and it's core concepts before moving onto abstractions like React or Vue or &lt;em&gt;insert framework name here&lt;/em&gt;. Because understanding JavaScript will help you understand React and so improve your code and make debugging a whole lot easier. &lt;/p&gt;

&lt;p&gt;But let's step back from this argument, even without learning vanilla JavaScript and going straight to React. React also has fundamentals. There are core concepts and patterns to React that will make your life a lot easier if you take the time to understand them. For instance, how state works, how to handle events etc. Again context matters. &lt;/p&gt;

&lt;p&gt;Fundamentals will evolve as the web evolves. And context will always define them. That doesn't mean they are not important, in fact I'd argue it means they are even more important because they are the foundation that we build upon. Maybe in the future we will all be writing Rust web components and I won't advocate for JavaScript anymore but I will advocate for the core concepts of Rust (which disclaimer right now I have no idea about so no questions at this time 😅). &lt;/p&gt;

&lt;p&gt;The point is &lt;strong&gt;the more you understand what you are working with, the better developer you will be and so the better the product you are building will be&lt;/strong&gt;. Context matters. &lt;/p&gt;

&lt;p&gt;One last point I want to touch on is the idea of low code or no code builders being the future of web. I think this is an interesting concept and I agree to an extent. I think these kind of solutions will gain traction and eventually become a part of what we do -- once they figure out accessibility issues (I hope). However, I only see these solutions working well for things like static sites and low complexity apps. And I'm sure when they do, there will some fundamentals to know to ensure you are doing it well. &lt;/p&gt;

&lt;p&gt;Saying that, there will always be complex apps such as banking applications, trading applications, flight applications etc. These will always require custom solutions and they will always have problems that can't be solved with a drag and drop component. &lt;strong&gt;And so there will always be code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So if you take one thing away from this post I hope it's that: &lt;em&gt;&lt;strong&gt;fundamentals matter but in context and they should empower not gate keep.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading! ❤️&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>career</category>
      <category>writing</category>
    </item>
    <item>
      <title>Handling requests with fetch</title>
      <dc:creator>Sarah 🦄</dc:creator>
      <pubDate>Tue, 16 Mar 2021 21:02:53 +0000</pubDate>
      <link>https://dev.to/sarahcodes_dev/that-s-so-fetch-4fo3</link>
      <guid>https://dev.to/sarahcodes_dev/that-s-so-fetch-4fo3</guid>
      <description>&lt;p&gt;This week I got to rewrite our apps requests from using &lt;a href="https://github.com/axios/axios"&gt;axios&lt;/a&gt; to using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch"&gt;Fetch API&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Let's get into it!&lt;/p&gt;

&lt;h3&gt;
  
  
  Wasn't that supposed to fail?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;try&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;response&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;fetch&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="nx"&gt;url&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="nx"&gt;requestOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;handleResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&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;error&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;Looking at the code above, you might expect that if the request responds with an error such as 404 or 500 it would be caught and rejected. Nice and tidy, right?&lt;/p&gt;

&lt;p&gt;Nope. The catch will only get invoked if the request does not complete, for instance in a network failure. If the request returns an error &lt;strong&gt;it will resolve normally but ok will be set to false.&lt;/strong&gt; (ok is a property on the HTTP response.)&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I handle my errors then?
&lt;/h3&gt;

&lt;p&gt;So your api might typically implement the following pattern; try to make a request, do something if it's successful, if it fails; catch the error and log it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;    &lt;span class="k"&gt;try&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="c1"&gt;// do something after request succeeds&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;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="c1"&gt;// log error&lt;/span&gt;
                &lt;span class="c1"&gt;// notify user something went wrong&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So with this in mind, we can look at our fetch code in the first code block and see that if the request returns an error, it will not trigger the catch because it's not rejected, it still resolves as normal. We don't want this, we want the catch to be triggered so our error gets logged and our user gets notified that something went wrong. &lt;/p&gt;

&lt;h3&gt;
  
  
  Handle it
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;204&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;300&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;json&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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;error&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;To handle this I wrote a handleResponse function. This function takes the response returned from the fetch and checks the status. (Here I wanted to specifically check the status's to handle different cases but you could also check the ok property.)&lt;/p&gt;

&lt;p&gt;In the code above you can see that a status of 204 will resolve with an empty object, that's because 204 is a No-Content response and so there is nothing to unwrap. &lt;/p&gt;

&lt;p&gt;For any response between 200 and 300 we unwrap the json and resolve the promise with the data. &lt;/p&gt;

&lt;p&gt;Otherwise we resolve the json and reject the promise with the error. This reject will invoke the catch in our saga thus logging the error and notifying the user. &lt;/p&gt;

&lt;h3&gt;
  
  
  Wrap it up
&lt;/h3&gt;

&lt;p&gt;I decided to write a wrapper function which would encapsulate most of this fetch logic. This way fellow developers can easily make requests without worrying about unwrapping, and resolving or rejecting responses for each request. &lt;/p&gt;

&lt;p&gt;Another benefit is that the Authorization headers get set in one place and are always attached to each request. &lt;/p&gt;

&lt;p&gt;Below is the fetchRequestWrapper in Typescript. We still give a lot of control to the user but ensure that responses and errors are handled in a consistent way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;204&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;300&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;json&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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;error&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IFetchRequestOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTTPMethods&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;headers&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="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchRequest&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;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;IFetchRequestOptions&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="nx"&gt;authHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Get your auth token`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;requestOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTTPMethods&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&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="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;headers&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;try&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;response&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;fetch&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="nx"&gt;url&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="nx"&gt;requestOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;handleResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&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;error&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;h2&gt;
  
  
  Additional GOTCHAs:
&lt;/h2&gt;

&lt;p&gt;These are some little things I ran into that caught me for a bit. &lt;/p&gt;

&lt;h3&gt;
  
  
  Posting JSON:
&lt;/h3&gt;

&lt;p&gt;When using POST with fetch to send JSON to the server there are two main things to remember. &lt;/p&gt;

&lt;p&gt;First, the Content-Type header needs to be set as application/json.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;headers&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;Content-Type&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;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second, the data you pass in the body needs to be wrapped in JSON.stringify&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Uploading data:
&lt;/h3&gt;

&lt;p&gt;Some of our requests require users to upload a file. This had me stumped for a few hours because the request kept failing even though I was setting the Content-Type header to multi-part/form-data which &lt;em&gt;I thought was required&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;I luckily stumbled across this &lt;a href="https://muffinman.io/blog/uploading-files-using-fetch-multipart-form-data/"&gt;post&lt;/a&gt; which helped resolve the issue. The main learning was that when uploading data &lt;strong&gt;don't set the Content-Type header&lt;/strong&gt;, if you don't the browser will do it automatically and add the web-boundary required for uploads. &lt;/p&gt;

&lt;p&gt;Additional tip: if you are using TypeScript make sure to the body is of type FormData. &lt;/p&gt;

&lt;p&gt;In the wrapper I decided it would be cleaner to add a separate function to handle uploads in order to separate the different functionality and not clutter up the main fetch request. Here is the fetch upload function in Typescript. You can see the interface for request options is much stricter here and the method is always POST.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IFetchRequestUploadOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchRequestUpload&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;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IFetchRequestUploadOptions&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="nx"&gt;authHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Get your auth token`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;requestOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTTPMethods&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&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="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authHeader&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;try&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;response&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`$url}`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;getResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&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;error&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;And that's it, that was my journey with fetch. Thanks for reading! If you liked it please like and share! I hope this helps you with your coding journey!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3oKIPkMhoaMlhtarF6/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3oKIPkMhoaMlhtarF6/giphy.gif" alt="meangirls-reference-that's-so-fetch" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Every developer is self taught</title>
      <dc:creator>Sarah 🦄</dc:creator>
      <pubDate>Tue, 05 Jan 2021 18:42:42 +0000</pubDate>
      <link>https://dev.to/sarahcodes_dev/every-developer-is-self-taught-1oal</link>
      <guid>https://dev.to/sarahcodes_dev/every-developer-is-self-taught-1oal</guid>
      <description>&lt;p&gt;I know this might be a "hot take" (or whatever the kids call it) but hear me out. I see a lot in the tech community about college vs. self-taught. This got me thinking. Reflecting on my own path it's not as straight forward as one or the other.&lt;/p&gt;

&lt;p&gt;I have a degree in Business Information Systems. My degree gave me a broad range of skills and exposure to different areas of IT. In my final year I was able to focus on programming and took all the programming related modules. When I graduated I took a graduate Java Software Engineer role. That was in 2012.&lt;/p&gt;

&lt;p&gt;Today I'm a JavaScript engineer. I build interactive web apps, and love CSS and web animations. How did I get to this point? I taught myself*.&lt;/p&gt;

&lt;p&gt;I knew very little about the web even after my four year degree. I took one web development module in first year and after four years it was pretty outdated. At this role I became fascinated with the web. A colleague who shared my interest inspired me to try it out for myself. Once I got started I realised this was it for me. This was the path I wanted to follow.  By day, I wrote Java code but I spent my spare time learning everything I could about web development. I created small projects, read books and watched videos.&lt;/p&gt;

&lt;p&gt;After about 7 months I decided to take a leap. I applied for a Web engineer role in another company. The role asked for someone proficient in JavaScript. I remember feeling nervous before the interview. I didn't &lt;em&gt;know&lt;/em&gt; JavaScript did I? I was never taught it so I couldn't &lt;em&gt;know&lt;/em&gt; it. I went for it anyway. To my delight (and surprise) I passed the assessment and got the job.&lt;/p&gt;

&lt;p&gt;Once I was in the door I faced a new challenge. A UI framework called ExtJS, which at the time to me was a complete unknown. Up to this point I had only been playing with vanilla Javascript and a little jQuery. I felt out of my depth but also very excited. I read the documentation page by page. I made practice projects, I experimented with the app and tried to use concepts I had learned. A couple months into the role a new Senior Web developer joined the team. He was a talented engineer and very proficient in JavaScript. I reached out and he became my mentor. We paired a lot and he helped me understand different concepts and all the JavaScript quirks. From there my confidence started to grow, as well as my skills.&lt;/p&gt;

&lt;p&gt;Every year since then I've had to learn something new, D3.js, Angular.js, Angular 2, React and so on. And not only different libraries but also different concepts. MVC, MVVM, Components. Tech is always changing. The web is always changing. We have to keep learning to keep up with it. At times it can be overwhelming. It's also (&lt;em&gt;at least for me&lt;/em&gt;) what keeps it interesting and one of the things I love about the web.&lt;/p&gt;

&lt;p&gt;So at this point being self taught or having a degree doesn't matter. Yes having a degree helps you get in the door. It's not right but that's the way it is at most places. But things are changing. Companies are realising it's not about the paper it's about the practice. It's about the commitment you put into it.&lt;/p&gt;

&lt;p&gt;Choosing college or not is a very personal decision. College offers a lot more than just the paper at the end. It's an experience and it's not for everyone. The main thing is to keep learning and keep building, get yourself a mentor, and put yourself out there. Because in the end we are all self taught developers.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I taught myself but could not have done it without the help of many others. Mentors and other colleagues and especially the amazing content available online.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>motivation</category>
      <category>beginners</category>
      <category>career</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Can you age out of dev?</title>
      <dc:creator>Sarah 🦄</dc:creator>
      <pubDate>Tue, 08 Dec 2020 23:17:26 +0000</pubDate>
      <link>https://dev.to/sarahcodes_dev/can-you-age-out-of-dev-2ncf</link>
      <guid>https://dev.to/sarahcodes_dev/can-you-age-out-of-dev-2ncf</guid>
      <description>&lt;p&gt;I've been thinking lately about how much I absolutely love to code, like just taking a task and building something cool. And I've also been thinking about my future with development, I'm a Senior Engineer now and I've been coding for 8 years, also taking responsibilities to design, team lead &amp;amp; architect at times. I worry that as I progress the only path is to move more and more into management responsibilities/roles or move into product. &lt;/p&gt;

&lt;p&gt;As a web engineer I feel too specialised to move into a full architect role which seems to be the only technical option for career progression. But I also have the worry that I'll get "too old" and not be hireable because all the new kids will know React v239 better than me 🙄&lt;/p&gt;

&lt;p&gt;Am I the only who worries about this? Is there career progression where you get to just stay coding/building cool shit most of the time or &lt;em&gt;can you age out of dev&lt;/em&gt;? &lt;/p&gt;

</description>
      <category>watercooler</category>
      <category>healthydebate</category>
      <category>discuss</category>
    </item>
    <item>
      <title>What exactly is Kubernetes anyway?</title>
      <dc:creator>Sarah 🦄</dc:creator>
      <pubDate>Wed, 09 Sep 2020 09:22:17 +0000</pubDate>
      <link>https://dev.to/sarahcodes_dev/what-exactly-is-kubernetes-anyway-4k9h</link>
      <guid>https://dev.to/sarahcodes_dev/what-exactly-is-kubernetes-anyway-4k9h</guid>
      <description>&lt;p&gt;&lt;em&gt;I spent some time learning about kubernetes this week as I want to understand the devops pipeline at my current role better. This post is essentially my organised notes so I can refer to it again when needed. Hopefully it may help others lost in the jargon understand better too&lt;/em&gt; 🤷&lt;/p&gt;

&lt;p&gt;Kubernetes is a platform for managing containerised workloads and services. All major cloud platforms have support for kubernetes including Google, AWS and Azure. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Okay but what does it &lt;em&gt;mean&lt;/em&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's go back in time for a minute, back in the old days applications would typically be deployed to a physical server, a human would be responsible for managing the application, allocating resources, monitoring the status, keeping the application up and running effectively. As applications grow and demand for the service grows this becomes increasingly difficult to manage and maintain. &lt;/p&gt;

&lt;p&gt;Flash forward a bit and we got virtualisation. This was great, virtualisation allowed us to run multiple Virtual Machines (VM) on one physical server. Each VM is encapsulated with its own operating system and other components. &lt;/p&gt;

&lt;p&gt;Things progressed even more and we got containerisation. Containers are similar to VMs but more lightweight, they are not as isolated and they share the same OS. You might have heard of &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt;, a very popular container solution. &lt;/p&gt;

&lt;p&gt;Here's a handy image of the evolution of deployment for reference:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zJvo_orD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/by5ov9dm4pw98wiwjnl2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zJvo_orD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/by5ov9dm4pw98wiwjnl2.png" alt="Evolution of Deployment" width="370" height="136"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay cool, so now we have containers, we can throw our application into a container with the exact environment and configuration we want. Awesome! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What now?&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Well now we have to manage those containers. This is where kubernetes comes in. Kubernetes helps us to orchestrate our containers. Orchestration is just a fancy word for a collection of tools and scripts that help host the containers. It enables us to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easily deploy multiple instances&lt;/li&gt;
&lt;li&gt;Scale up and down depending on demand&lt;/li&gt;
&lt;li&gt;Network easily between hosts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cool, cool, cool, so how does this work?&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Well, typically you will have a kubernetes cluster, a cluster is a set of nodes. A node is a physical or virtual machine in which kubernetes is installed. &lt;/p&gt;

&lt;p&gt;A node is a worker machine where containers will be launched and managed by kubernetes. How do we manage it? We have a master node, this node watches over the nodes in the cluster and is responsible for the orchestration of the nodes. The master will have different scripts and tools running which will be configured however you want to manage the cluster. &lt;/p&gt;

&lt;p&gt;The configuration can manage things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;load balancing&lt;/li&gt;
&lt;li&gt;service discovery&lt;/li&gt;
&lt;li&gt;sharing configuration across instances&lt;/li&gt;
&lt;li&gt;storage management &lt;/li&gt;
&lt;li&gt;self healing &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An important distinction is that Kubernetes &lt;strong&gt;does not build or deploy your application&lt;/strong&gt; but &lt;strong&gt;provides tools to help you manage your environment&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Kubernetes is made up of the following components:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API server&lt;/strong&gt;: This is the frontend of the system, the CLI talks to the API server to interact with the cluster. Here, you can make REST calls to manage shared state and configuration. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;etcd server&lt;/strong&gt;: This is a key/value store used to store all data used to manage the cluster. This stores the data about all the nodes and masters in a distributed way. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;scheduler&lt;/strong&gt;: This is responsible for distributing work across multiple nodes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;controller&lt;/strong&gt;: This is the brain of the orchestration, it monitors for any issues or failures across the cluster and is responsible for responding accordingly if a node or container goes down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;container runtime&lt;/strong&gt;: Software used to run the containers. e.g. Docker. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;kubelet&lt;/strong&gt;: This is the agent that runs on each node within the cluster, they ensure the containers on the nodes are running as expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;kubectl&lt;/strong&gt;: Or Kube control tool, this is the CLI used to deploy and manage applications in the cluster. You can use it to get cluster information, the status of the nodes etc.&lt;/p&gt;

&lt;p&gt;And that's my high level, novice take on what kubernetes actually is. Thanks for reading!!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>beginners</category>
      <category>kubernetes</category>
      <category>learning</category>
    </item>
    <item>
      <title>Nevertheless, Sarah Coded</title>
      <dc:creator>Sarah 🦄</dc:creator>
      <pubDate>Sun, 08 Mar 2020 10:29:39 +0000</pubDate>
      <link>https://dev.to/sarahcodes_dev/nevertheless-sarah-coded-1mk</link>
      <guid>https://dev.to/sarahcodes_dev/nevertheless-sarah-coded-1mk</guid>
      <description>&lt;p&gt;I've been coding professionally for almost 8 years now. Wow. That's insane to me. 12 years ago when I started college, whoa...was it really 12 years ago already?😨, anyways 12 years ago I had no idea what code was, I was starting my 4 year degree in Business Information Systems because I liked business and I had absolutely no idea what I wanted to do. Unknowingly, I had put myself on a path that would be challenging, rewarding and a hell of a lot of fun! &lt;/p&gt;

&lt;p&gt;It all started with my first Information System module, Visual Basic 6.0. (As I mentioned this was 12 years ago 🙈). I remember it so clearly, sitting in the lecture hall just being completely fascinated by this new world that opened up to me. Then I was introduced to Web development and that was it. I was in! Four years, many late nights, fights with Java and OOP (we made up eventually!), and a LOT of coffee later, I started my career as a Software Engineer. &lt;/p&gt;

&lt;p&gt;I almost never made that start though. I almost never applied for that course and I almost never sat in the first Visual Basic 6.0 lecture. In my final year of school I had the typical mandatory session with the Career Guidance teacher. I told him I wanted to do BIS. He told me "Oh I don't know about that, that's really hard, my son is doing that now and I don't know if you would be able for it. What else have you considered?" My gut sank. I didn't know how to respond, isn't he supposed to be encouraging? What about me implied I wouldn't be able for it? I had top grades, I was in honors classes. I was all in all a good student. Looking back now there can only be one reason he would have made those comments, I am a woman. I went home that day dejected and told my mom what happened. She comforted me and then she gave me the best advice I ever received, "you get to decide, you can do anything you set your mind to." &lt;/p&gt;

&lt;p&gt;That drove me my entire career, it pushed me to work hard, learn more, constantly improve and get better. I set my mind to something and I get it. Hard work and perseverance. I flew through the course and graduated with honors, today I am the Lead UI Engineer and UI Architect for a 60 people organization. And I'm just getting started.🙌🏻     &lt;/p&gt;

&lt;h2&gt;
  
  
  Equality in tech looks like…
&lt;/h2&gt;

&lt;p&gt;No girl or woman ever having her dreams or goals questions because of her gender. &lt;br&gt;
All women being considered equally for opportunities. &lt;br&gt;
No more gender wage gap!&lt;/p&gt;

&lt;h2&gt;
  
  
  I’m an expert at…
&lt;/h2&gt;

&lt;p&gt;JavaScript, Web development, UX, drinking obscene amounts of coffee. &lt;/p&gt;

&lt;h2&gt;
  
  
  My advice for allies for other self-identifying women and non-binary folks who code...
&lt;/h2&gt;

&lt;p&gt;PERSIST. Just keep going. It's a hard path. It's exhausting at times. But you can do it. No matter what anyone else says, keep going and you'll get there.&lt;/p&gt;

</description>
      <category>wecoded</category>
    </item>
    <item>
      <title>You don't need a side project, just practice.</title>
      <dc:creator>Sarah 🦄</dc:creator>
      <pubDate>Wed, 27 Nov 2019 22:46:58 +0000</pubDate>
      <link>https://dev.to/sarahcodes_dev/you-don-t-need-a-side-project-just-practice-4k44</link>
      <guid>https://dev.to/sarahcodes_dev/you-don-t-need-a-side-project-just-practice-4k44</guid>
      <description>&lt;p&gt;There are a lot of opinions out there regarding coding outside of work and whether it's required for success or not. Personally, I think it's very much up to the individual. I code outside work because I love to code. Still though, after a long day of work sometimes the last thing I want to do is dive into ANOTHER project. &lt;/p&gt;

&lt;p&gt;Side projects can be great. They are a great way to learn, to build your skills and to build your portfolio. They can also be overwhelming and time consuming. Sometimes you just have 30 minutes and you want to do something fun and quick. &lt;/p&gt;

&lt;p&gt;Some people choose an open source project to contribute to rather than coming up with their own project. I think open source is amazing but it's also a huge commitment. To learn someone else's code base and get up to speed enough to contribute, that takes time. Which is awesome, if you want to spend time investing in it but I just have never found the time or motivation myself. After coding all day in my day job, I just want to do something fun, something quick but challenging that I can complete in one sitting or dip in and out of without pressure. &lt;/p&gt;

&lt;p&gt;An alternative I've found is code katas. Code kata's are set problems you can solve using a language of your choice. There are a plethora of katas out there, all varying in difficulty and area of focus. Some focus on algorithms, some on fundamentals and logic. There's something for every mood. &lt;a href="https://www.codewars.com/dashboard"&gt;Code Wars&lt;/a&gt; is a great resource for katas. Another resource I use is &lt;a href="https://www.reddit.com/r/dailyprogrammer/"&gt;reddit daily challenge&lt;/a&gt;. They provide challenges of different levels easy, medium or hard. You can also see the solutions in the comments and improve by reading other peoples code. Another way I like to practice is to just pick a cool feature on a website and try to implement it myself. Usually setting a time limit to challenge myself. &lt;/p&gt;

&lt;p&gt;All of these little efforts will compound together to help hone and build your skills. You don't necessarily have to have an epic side project or be an open source savant. &lt;/p&gt;

&lt;p&gt;The most important thing is to keep learning and keep coding. ✌️&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>career</category>
      <category>opinion</category>
      <category>motivation</category>
    </item>
    <item>
      <title>So you want to be a better developer? </title>
      <dc:creator>Sarah 🦄</dc:creator>
      <pubDate>Sat, 22 Jun 2019 19:28:04 +0000</pubDate>
      <link>https://dev.to/sarahcodes_dev/so-you-want-to-be-a-better-developer-409l</link>
      <guid>https://dev.to/sarahcodes_dev/so-you-want-to-be-a-better-developer-409l</guid>
      <description>&lt;p&gt;Recently at work my colleague and I have been tasked with creating a learning path for junior developers who want to learn the front end. This is something we are still working on but it got me thinking about my journey and how I got from a junior developer, who knew relatively little, to a senior developer who knows a little bit more.&lt;/p&gt;

&lt;p&gt;After college I went straight into work. Working within a team of experienced developers a couple of things quickly became apparent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What I learned in college barely scratched the surface&lt;/li&gt;
&lt;li&gt;If I wanted to be a good developer it would be a life-long learning journey&lt;/li&gt;
&lt;li&gt;There was a lot I didn’t know and even more &lt;em&gt;I didn’t know I didn’t know&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point is the most important lesson I learned very early on in my career. Firstly, just say you don’t know if you don’t know something. It’s so much better than just pretending and it means you will actually learn more. Secondly, “You don’t know what you don’t know”, it sounds funny but once you realise it and embrace it, it can be quite powerful. For me, it drove my curiosity. It made me ask questions, delve deep into topics and try to figure out not only the things I knew I needed to learn but also identify the things I had not yet discovered I needed to learn.&lt;/p&gt;

&lt;p&gt;So what did I do? Well at times I felt extremely overwhelmed, “how the heck was I going to ever be as good as the developers around me?”. There was just too much to learn. Spoiler alert for all you new developers, there will always be too much to learn. It’s the nature of our industry. It’s what makes it fun and exciting, and also terrifying and exhausting. The key is to do your best not to let it get to you and just focus on getting better at whatever skill you’ve selected. For me it was JavaScript. While my strength at the time was Java because I had done a lot of it in college, my passion was for the web and front end. I knew if I wanted to go down that road the first priority was JavaScript, So I’d identified the first area of focus, what now?&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn the Rules
&lt;/h2&gt;

&lt;p&gt;Remember you can learn all the frameworks you want but if you don’t have a good grasp on the fundamentals of the underlying language you won’t be properly equipped to deal with issues as they arise. Do you ever hear people complain about a language, like “Ugh I hate JavaScript, it doesn’t make sense.” Well it doesn’t make sense because you don’t understand it yet. If you take the time to delve into the problem and figure it out, it will most definitely make sense. Trying to work with something when you don’t fully understand the basics can be extremely frustrating. Which is why my most important recommendation if you take nothing else away from this is to learn the basics. This goes for any language. Spend time really getting to grips with the language and all its nuances.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t be afraid to ask questions
&lt;/h2&gt;

&lt;p&gt;This one is key. I know how intimating it can be, feeling like you are the only person in a room who doesn’t “get” something. That insecurity might prevent you from ever asking the necessary questions to help you understand. But you have to push through it, ask all the questions. If you are working as part of a team, ask your teammates. If you are self-learning, ask the internet. Just ask the question. Remember there is no such thing as a stupid question. There is no need to struggle alone when you could simply reach out and ask for help.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice. Practice. Practice.
&lt;/h2&gt;

&lt;p&gt;You can read as many books as you want, and watch all the YouTube tutorials there are, but the best way to learn is through doing. Practice your craft. And most importantly be consistent about it. That’s the best way to progress quickly. Practice. Learn. Practice more. Learn more. You don’t need to have some grand project to work on either, do a daily kata or find something cool on a website and try to implement it. Some good resources for inspiration are the katas on codewars and the challenges on reddit daily programmer. The point is to just practice. I used to challenge myself to solving a kata a day, usually in the morning. The more you do, the faster you build your skill and it’s also a lot of fun. You can also learn a lot from looking at other peoples solutions and how you could have improved yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find someone who knows more than you
&lt;/h2&gt;

&lt;p&gt;If you can identify a mentor, they can be invaluable to building your skill. Someone who you can ask questions, discuss concepts with and who can help you on your journey. This could be a fellow colleague or a friend in a similar field, basically anyone who you can look at and say I want to know what they know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be Persistent
&lt;/h2&gt;

&lt;p&gt;This is not an easy path, it’s a long road of learning and then when the new thing comes out… learning some more. It can feel overwhelming but as you get more experienced it does get easier. Just keep at it. Persistence is the most important quality in any developer. Persist through the problems, persist through that imposter syndrome, persist through the mistakes. It’s all just one big learning experience. Persist and you will triumph.&lt;/p&gt;

&lt;p&gt;If you’ve made it this far thanks for reading my ramblings, I hope it helps in some way and best of luck on your coding journey :)&lt;/p&gt;

&lt;p&gt;This post was migrated from my medium account: &lt;a href="https://levelup.gitconnected.com/so-you-want-to-be-a-better-developer-1f9894a1e991"&gt;https://levelup.gitconnected.com/so-you-want-to-be-a-better-developer-1f9894a1e991&lt;/a&gt;&lt;/p&gt;

</description>
      <category>softwareenginneering</category>
      <category>personaldevelopment</category>
      <category>webdev</category>
      <category>codingjourney</category>
    </item>
    <item>
      <title>var vs let &amp; const</title>
      <dc:creator>Sarah 🦄</dc:creator>
      <pubDate>Sat, 22 Jun 2019 19:21:38 +0000</pubDate>
      <link>https://dev.to/sarahcodes_dev/var-vs-let-const-12p</link>
      <guid>https://dev.to/sarahcodes_dev/var-vs-let-const-12p</guid>
      <description>&lt;p&gt;ES6 has been around for a while now, and brought with it a lot of cool changes for JavaScript. One of those changes is how we &lt;br&gt;
declare variables. We now have three options: var, let and const. This post is going to attempt to explain them in a simple and hopefully helpful way. Let’s start.&lt;/p&gt;

&lt;h2&gt;
  
  
  var
&lt;/h2&gt;

&lt;p&gt;Before ES6 we used the var keyword. A variable declared with var can be initialized immediately but doesn’t need to be. Lets take a look at an example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;var superhero = 'Batman'; // we initialized the variable immediately&lt;br&gt;
var villain;&lt;br&gt;
if(superhero === 'Batman'){&lt;br&gt;
    villain = 'The Joker'; // we initialized the variable later&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;With var the variable is declared on either the global scope or within function scope. For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;var name = 'Bob';&lt;br&gt;
function getName(){&lt;br&gt;
 var name = 'Bill';&lt;br&gt;
  return name;&lt;br&gt;
}&lt;br&gt;
console.log(getName()); //logs Bill&lt;br&gt;
console.log(name); //logs Bob&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In the example above ‘Bob’ is declared on the global scope but even though we are using the same variable name, ‘Bill’ is declared on the function scope&lt;br&gt;
and so logging name will result in ‘Bob’ while logging getName() will result in ‘Bill’.&lt;/p&gt;

&lt;h2&gt;
  
  
  let
&lt;/h2&gt;

&lt;p&gt;ES6 gave us the let keyword. let works similarly to var, variables can be either immediately initialized or not. With let we get block level declaration scope. Let’s take a look at an example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;function varScoped(){&lt;br&gt;
  var num = 1; &lt;br&gt;
  if(num === 1){&lt;br&gt;
   var num = 2;&lt;br&gt;
    console.log('I am within function scope',num); //logs 2&lt;br&gt;
  }&lt;br&gt;
  console.log('I am within function scope too',num); //logs 2   &lt;br&gt;
}&lt;br&gt;
function letScoped(){ &lt;br&gt;
  let num = 1;&lt;br&gt;
  if(num === 1){&lt;br&gt;
   let num = 2;&lt;br&gt;
    console.log('I am within block scope',num); //logs 2&lt;br&gt;
  }&lt;br&gt;
  console.log('I am within function scope',num); //logs 1&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In the first function above we are declaring our variable with var, so as discussed earlier variables will be at function scope. Even though it might seem like we are re-declaring num in the if block, we are overriding our previous declaration and so num logs as 2 both inside and outside the if block.&lt;/p&gt;

&lt;p&gt;In the second function we are declaring with let, because let gives us block level scope our num variable within the if block is on a different scope to the num variable outside it, they don’t interfere with each other and so num logs as 2 inside the if block and retains it’s value of 1 outside the if block.&lt;/p&gt;

&lt;h2&gt;
  
  
  const
&lt;/h2&gt;

&lt;p&gt;Last but not least we have const. Unlike var or let a const needs a value assigned to it on declaration.&lt;/p&gt;

&lt;p&gt;So we cannot do this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const num;&lt;br&gt;
num = 5;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We need to do this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const num = 5;&lt;/code&gt;&lt;br&gt;
Declaring a variable with const means this value will not change and cannot be reassigned within that block scope. Let’s look at an example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;function constScopedA(){&lt;br&gt;
const num = 5;&lt;br&gt;
if(num === 5){&lt;br&gt;
    num += 1; // this will throw an error: Uncaught TypeError: Assignment to constant variable.&lt;br&gt;
   }&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In the above example an error is thrown when we try to reassign the variable num. The variable identifier cannot be reassigned.&lt;/p&gt;

&lt;p&gt;However as const, like let, is also block scoped, we can do this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;function constScopedB(){&lt;br&gt;
const num = 5;&lt;br&gt;
if(num === 5){&lt;br&gt;
   const num = 6; &lt;br&gt;
    console.log(num); //log 6&lt;br&gt;
  }&lt;br&gt;
  console.log(num); //log 5&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The num variable within the if block is on a different scope to the num variable within the function and so we get no error here. We have two different num constants on two different scopes.&lt;/p&gt;

&lt;p&gt;An important note about const is that you can change a const value but not the reference. So for instance if you declare an object as a const you can change the objects contents. So for example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;function constObject(){&lt;br&gt;
  const person = {name: 'Bob'}; &lt;br&gt;
  person.name = 'Bill';&lt;br&gt;
  console.log(person.name); //logs Bill&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In the above example we can update the name property of person even though person is a constant, const variables are not immutable. However we cannot create a new reference to person.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;function constObject(){&lt;br&gt;
  const person = {name: 'Bob'};&lt;br&gt;
  const person = {name: 'Bill'};&lt;br&gt;
  console.log(person.name); //throws error Identifier 'person' has already been declared&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The function above will throw a syntax error because we already declared a constant called person.&lt;/p&gt;

&lt;p&gt;So that’s it, a basic summary of variable declaration with JavaScript and ES6. I hope you found it helpful :)&lt;/p&gt;

&lt;p&gt;This post was migrated from my medium account: &lt;a href="https://medium.com/@sarbot/declaring-variables-with-javascript-es6-ab71c0a60768"&gt;https://medium.com/@sarbot/declaring-variables-with-javascript-es6-ab71c0a60768&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginnerjavascript</category>
    </item>
    <item>
      <title>Sorting in JavaScript</title>
      <dc:creator>Sarah 🦄</dc:creator>
      <pubDate>Sat, 22 Jun 2019 19:21:24 +0000</pubDate>
      <link>https://dev.to/sarahcodes_dev/sorting-in-javascript-3hl5</link>
      <guid>https://dev.to/sarahcodes_dev/sorting-in-javascript-3hl5</guid>
      <description>&lt;p&gt;JavaScript arrays have a bunch of nifty features, one of which is the sort method.&lt;/p&gt;

&lt;p&gt;The sort method will sort the items in an array and return the array.&lt;/p&gt;

&lt;p&gt;By default the sort order is determined by converting each item to strings and comparing their unicode value.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const names = [‘Buffy’, ‘Xander’, ‘Angel’, ‘Willow’, ‘Giles’, ‘Anya’];&lt;br&gt;
names.sort();&lt;br&gt;
// result: [“Angel”, “Anya”, “Buffy”, “Giles”, “Willow”, “Xander”]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Cool that worked, seems pretty easy when our value is a string. What about numerical values though?&lt;/p&gt;

&lt;p&gt;Let’s take an example, say we have an array of random numbers; let’s call them user ages for context. We have a requirement to sort the user ages from youngest to oldest.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const ages = [16, 24, 61, 31, 17, 39, 27, 8, 12, 82, 48, 42, 26, 46, 76, 84, 89, 46, 62, 28];&lt;br&gt;
ages.sort(); // no custom function provided&lt;br&gt;
// result: [12, 16, 17, 24, 26, 27, 28, 31, 39, 42, 46, 46, 48, 61, 62, 76, 8, 82, 84, 89]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Hmmm this looks okay but wait I’m pretty sure 8 doesn’t come after 76 numerically!&lt;/p&gt;

&lt;p&gt;Without the custom function, each item is converted to a string and sorted based on their Unicode characters which does not work here as we are comparing numerical values not strings.&lt;/p&gt;

&lt;p&gt;Don’t worry though we can just provide a custom compare function to determine our sort order.&lt;/p&gt;

&lt;p&gt;The compare function has two arguments, typically these arguments are referred to as a and b but you can name them whatever you’d like.&lt;/p&gt;

&lt;p&gt;Let’s create our sort function:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const customSort = ages.sort((a, b) =&amp;gt; {&lt;br&gt;
if(a &amp;gt; b){&lt;br&gt;
 return 1;&lt;br&gt;
}else{&lt;br&gt;
 return -1;&lt;br&gt;
}&lt;br&gt;
});&lt;br&gt;
// result: [8, 12, 16, 17, 24, 26, 27, 28, 31, 39, 42, 46, 46, 48, 61, 62, 76, 82, 84, 89]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That looks better, but what are we actually doing here? The sort function will run over our array taking two arguments at a time, compare them and then move onto the next two until the sort is complete.&lt;/p&gt;

&lt;p&gt;In our function we are saying, if a is greater than b then return 1, this will sort a to an index higher than b, otherwise return -1, this will sort a to an index lower than b.&lt;/p&gt;

&lt;p&gt;Cool! What about sorting arrays of objects? Don’t worry we can sort objects no problem, we first need to identify what we want to sort by, let’s take this array of objects:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const objects = [{name: ‘Buffy’, year: 1981}, {name: ‘Angel’, year: 1727}, {name: ‘Anya’, year: 860}, {name: ‘Spike’, year: 1850}];&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Say we want to sort by year of birth, and we want to sort from youngest to oldest. Here we just need to specify the property we are comparing in our custom function.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const customSort = objects.sort((a, b) =&amp;gt; {&lt;br&gt;
if(a.year &amp;gt; b.year){&lt;br&gt;
return -1;&lt;br&gt;
}else{&lt;br&gt;
return 1;&lt;br&gt;
}&lt;br&gt;
});&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Because we want youngest to oldest this time we just reverse the logic and return -1 if the a.year is greater than b.year and 1 if the a.year is less than b.year.&lt;/p&gt;

&lt;p&gt;And that’s it, that’s the JavaScript sort function! Thanks for reading. Hope this explanation helped you on your journey with JavaScript. If it did maybe give it a clap or leave a comment :)&lt;/p&gt;

&lt;p&gt;This post was migrated from my medium account: &lt;a href="https://medium.com/@sarbot/sorting-in-javascript-a9c04f865267"&gt;https://medium.com/@sarbot/sorting-in-javascript-a9c04f865267&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>vanillajavascript</category>
      <category>webdev</category>
      <category>sorting</category>
    </item>
  </channel>
</rss>
